diff --git a/app/controllers/admin/events_controller.rb b/app/controllers/admin/events_controller.rb index 1f8eb566..c5f728af 100644 --- a/app/controllers/admin/events_controller.rb +++ b/app/controllers/admin/events_controller.rb @@ -4,8 +4,9 @@ module Admin load_and_authorize_resource :program, through: :conference, singleton: true load_and_authorize_resource :event, through: :program load_and_authorize_resource :events_registration, only: :toggle_attendance + load_and_authorize_resource :user, only: :new - before_action :get_event, except: [:index, :create, :reports] + before_action :get_event, except: [:index, :create, :new, :reports] # FIXME: The timezome should only be applied on output, otherwise # you get lost in timezone conversions... @@ -92,7 +93,34 @@ module Admin end end - def create; end + def create + @url = admin_conference_program_events_path(@conference.short_title, @event) + @users = User.all + @languages = @program.languages_list + @event.validate_owners = true + # make event confirmed initially since there is not much sens to go over the approval procedure for admin-added proposals + @event.state = :confirmed + if @event.valid? + @event.event_users.new(user_id: @event.submitter_id, + event_role: 'submitter') + @event.event_users.new(user_id: @event.speaker_id, + event_role: 'speaker') + end + + if @event.save + ahoy.track 'Event submission', title: 'New submission' + redirect_to admin_conference_program_events_path(@conference.short_title), notice: 'Event was successfully submitted.' + else + flash[:error] = "Could not create event: #{@event.errors.full_messages.join(', ')}" + render action: 'new' + end + end + + def new + @url = admin_conference_program_events_path(@conference.short_title, @event) + @languages = @program.languages_list + @users = User.all.order(:name) + end def accept send_mail = @event.program.conference.email_settings.send_on_accepted @@ -168,7 +196,7 @@ module Admin # Set also in proposals controller :title, :subtitle, :event_type_id, :abstract, :description, :require_registration, :difficulty_level_id, # Set only in admin/events controller - :track_id, :state, :language, :is_highlight, :max_attendees, + :track_id, :state, :language, :is_highlight, :max_attendees, :speaker_id, :submitter_id, # Not used anymore? :proposal_additional_speakers, :user, :users_attributes) end diff --git a/app/models/event.rb b/app/models/event.rb index d2446847..218bdd87 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,4 +1,8 @@ class Event < ActiveRecord::Base + attr_accessor :submitter_id + attr_accessor :speaker_id + attr_accessor :validate_owners + include ActiveRecord::Transitions has_paper_trail on: [:create, :update], ignore: [:updated_at, :guid, :week], meta: { conference_id: :conference_id } @@ -37,6 +41,8 @@ class Event < ActiveRecord::Base validate :max_attendees_no_more_than_room_size + validate :submitter_and_speaker_present + scope :confirmed, -> { where(state: 'confirmed') } scope :canceled, -> { where(state: 'canceled') } scope :withdrawn, -> { where(state: 'withdrawn') } @@ -294,4 +300,18 @@ class Event < ActiveRecord::Base def conference_id program.conference_id end + + def submitter_and_speaker_present + if validate_owners + errors.add(:speaker_id, "can't be blank!") unless self.speaker_id.present? + if self.speaker_id.present? + errors.add(:speaker_id, "user should exist!") unless User.where(id: self.speaker_id).take + end + + errors.add(:submitter_id, "can't be blank!") unless self.submitter_id.present? + if self.submitter_id.present? + errors.add(:submitter_id, "user should exist!") unless User.where(id: self.submitter_id).take + end + end + end end diff --git a/app/views/admin/events/_form.html.haml b/app/views/admin/events/_form.html.haml new file mode 100644 index 00000000..8ac7735e --- /dev/null +++ b/app/views/admin/events/_form.html.haml @@ -0,0 +1,77 @@ +.row + .col-md-12 + .page-header + %h1 + New Event +.row + .col-md-12 + = semantic_form_for(@event, url: @url) do |f| + = f.inputs name: 'Event Information' do + = f.input :title, as: :string, required: true + + = f.input :subtitle, as: :string + + = f.input :submitter_id, as: :select, + collection: @users.map {|user| ["#{user.name}", user.id,]}, + include_blank: '(Please select)', label: 'Submitter', input_html: { class: 'select-help-toggle' } + + = f.input :speaker_id, as: :select, + collection: @users.map {|user| ["#{user.name}", user.id,]}, + include_blank: '(Please select)', label: 'Speaker', input_html: { class: 'select-help-toggle' } + + - if @program.tracks.any? + = f.input :track_id, as: :select, + collection: @program.tracks.map {|track| ["#{track.name}", track.id] }, + include_blank: true + + = f.input :event_type_id, as: :select, + collection: @conference.program.event_types.map {|type| ["#{type.title} - #{show_time(type.length)}", type.id, + data: { min_words: type.minimum_abstract_length, max_words: type.maximum_abstract_length }]}, + include_blank: false, label: 'Type', input_html: { class: 'select-help-toggle' } + + - if @program.languages.present? + = f.input :language, as: :select, + collection: @languages, + include_blank: false, label: 'Language', input_html: { class: 'select-help-toggle' } + + - @conference.program.event_types.each do |event_type| + %span{ class: 'help-block select-help-text event_event_type_id collapse', id: "#{event_type.id}-help" } + = event_type.description + + = f.input :difficulty_level, as: :select, collection: @conference.program.difficulty_levels, input_html: { class: 'select-help-toggle' }, + include_blank: '(Please select)' if @conference.program.difficulty_levels.any? + + - @conference.program.difficulty_levels.each do |difficulty_level| + %span{ class: 'help-block select-help-text collapse event_difficulty_level_id', id: "#{difficulty_level.id}-help" } + = difficulty_level.description + + = f.input :abstract, required: true, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, + hint: markdown_hint(link_to('Tips to improve your presentations.', 'http://blog.hubspot.com/blog/tabid/6307/bid/5975/10-Rules-to-Instantly-Improve-Your-Presentations.aspx')) + + %p + You have used + %span#abstract-count #{@event.abstract_word_count} + words. Abstracts must be between + %span#abstract-minimum-word-count + 0 + and + %span#abstract-maximum-word-count + 250 + words. + + = f.inputs 'Enable pre-registration' do + = f.input :require_registration, label: 'Require participants to register to your event' + - message = @event.room ? "Value must be between 1 and #{@event.room.size}" : 'Check room capacity after scheduling.' + = f.input :max_attendees, hint: 'The maximum number of participants. ' + message + + = f.input :is_highlight + + %p.text-right + = link_to '#description', 'data-toggle' => 'collapse' do + Do you require something special? + .collapse#description + = f.input :description, input_html: { rows: 5 }, label: 'Requirements', placeholder: 'Eg. Whiteboard, printer, or something like that.' + + + %p.text-right + = f.submit 'Create Event', class: 'btn btn-success' diff --git a/app/views/admin/events/index.html.haml b/app/views/admin/events/index.html.haml index ff01cfd4..fe104cf5 100644 --- a/app/views/admin/events/index.html.haml +++ b/app/views/admin/events/index.html.haml @@ -4,7 +4,10 @@ %h1 Events = "(#{@events.length})" if @events.any? - .btn-group.pull-right + + .pull-right + - if can? :create, Event + =link_to 'Add Event', new_admin_conference_program_event_path(@conference.short_title), :class => 'button btn btn-default btn-info' - if can? :read, Event .btn-group %button.btn.btn-default.dropdown-toggle{"data-toggle" => "dropdown", :type => "button", :class => 'btn btn-success'} diff --git a/spec/controllers/admin/events_controller_spec.rb b/spec/controllers/admin/events_controller_spec.rb new file mode 100644 index 00000000..fb6cea6b --- /dev/null +++ b/spec/controllers/admin/events_controller_spec.rb @@ -0,0 +1,88 @@ +require 'spec_helper' + +describe Admin::EventsController do + let!(:admin) { create(:admin) } + let(:conference) { create(:conference) } + let(:event_type) { create :event_type } + let(:event) { create(:event, program: conference.program) } + + context 'admin is signed in' do + before { sign_in admin } + + describe 'GET #index' do + before { get :index, conference_id: conference.short_title } + + it 'renders index template' do + expect(response).to render_template('index') + end + end + + describe 'GET #edit' do + before { get :edit, conference_id: conference.short_title, id: event.id } + + it 'renders edit template' do + expect(response).to render_template('edit') + end + + it 'assigns event variable' do + expect(assigns(:event)).to be_instance_of(Event) + end + end + + describe 'GET #new' do + before { get :new, conference_id: conference.short_title } + + it 'renders new template' do + expect(response).to render_template('new') + end + + it 'assigns event variable' do + expect(assigns(:event)).to be_instance_of(Event) + end + end + + describe 'POST #create' do + context 'saves successfuly' do + before do + post :create, event: attributes_for(:event, event_type_id: event_type.id).merge!(submitter_id: admin.id, speaker_id: admin.id), conference_id: conference.short_title + end + + it 'redirects to admin events index path' do + expect(response).to redirect_to admin_conference_program_events_path(conference_id: conference.short_title) + end + + it 'shows success message in flash notice' do + expect(flash[:notice]).to match('Event was successfully submitted.') + end + + it 'creates new event' do + expect(Event.find(event.id)).to be_instance_of(Event) + end + end + + context 'save fails' do + before do + allow_any_instance_of(Event).to receive(:save).and_return(false) + post :create, event: attributes_for(:event), conference_id: conference.short_title + end + + it 'renders new template' do + expect(response).to render_template('new') + end + + it 'shows error in flash message' do + expect(flash[:error]).to match("Could not create event: #{event.errors.full_messages.join(', ')}") + end + + it 'does not create new event' do + allow_any_instance_of(Event).to receive(:save).and_return(false) + expect do + post :create, event: attributes_for(:event, event_type_id: event_type.id), + conference_id: conference.short_title, + user: attributes_for(:user) + end.not_to change{ Event.count } + end + end + end + end +end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index f9c46559..ee1e6ada 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -96,6 +96,43 @@ describe Event do end end end + + describe 'submitter_and_speaker_present' do + context 'is invalid with owner validation enabled' do + before :each do + @new_event = build(:event, program: conference.program) + @new_event.validate_owners = true + end + it 'when submitter id is blank' do + expect(@new_event).to be_invalid + expect(@new_event.errors[:submitter_id]).to eq ["can't be blank!"] + end + it 'when speaker id is blank' do + expect(@new_event).to be_invalid + expect(@new_event.errors[:speaker_id]).to eq ["can't be blank!"] + end + it 'when submitter is is provided but the user does not exist' do + @new_event.submitter_id = 'deadbeef' + expect(@new_event).to be_invalid + expect(@new_event.errors[:submitter_id]).to eq ["user should exist!"] + end + it 'when speaker id is provided but the user does not exist' do + @new_event.speaker_id = 'deadbeef' + expect(@new_event).to be_invalid + expect(@new_event.errors[:speaker_id]).to eq ["user should exist!"] + end + end + context 'is valid with owner validation enabled' do + it 'when submitter and speaker ids are not blank and users with these ids exist' do + new_event = build(:event, program: conference.program) + new_user = create(:user) + new_event.submitter_id = new_user.id + new_event.speaker_id = new_user.id + new_event.validate_owners = true + expect(new_event).to be_valid + end + end + end end describe 'scope ' do diff --git a/spec/views/admin/events/index.html.haml_spec.rb b/spec/views/admin/events/index.html.haml_spec.rb index 5a252862..5b2330e7 100644 --- a/spec/views/admin/events/index.html.haml_spec.rb +++ b/spec/views/admin/events/index.html.haml_spec.rb @@ -38,4 +38,19 @@ describe 'admin/events/index' do expect(rendered).to have_selector('table tr:nth-of-type(2) td:nth-of-type(2)', text: 'event2') expect(rendered).to have_selector('table tr:nth-of-type(2) td:nth-of-type(5)', text: 'German') end + + it 'has the Add Event link with a new event path' do + assign(:conference, conference) + assign(:program, conference.program) + program.languages = 'en,de' + assign(:events, [ event1, event2 ]) + assign(:event_types, [ create(:event_type, program: conference.program), create(:event_type, program: conference.program) ]) + assign(:tracks, [ create(:track, program: conference.program), create(:track, program: conference.program) ]) + assign(:difficulty_levels, [ create(:difficulty_level, program: conference.program), create(:difficulty_level, program: conference.program) ]) + + render + + expect(rendered).to have_link('Add Event', href: new_admin_conference_program_event_path(conference.short_title)) + end + end diff --git a/spec/views/admin/events/new.html.haml_spec.rb b/spec/views/admin/events/new.html.haml_spec.rb new file mode 100644 index 00000000..a0a4751f --- /dev/null +++ b/spec/views/admin/events/new.html.haml_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe 'admin/conferences/events/new' do + it 'renders the new event template' do + @conference = create(:conference) + @user = create(:user) + @program = @conference.program + @event = build(:event, program: @conference.program) + @event_type = create(:event_type, program: @program) + @url = admin_conference_program_events_path(@conference.short_title, @event) + @users = User.all + render template: 'admin/events/_form.html.haml' + expect(rendered).to include('New Event') + end +end