diff --git a/app/controllers/admin/photos_controller.rb b/app/controllers/admin/photos_controller.rb
new file mode 100644
index 00000000..fc598d09
--- /dev/null
+++ b/app/controllers/admin/photos_controller.rb
@@ -0,0 +1,65 @@
+module Admin
+ class PhotosController < ApplicationController
+ before_action :set_conference
+ before_action :set_photo, only: [:edit, :update, :destroy]
+
+# GET /admin/photos
+ def index
+ @photos = @conference.photos.all
+ end
+
+# GET /admin/photos/new
+ def new
+ @photo = @conference.photos.build
+ end
+
+# GET /admin/photos/1/edit
+ def edit
+ end
+
+# POST /admin/photos
+ def create
+ @photo = @conference.photos.build(photo_params)
+ if @photo.save
+ redirect_to admin_conference_photos_path, notice: 'Photo was successfully created.'
+ else
+ flash[:alert] = "A error prohibited this Photo from being saved: #{@photo.errors.full_messages.join('. ')}."
+ render :new
+ end
+ end
+
+# PATCH/PUT /admin/photos/1
+ def update
+ if @photo.update(photo_params)
+ redirect_to admin_conference_photos_path, notice: 'Photo was successfully updated.'
+ else
+ flash[:alert] = "A error prohibited this Photo from being saved: #{@photo.errors.full_messages.join('. ')}."
+ render :edit
+ end
+ end
+
+# DELETE /admin/photos/1
+ def destroy
+ @photo.destroy
+ redirect_to admin_conference_photos_path, notice: 'Photo was successfully destroyed.'
+ end
+
+ private
+
+# Use callbacks to share common setup or constraints between actions.
+ def set_conference
+ @conference = Conference.find_by(short_title: params[:conference_id])
+ end
+
+# Use callbacks to share common setup or constraints between actions.
+ def set_photo
+ @photo = Photo.find_by(id: params[:id])
+ end
+
+# Only allow a trusted parameter "white list" through.
+ def photo_params
+ params[:photo]
+ end
+ end
+end
+
diff --git a/app/views/admin/conference/_edit_form.html.haml b/app/views/admin/conference/_edit_form.html.haml
index 11e7a513..ed26829b 100644
--- a/app/views/admin/conference/_edit_form.html.haml
+++ b/app/views/admin/conference/_edit_form.html.haml
@@ -27,6 +27,4 @@
= f.input :sponsor_description, hint: markdown_hint("This will appear in the sponsor segment of the splash."), input_html: { rows: 5, data: { provide: "markdown-editable" } }
= f.input :sponsor_email, hint: 'This will appear in the sponsor segment of the splash for the sponsors to contact to the organizers'
= f.input :lodging_description, hint: markdown_hint("This will appear in the lodging segment of the splash."), input_html: { rows: 5, data: { provide: "markdown-editable" } }
-
- = dynamic_association :photos, "Photos", f
= f.action :submit, :as => :button, :button_html => {:class => "btn btn-primary"}
diff --git a/app/views/admin/conference/_photo_fields.html.erb b/app/views/admin/conference/_photo_fields.html.erb
deleted file mode 100644
index 29a1f16b..00000000
--- a/app/views/admin/conference/_photo_fields.html.erb
+++ /dev/null
@@ -1,10 +0,0 @@
-
- <%= f.inputs do %>
- <%= image_tag(f.object.picture(:thumb)) unless f.object.picture.blank? %>
- <%= f.input :picture, hint: 'This photo will appear in the gallery of the splash
- so please give photos of banner sizes' %>
- <%= f.input :description, :input_html => {:rows => 2 }, hint: 'This description will appear
- at the bottom of each photo' %>
- <%= remove_association_link :photo, f %>
- <% end %>
-
diff --git a/app/views/admin/photos/_form.html.haml b/app/views/admin/photos/_form.html.haml
new file mode 100644
index 00000000..2c01af4c
--- /dev/null
+++ b/app/views/admin/photos/_form.html.haml
@@ -0,0 +1,7 @@
+= f.inputs do
+ - if !f.object.errors
+ = image_tag(f.object.picture(:thumb)) unless f.object.picture.blank?
+ = f.input :picture, hint: 'This photo will appear in the gallery of the splash so please give photos of banner sizes.'
+ = f.input :description, :input_html => {:rows => 2 }, hint: 'This description will appear at the bottom of each photo.'
+ .actions
+ = f.button 'Save Photo', :class => 'btn btn-primary'
diff --git a/app/views/admin/photos/edit.html.haml b/app/views/admin/photos/edit.html.haml
new file mode 100644
index 00000000..9b87054b
--- /dev/null
+++ b/app/views/admin/photos/edit.html.haml
@@ -0,0 +1,6 @@
+%h1 Editing Photo
+
+= semantic_form_for @photo, url: admin_conference_photo_path(conference_id: @conference.short_title, id: @photo.id) do |f|
+ = render 'form', f: f
+
+= link_to 'Back', admin_conference_photos_path
diff --git a/app/views/admin/photos/index.html.haml b/app/views/admin/photos/index.html.haml
new file mode 100644
index 00000000..77f4f5d7
--- /dev/null
+++ b/app/views/admin/photos/index.html.haml
@@ -0,0 +1,22 @@
+%h1 Photos
+%p.text-muted
+ Photo's will appear in the gallery of the conference splash page.
+- @photos.each_slice(3) do |slice|
+ .row.row-flex.row-flex-wrap
+ - slice.each do |photo|
+ .col-md-4
+ .thumbnail.flex-col
+ %h3.text-center
+ = photo.picture_file_name
+ = image_tag(photo.picture(:large), class: 'img-responsive', title: photo.description)
+ .caption.flex-grow
+ .caption
+ = link_to edit_admin_conference_photo_path(@conference.short_title, photo.id), class: 'btn btn-primary' do
+ %i.fa.fa-pencil-square-o
+ Edit
+ = link_to admin_conference_photo_path(@conference.short_title, photo.id), method: :delete, class: 'btn btn-danger' do
+ %i.fa.fa-times-circle
+ Delete
+.row{'style'=>'padding-top: 10px;'}
+ .col-md-12
+ = link_to 'New Photo', new_admin_conference_photo_path, class: 'btn btn-primary'
diff --git a/app/views/admin/photos/new.html.haml b/app/views/admin/photos/new.html.haml
new file mode 100644
index 00000000..68115b49
--- /dev/null
+++ b/app/views/admin/photos/new.html.haml
@@ -0,0 +1,6 @@
+%h1 New Photo
+
+= semantic_form_for @photo, url: admin_conference_photos_path(conference_id: @conference.short_title) do |f|
+ = render 'form', f: f
+
+= link_to 'Back', admin_conference_photos_path
diff --git a/app/views/layouts/_admin_sidebar.html.haml b/app/views/layouts/_admin_sidebar.html.haml
index b9f5e5db..31f8397b 100644
--- a/app/views/layouts/_admin_sidebar.html.haml
+++ b/app/views/layouts/_admin_sidebar.html.haml
@@ -35,8 +35,12 @@
Contact
%li{:class=> "#{active_nav_li(admin_conference_commercials_path(@conference.short_title))}"}
= link_to(admin_conference_commercials_path(@conference.short_title)) do
- %span.fa.fa-comment
+ %span.fa.fa-film
Commercials
+ %li{:class=> "#{active_nav_li(admin_conference_photos_path(@conference.short_title))}"}
+ = link_to(admin_conference_photos_path(@conference.short_title)) do
+ %span.fa.fa-picture-o
+ Photos
%li{:class=> active_nav_li(admin_conference_events_path(@conference.short_title))}
= link_to(admin_conference_events_path(@conference.short_title)) do
%span.glyphicon.glyphicon-comment
diff --git a/config/routes.rb b/config/routes.rb
index c8bc7a05..030a4bb0 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -9,6 +9,7 @@ Osem::Application.routes.draw do
resources :people
resources :conference do
resource :contact, except: [:index, :new, :create, :show, :destroy]
+ resources :photos, except: [:show]
resource :schedule, only: [:show, :update]
resources :commercials, except: [:show]
get '/stats' => 'stats#index'
diff --git a/spec/factories/photos.rb b/spec/factories/photos.rb
index 49180a0b..c3e69721 100644
--- a/spec/factories/photos.rb
+++ b/spec/factories/photos.rb
@@ -1,7 +1,9 @@
# Read about factories at https://github.com/thoughtbot/factory_girl
+include ActionDispatch::TestProcess
FactoryGirl.define do
factory :photo do
+ picture { fixture_file_upload(Rails.root.join('app', 'assets', 'images', 'rails.png'), 'image/png') }
picture_file_name 'rails.png'
picture_content_type 'image/png'
picture_file_size '1024'
diff --git a/spec/features/photo_spec.rb b/spec/features/photo_spec.rb
new file mode 100644
index 00000000..c9150b30
--- /dev/null
+++ b/spec/features/photo_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+feature Photo do
+
+ # It is necessary to use bang version of let to build roles before user
+ let!(:organizer_role) { create(:organizer_role) }
+ let!(:participant_role) { create(:participant_role) }
+ let!(:admin_role) { create(:admin_role) }
+
+ shared_examples 'add and update photo' do |user|
+ scenario 'adds a new photo', feature: true, js: true do
+ expected_count = Photo.count + 1
+ conference = create(:conference)
+ sign_in create(user)
+
+ visit new_admin_conference_photo_path(conference.short_title)
+
+ file_path = Rails.root.join('app', 'assets', 'images', 'rails.png')
+ attach_file('photo_picture', file_path)
+ fill_in 'photo_description', with: 'Lorem ipsum dolorem...'
+
+ click_button 'Save Photo'
+
+ expect(flash).
+ to eq('Photo was successfully created.')
+ expect(Photo.count).to eq(expected_count)
+ end
+
+ scenario 'updates a photo', feature: true, js: true do
+ expected_count = Photo.count + 1
+ conference = create(:conference)
+ photo = create(:photo)
+ sign_in create(user)
+
+ visit edit_admin_conference_photo_path(conference.short_title, photo.id)
+
+ file_path = Rails.root.join('app', 'assets', 'images', 'person_large.png')
+ attach_file('photo_picture', file_path)
+ fill_in 'photo_description', with: 'Lorem ipsum dolorem...'
+
+ click_button 'Save Photo'
+
+ expect(flash).
+ to eq('Photo was successfully updated.')
+ expect(Photo.count).to eq(expected_count)
+ end
+
+ scenario 'adds a text file', feature: true, js: true do
+ expected_count = Photo.count
+ conference = create(:conference)
+ sign_in create(user)
+
+ visit new_admin_conference_photo_path(conference.short_title)
+
+ file_path = Rails.root + 'spec/fixtures/test.txt'
+ attach_file('photo_picture', file_path)
+ fill_in 'photo_description', with: 'Lorem ipsum dolorem...'
+
+ click_button 'Save Photo'
+
+ expect(flash).
+ to eq("A error prohibited this Photo from being saved: Picture content type is invalid. Picture is invalid.")
+ expect(Photo.count).to eq(expected_count)
+ end
+ end
+
+ describe 'admin' do
+ it_behaves_like 'add and update photo', :admin
+ end
+
+ describe 'organizer' do
+ it_behaves_like 'add and update photo', :organizer
+ end
+
+end
diff --git a/spec/fixtures/test.txt b/spec/fixtures/test.txt
new file mode 100644
index 00000000..7dc5f421
--- /dev/null
+++ b/spec/fixtures/test.txt
@@ -0,0 +1 @@
+Lorem ipsum dolorem...
\ No newline at end of file