Skip to content
20 changes: 8 additions & 12 deletions app/controllers/participants_controller.rb

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing this locally made me realize that there are four places on the front-end where we currently show the CoC checkbox:

  • 2 in participant views (new and edit)
  • 2 in session views (new and edit, via the form partial)

With this change, we should be sure to update the front-end views accordingly to make sure we're capturing CoC agreements correctly and in the right places.


After running through all four scenarios locally, I think they're still working as expected. I don't think showing a checked checkbox that's also disabled, along with a link to the CoC, whenever a user is creating or editing a session, or editing their own profile hurts anything. It's a nice reminder that we have a CoC and expect attendees & presenters to follow it.

And it's also still worth including the checkbox in all 4 places because we already have existing users in the system who haven't necessarily accepted the CoC yet.

Sorry this is a bit of a word salad, but the TLDR is that the way we're currently presenting CoC checkboxes in the front-end seems like it'll still work with these changes. But I had to walk through each scenario locally and say it all out loud before it made sense to me 🙃

Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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."
Expand All @@ -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
2 changes: 1 addition & 1 deletion app/controllers/presentations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 2 additions & 6 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -139,5 +136,4 @@ def sessions_for_event(event)
.order('created_at desc')
.distinct
end

end
9 changes: 2 additions & 7 deletions app/models/participant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions app/views/participants/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -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 %>
Expand Down
4 changes: 2 additions & 2 deletions app/views/participants/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -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 %>
Expand Down
4 changes: 2 additions & 2 deletions app/views/sessions/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -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 %>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddCocAgreedAtToParticipants < ActiveRecord::Migration[7.2]
def change
add_column :participants, :coc_agreed_at, :datetime
end
end
13 changes: 13 additions & 0 deletions db/migrate/20260526173525_backfill_coc_agreed_at.rb

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This should be a db migration, not a rake task. We should avoid writing raw SQL wherever possible, and use active record instead (as long as it's reasonably performant).

See this migration as an example (not the same, but similar idea): https://github.com/minnestar/sessionizer/blob/main/db/migrate/20260319035803_backfill_event_categories.rb

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree that it should be a migration. I'm still getting the rust off of my "this is the RoR" way of doing things and this is the correct approach. I'll see about doing it all in active record. The main issue here is that there isn't any easy/idiomatic way to do it efficiently.

I'll do the following:

  1. Update the logic to be inside a migration instead of a rake task.
  2. add an isolated commit to do the most optimized version of ORM instead of efficient SQL. If the ORM is good enough, great. If it's not, we can revert the one commit.

Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions lib/tasks/app.rake
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions spec/controllers/participants_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 2 additions & 8 deletions spec/controllers/presentations_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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 }
Expand Down
23 changes: 23 additions & 0 deletions spec/controllers/sessions_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading