diff --git a/app/controllers/participants_controller.rb b/app/controllers/participants_controller.rb index d8b47272..8f0de7a3 100644 --- a/app/controllers/participants_controller.rb +++ b/app/controllers/participants_controller.rb @@ -4,6 +4,7 @@ class ParticipantsController < ApplicationController respond_to :html load_resource except: :confirm_email before_action :verify_owner, :only => [:edit, :update] + before_action :coc_agreement_param_as_timestamp, only: [:create, :update] def index respond_to do |format| @@ -40,7 +41,6 @@ def update end if @participant.update(new_params) - create_code_of_conduct_agreement_if_not_exists! flash[:notice] = "Profile updated successfully." redirect_to participant_path(@participant) else @@ -52,7 +52,6 @@ def update def create @participant.attributes = participant_params.except(:code_of_conduct_agreement) if @participant.save - create_code_of_conduct_agreement_if_not_exists! @participant.deliver_email_confirmation_instructions! flash[:notice] = "Thanks for registering an account. Please check your email to confirm your account." redirect_to root_path @@ -62,15 +61,6 @@ def create 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 - def send_confirmation_email @participant.deliver_email_confirmation_instructions! flash[:notice] = "Confirmation instructions sent! Please check your email." @@ -95,11 +85,17 @@ def participant_params :name, :email, :password, :bio, :code_of_conduct_agreement, - :contact_details + :contact_details, :coc_agreed_at ) end def verify_owner redirect_to participant_path(@participant) if @participant != current_participant end + + def coc_agreement_param_as_timestamp + if params[:participant][:code_of_conduct_agreement] == '1' + params[:participant][:coc_agreed_at] = Time.current + end + end end diff --git a/app/controllers/presentations_controller.rb b/app/controllers/presentations_controller.rb index 077a69ae..5fef621a 100644 --- a/app/controllers/presentations_controller.rb +++ b/app/controllers/presentations_controller.rb @@ -20,7 +20,7 @@ def create flash[:error] = "Sorry, no presenter #{participant_params[:name] ? "matching '#{participant_params[:name]}' " : "" }was found. Please try again." redirect_to session_presentations_path(@session) return - elsif participant.signed_code_of_conduct_for_current_event? == false + elsif participant.signed_code_of_conduct? == false flash[:error] = "Sorry, #{participant.name} hasn't signed the current Code of Conduct." redirect_to session_presentations_path(@session) return diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 6068295c..09a27794 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -78,11 +78,8 @@ def create 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, - }) + if session_params[:code_of_conduct_agreement] == '1' && !current_participant.signed_code_of_conduct? + current_participant.update!(coc_agreed_at: Time.current) end end @@ -139,5 +136,4 @@ def sessions_for_event(event) .order('created_at desc') .distinct end - end diff --git a/app/models/participant.rb b/app/models/participant.rb index 724e52dd..020962f4 100644 --- a/app/models/participant.rb +++ b/app/models/participant.rb @@ -81,13 +81,8 @@ 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? + def signed_code_of_conduct? + coc_agreed_at.present? end def attending_session?(session) diff --git a/app/views/participants/edit.html.erb b/app/views/participants/edit.html.erb index 331cd663..e7447cde 100644 --- a/app/views/participants/edit.html.erb +++ b/app/views/participants/edit.html.erb @@ -28,8 +28,8 @@ as: :boolean, label: ("I agree to the #{link_to 'Code of Conduct', 'https://minnestar.org/code-of-conduct'} governing this event.").html_safe, input_html: { - checked: @participant.signed_code_of_conduct_for_current_event?, - disabled: @participant.signed_code_of_conduct_for_current_event? + checked: @participant.signed_code_of_conduct?, + disabled: @participant.signed_code_of_conduct? } %> <% end %> diff --git a/app/views/participants/new.html.erb b/app/views/participants/new.html.erb index 8e00e969..32a50e94 100644 --- a/app/views/participants/new.html.erb +++ b/app/views/participants/new.html.erb @@ -17,8 +17,8 @@ required: true, # this only adds an asterisk to the label, it doesn't make the field required label: ("I agree to the #{link_to 'Code of Conduct', 'https://minnestar.org/code-of-conduct'} governing this event.").html_safe, input_html: { - checked: @participant.signed_code_of_conduct_for_current_event?, - disabled: @participant.signed_code_of_conduct_for_current_event? + checked: @participant.signed_code_of_conduct?, + disabled: @participant.signed_code_of_conduct? } %> <% end %> diff --git a/app/views/sessions/_form.html.erb b/app/views/sessions/_form.html.erb index 358bf97c..bf6ad7ef 100644 --- a/app/views/sessions/_form.html.erb +++ b/app/views/sessions/_form.html.erb @@ -17,8 +17,8 @@ required: true, label: ("I agree to the #{link_to 'Code of Conduct', 'https://minnestar.org/code-of-conduct'} governing this event.").html_safe, input_html: { - checked: current_participant.signed_code_of_conduct_for_current_event?, - disabled: current_participant.signed_code_of_conduct_for_current_event? + checked: current_participant.signed_code_of_conduct?, + disabled: current_participant.signed_code_of_conduct? } %> <% end %> diff --git a/db/migrate/20260520012517_add_coc_agreed_at_to_participants.rb b/db/migrate/20260520012517_add_coc_agreed_at_to_participants.rb new file mode 100644 index 00000000..6130eae1 --- /dev/null +++ b/db/migrate/20260520012517_add_coc_agreed_at_to_participants.rb @@ -0,0 +1,5 @@ +class AddCocAgreedAtToParticipants < ActiveRecord::Migration[7.2] + def change + add_column :participants, :coc_agreed_at, :datetime + end +end diff --git a/db/migrate/20260526173525_backfill_coc_agreed_at.rb b/db/migrate/20260526173525_backfill_coc_agreed_at.rb new file mode 100644 index 00000000..f4cbf41c --- /dev/null +++ b/db/migrate/20260526173525_backfill_coc_agreed_at.rb @@ -0,0 +1,13 @@ +class BackfillCocAgreedAt < ActiveRecord::Migration[7.2] + def up + Participant.where(coc_agreed_at: nil).find_each do |participant| + aggreement = CodeOfConductAgreement.where(participant_id: participant.id) + .order(created_at: :desc).first + participant.update_column(:coc_agreed_at, aggreement.created_at) if aggreement + end + end + + def down + Participant.update_all(coc_agreed_at: nil) + end +end diff --git a/db/schema.rb b/db/schema.rb index 0e322fbb..2c4e8a56 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2026_03_22_192507) do +ActiveRecord::Schema[7.2].define(version: 2026_05_26_173525) do create_schema "heroku_ext" # These are extensions that must be enabled in order to support this database @@ -114,6 +114,7 @@ t.datetime "email_confirmed_at", precision: nil t.integer "presentations_count", default: 0 t.integer "attendances_count", default: 0 + t.datetime "coc_agreed_at" t.index ["email"], name: "index_participants_on_email", unique: true t.index ["perishable_token"], name: "index_participants_on_perishable_token" end diff --git a/lib/tasks/app.rake b/lib/tasks/app.rake index aae3860d..5f8533ac 100644 --- a/lib/tasks/app.rake +++ b/lib/tasks/app.rake @@ -517,6 +517,7 @@ namespace :app do participant.email = FFaker::Internet.safe_email participant.password = 'password' participant.bio = FFaker::Lorem.paragraph if [true, false].sample + participant.coc_agreed_at = Time.current participant.save! progress.increment end diff --git a/spec/controllers/participants_controller_spec.rb b/spec/controllers/participants_controller_spec.rb index 3633eea5..0425f31d 100644 --- a/spec/controllers/participants_controller_spec.rb +++ b/spec/controllers/participants_controller_spec.rb @@ -45,6 +45,25 @@ expect(response).to render_template(:new) expect(flash[:error]).to eq "There was a problem creating your account." end + + it "does not set coc_agreed_at on the participant when the user has not signed the code of conduct" do + post :create, params: { participant: { name: 'Alan Turing', + email: 'tapewriter@example.org', + password: 'infinite-memory', + code_of_conduct_agreement: '0' + } + } + expect(Participant.find_by(email: 'tapewriter@example.org').coc_agreed_at).to be_nil + end + it "sets coc_agreed_at on the participant when the user has signed the code of conduct" do + post :create, params: { participant: { name: 'Alan Turing', + email: 'tapewriter@example.org', + password: 'infinite-memory', + code_of_conduct_agreement: '1' + } + } + expect(Participant.find_by(email: 'tapewriter@example.org').coc_agreed_at).not_to be_nil + end end describe "#show" do @@ -137,6 +156,14 @@ expect(response).to redirect_to participant_path(joe) expect(joe.reload.name).to eq 'schmoe, joe' end + it "does not set coc_agreed_at on the participant when the user has not signed the code of conduct" do + put :update, params: { id: joe, participant: { name: 'Alan Turing', code_of_conduct_agreement: '0' } } + expect(joe.reload.coc_agreed_at).to be_nil + end + it "sets coc_agreed_at on the participant when the user has signed the code of conduct" do + put :update, params: { id: joe, participant: { code_of_conduct_agreement: '1' } } + expect(joe.reload.coc_agreed_at).not_to be_nil + end describe "more attributes are not required" do it "should be successful" do diff --git a/spec/controllers/presentations_controller_spec.rb b/spec/controllers/presentations_controller_spec.rb index d2493c12..3fe11228 100644 --- a/spec/controllers/presentations_controller_spec.rb +++ b/spec/controllers/presentations_controller_spec.rb @@ -20,10 +20,7 @@ context "when the user is found by id" do it "should be successful when the user has signed the code of conduct" do - CodeOfConductAgreement.create!({ - participant_id: participant.id, - event_id: Event.current_event.id, - }) + participant.update!(coc_agreed_at: Time.current) expect { post :create, params: { session_id: session, id: participant.id } @@ -41,10 +38,7 @@ context "when the user is found by name" do it "should be successful when the user has signed the code of conduct" do - CodeOfConductAgreement.create!({ - participant_id: participant.id, - event_id: Event.current_event.id, - }) + participant.update!(coc_agreed_at: Time.current) expect { post :create, params: { session_id: session, name: participant.name } diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 13b19981..801353e0 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -138,6 +138,29 @@ expect(assigns[:session].category_ids).to include category.id expect(flash[:notice]).to eq "Thanks for adding your session." end + it "should sign code of conduct if param is present" do + expect { + post :create, params: { session: { title: "new title", + description: "new description", + category_ids: [category.id], + level_id: "2", + code_of_conduct_agreement: "1", + } + } + }.to change { Session.count }.by(1) + expect(user.reload.coc_agreed_at).not_to be_nil + end + it "should not sign code of conduct if param is not present" do + expect { + post :create, params: { session: { title: "new title", + description: "new description", + category_ids: [category.id], + level_id: "2", + } + } + }.to change { Session.count }.by(1) + expect(user.reload.coc_agreed_at).to be_nil + end end context "with invalid values" do