diff --git a/.travis.yml b/.travis.yml index ff4f8805..ce7fe77d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ cache: bundler sudo: false rvm: - 2.6.4 +dist: trusty notifications: email: diff --git a/src/app/assets/stylesheets/formtastic.css b/src/app/assets/stylesheets/formtastic.css index 54485ee1..aa6a28c6 100644 --- a/src/app/assets/stylesheets/formtastic.css +++ b/src/app/assets/stylesheets/formtastic.css @@ -50,7 +50,7 @@ html[xmlns] form.formtastic fieldset ol li { display: block; } form.formtastic fieldset ol li.required { } form.formtastic fieldset ol li.optional { } form.formtastic fieldset ol li.error { } - + /* LABELS --------------------------------------------------------------------------------------------------*/ @@ -100,7 +100,7 @@ form.formtastic fieldset ol li.hidden { display:none; } /* BOOLEAN OVERRIDES --------------------------------------------------------------------------------------------------*/ -form.formtastic fieldset ol li.boolean label { padding-left:25%; width:auto; } +form.formtastic fieldset ol li.boolean label { padding-left:0%; width:auto; } form.formtastic fieldset ol li.boolean label input { margin:0 0.5em 0 0.2em; } @@ -133,6 +133,6 @@ form.formtastic fieldset ol li.date fieldset ol li label, form.formtastic fieldset ol li.time fieldset ol li label, form.formtastic fieldset ol li.datetime fieldset ol li label { display:none; } -form.formtastic fieldset ol li.date fieldset ol li label input, -form.formtastic fieldset ol li.time fieldset ol li label input, +form.formtastic fieldset ol li.date fieldset ol li label input, +form.formtastic fieldset ol li.time fieldset ol li label input, form.formtastic fieldset ol li.datetime fieldset ol li label input { display:inline; margin:0; padding:0; } diff --git a/src/app/controllers/participants_controller.rb b/src/app/controllers/participants_controller.rb index 068f2e43..26f5002e 100644 --- a/src/app/controllers/participants_controller.rb +++ b/src/app/controllers/participants_controller.rb @@ -24,28 +24,40 @@ def edit end def update - @participant.update(participant_params) + @participant.update(participant_params.except(:code_of_conduct_agreement)) + create_code_of_conduct_agreement_if_not_exists! respond_with(@participant) end def create - @participant.attributes = participant_params + @participant.attributes = participant_params.except(:code_of_conduct_agreement) if @participant.save + create_code_of_conduct_agreement_if_not_exists! flash[:notice] = "Thanks for registering an account. You may now create sessions and mark sessions you'd like to attend." redirect_to root_path else - flash[:error] = "There was a problem creating that account." + flash[:error] = "There was a problem creating that account." render :new end + end + def create_code_of_conduct_agreement_if_not_exists! + if participant_params[:code_of_conduct_agreement] == '1' && @participant.signed_code_of_conduct_for_current_event? == false + CodeOfConductAgreement.create!({ + participant_id: @participant.id, + event_id: Event.current_event.id, + }) + end end private def participant_params - params.require(controller_name.singularize).permit(:name, :email, :password, - :bio, :github_profile_username, - :twitter_handle) + params.require(controller_name.singularize).permit( + :name, :email, :password, + :bio, :github_profile_username, + :twitter_handle, :code_of_conduct_agreement + ) end def verify_owner diff --git a/src/app/controllers/presentations_controller.rb b/src/app/controllers/presentations_controller.rb index 44c50bfc..91dc32f0 100644 --- a/src/app/controllers/presentations_controller.rb +++ b/src/app/controllers/presentations_controller.rb @@ -17,6 +17,10 @@ def create flash[:error] = "Sorry, no presenter named '#{params[:name]}' was found. Please try again." redirect_to session_presentations_path(@session) return + elsif participant.signed_code_of_conduct_for_current_event? == false + flash[:error] = "Sorry, '#{params[:name]}' hasn't signed the Code of Conduct for this event." + redirect_to session_presentations_path(@session) + return end presentation = @session.presentations.new diff --git a/src/app/controllers/sessions_controller.rb b/src/app/controllers/sessions_controller.rb index d29a381d..cc60570d 100644 --- a/src/app/controllers/sessions_controller.rb +++ b/src/app/controllers/sessions_controller.rb @@ -57,11 +57,12 @@ def index end def create - @session.attributes = session_params + @session.attributes = session_params.except(:code_of_conduct_agreement) @session.participant = current_participant @session.event = Event.current_event if @session.save + create_code_of_conduct_agreement_if_not_exists! flash[:notice] = "Thanks for adding your session." redirect_to @session else @@ -69,6 +70,15 @@ def create end end + def create_code_of_conduct_agreement_if_not_exists! + if session_params[:code_of_conduct_agreement] == '1' && @session.participant.signed_code_of_conduct_for_current_event? == false + CodeOfConductAgreement.create!({ + participant_id: @session.participant.id, + event_id: Event.current_event.id, + }) + end + end + STOP_WORDS = Set.new(['session', 'etc', 'just', 'presentation', 'get', 'discussion']) def words @@ -97,7 +107,17 @@ def popularity private def session_params - params.require(controller_name.singularize).permit(:title, :description, :level_id, :name, :email, :category_ids => []) + params + .require(controller_name.singularize) + .permit( + :title, + :description, + :level_id, + :name, + :email, + :code_of_conduct_agreement, + :category_ids => [] + ) end def verify_owner diff --git a/src/app/models/code_of_conduct_agreement.rb b/src/app/models/code_of_conduct_agreement.rb new file mode 100644 index 00000000..c8c8baf8 --- /dev/null +++ b/src/app/models/code_of_conduct_agreement.rb @@ -0,0 +1,4 @@ +class CodeOfConductAgreement < ActiveRecord::Base + belongs_to :participant + belongs_to :event +end diff --git a/src/app/models/participant.rb b/src/app/models/participant.rb index 36cf4c8a..1d81c1bb 100644 --- a/src/app/models/participant.rb +++ b/src/app/models/participant.rb @@ -9,6 +9,9 @@ class Participant < ActiveRecord::Base validates_presence_of :name validates_uniqueness_of :email, :case_sensitive => false, :allow_blank => true + # used for formtastic form to allow sending a field related to a separate model + attr_accessor :code_of_conduct_agreement + acts_as_authentic do |config| config.crypto_provider = Authlogic::CryptoProviders::BCrypt config.require_password_confirmation = false @@ -39,6 +42,15 @@ def deliver_password_reset_instructions! Notifier.password_reset_instructions(self).deliver_now! end + def signed_code_of_conduct_for_current_event? + return false unless Event.current_event + + CodeOfConductAgreement.where({ + participant_id: id, + event_id: Event.current_event.id, + }).exists? + end + def attending_session?(session) sessions_attending.include?(session) end diff --git a/src/app/models/session.rb b/src/app/models/session.rb index 69c3a134..0868556c 100644 --- a/src/app/models/session.rb +++ b/src/app/models/session.rb @@ -23,7 +23,7 @@ class Session < ActiveRecord::Base scope :for_past_events, -> { where.not(event_id: Event.current_event.id).includes(:event).order('events.date desc') } scope :recent, -> { order('created_at desc') } - + scope :random_order, -> { order('random()') } validates_presence_of :description @@ -34,7 +34,7 @@ class Session < ActiveRecord::Base #validates_uniqueness_of :timeslot_id, :scope => :room_id, :allow_blank => true, :message => 'and room combination already in use' - attr_accessor :name, :email + attr_accessor :name, :email, :code_of_conduct_agreement after_create :create_presenter @@ -132,7 +132,7 @@ def self.preload_attendance_counts(sessions) sessions.each do |session| sessions_by_id[session.id] = session end - + # Surely there’s a Rails helper for this? # But I can’t find it — only some abandoned gems. Attendance @@ -181,7 +181,7 @@ def estimated_interest # make estimated_interest tend toward the mean in cases when there are few real votes. ballast_votes = 10.0 - session_votes / (possible_votes + ballast_votes * session_count) * total_votes + session_votes / (possible_votes + ballast_votes * session_count) * total_votes end end diff --git a/src/app/views/participants/edit.html.erb b/src/app/views/participants/edit.html.erb index 9cb121b2..591862f4 100644 --- a/src/app/views/participants/edit.html.erb +++ b/src/app/views/participants/edit.html.erb @@ -9,6 +9,15 @@ <%= f.input :github_profile_username, :label => 'Your GitHub username' %> <%= f.input :twitter_handle, :label => 'Your Twitter handle' %> <%= f.input :bio, :hint => 'You can use Markdown syntax here. Examples: **bold**, *italic*, [link](http://example.com)'.html_safe %> + <%= + f.input :code_of_conduct_agreement, + as: :boolean, + label: 'I agree to the Code of Conduct governing this event', + input_html: { + checked: @participant.signed_code_of_conduct_for_current_event?, + disabled: @participant.signed_code_of_conduct_for_current_event? + } + %> <% end %> <%= f.actions do %> diff --git a/src/app/views/participants/new.html.erb b/src/app/views/participants/new.html.erb index 7a161825..decbf7db 100644 --- a/src/app/views/participants/new.html.erb +++ b/src/app/views/participants/new.html.erb @@ -8,6 +8,15 @@ <%= f.input :name, :label => 'Your name', :hint => "Please use your real name. This will be used in our printed materials. Use JUST your name. For joint sessions, you will be able to add co-presenters." %> <%= f.input :email, :label => 'Your email', :hint => "Please use a real email address. We need this to contact you about your presentation." %> <%= f.input :password, :label => 'Password' %> + <%= + f.input :code_of_conduct_agreement, + as: :boolean, + label: 'I agree to the Code of Conduct governing this event', + input_html: { + checked: @participant.signed_code_of_conduct_for_current_event?, + disabled: @participant.signed_code_of_conduct_for_current_event? + } + %> <% end %> <%= f.actions do %> diff --git a/src/app/views/presentations/index.html.erb b/src/app/views/presentations/index.html.erb index e7231ebc..c0042291 100644 --- a/src/app/views/presentations/index.html.erb +++ b/src/app/views/presentations/index.html.erb @@ -10,7 +10,7 @@

Add presenters

-

Note: In order to add a presenter to your session, they must be registered in the system.

+

Note: In order to add a presenter to your session, they must be registered in the system and agreed to the Code of Conduct.

<%= semantic_form_for([@session, @presentation]) do |f| %> <%= text_field_tag 'name', '', :placeholder => 'Type a name', :id => 'typeahead' %> <%= submit_tag 'Add', :id => 'add-presenter' %> diff --git a/src/app/views/sessions/_form.html.erb b/src/app/views/sessions/_form.html.erb index d94c57e9..fc49c8a0 100644 --- a/src/app/views/sessions/_form.html.erb +++ b/src/app/views/sessions/_form.html.erb @@ -11,6 +11,13 @@ <%= f.input :categories, :as => :check_boxes %> <%= f.input :level, :as => :select, :label => 'Topic level' %> + <%= + f.input :code_of_conduct_agreement, + as: :boolean, + required: true, + label: 'I agree to the Code of Conduct governing this event', + input_html: { checked: current_participant.signed_code_of_conduct_for_current_event? } + %> <% end %> <%= f.actions do %> diff --git a/src/db/migrate/20190717012137_create_code_of_conduct_agreements.rb b/src/db/migrate/20190717012137_create_code_of_conduct_agreements.rb new file mode 100644 index 00000000..77c35b5c --- /dev/null +++ b/src/db/migrate/20190717012137_create_code_of_conduct_agreements.rb @@ -0,0 +1,9 @@ +class CreateCodeOfConductAgreements < ActiveRecord::Migration[5.2] + def change + create_table :code_of_conduct_agreements do |t| + t.references :participant, null: false + t.references :event, null: false + t.timestamps + end + end +end diff --git a/src/db/schema.rb b/src/db/schema.rb index dee11be4..a22a70c5 100644 --- a/src/db/schema.rb +++ b/src/db/schema.rb @@ -10,8 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_07_13_173911) do - +ActiveRecord::Schema.define(version: 2019_07_17_012137) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -36,6 +35,15 @@ t.index ["category_id", "session_id"], name: "index_categorizations_on_category_id_and_session_id", unique: true end + create_table "code_of_conduct_agreements", force: :cascade do |t| + t.bigint "participant_id", null: false + t.bigint "event_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["event_id"], name: "index_code_of_conduct_agreements_on_event_id" + t.index ["participant_id"], name: "index_code_of_conduct_agreements_on_participant_id" + end + create_table "events", id: :serial, force: :cascade do |t| t.string "name", limit: 255, null: false t.date "date", null: false diff --git a/src/spec/controllers/presentations_controller_spec.rb b/src/spec/controllers/presentations_controller_spec.rb index b8c05008..49fe2792 100644 --- a/src/spec/controllers/presentations_controller_spec.rb +++ b/src/spec/controllers/presentations_controller_spec.rb @@ -14,21 +14,35 @@ describe "#create" do context "when the user exists" do - before { create(:joe) } + before do + create(:event) + create(:joe) + end + + it "should be successful when the user has signed the code of conduct" do + CodeOfConductAgreement.create!({ + participant_id: Participant.where(name: 'Joe Schmoe').first.id, + event_id: Event.current_event.id, + }) - it "should be successful" do expect { - post :create, params: {session_id: session, name: 'Joe Schmoe'} + post :create, params: { session_id: session, name: 'Joe Schmoe' } }.to change { session.presentations.count }.by(1) expect(response).to redirect_to session_presentations_path(session) expect(flash[:notice]).to eq "Presenter added." end + + it 'is unsuccessful when the user hasnt signed the code of conduct' do + post :create, params: { session_id: session, name: 'Joe Schmoe' } + expect(response).to redirect_to session_presentations_path(session) + expect(flash[:error]).to match(/hasn\'t signed the Code of Conduct for this event/) + end end context "when the user is not found" do it "should set a flash message" do expect { - post :create, params: {session_id: session, name: 'Grace Hopper'} + post :create, params: { session_id: session, name: 'Grace Hopper' } }.not_to change { session.presentations.count } expect(flash[:error]).to eq "Sorry, no presenter named 'Grace Hopper' was found. Please try again." expect(response).to redirect_to session_presentations_path(session)