From 74136d8ac129c094c80bc75e0f24b9a19cda7e61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:12:35 +0000 Subject: [PATCH 1/6] Bump uri from 0.13.2 to 0.13.3 in the bundler group across 1 directory Bumps the bundler group with 1 update in the / directory: [uri](https://github.com/ruby/uri). Updates `uri` from 0.13.2 to 0.13.3 - [Release notes](https://github.com/ruby/uri/releases) - [Commits](https://github.com/ruby/uri/compare/v0.13.2...v0.13.3) --- updated-dependencies: - dependency-name: uri dependency-version: 0.13.3 dependency-type: indirect dependency-group: bundler ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9ebfde1..0dc1d2f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -523,7 +523,7 @@ GEM uber (0.1.0) unaccent (0.4.0) unicode-display_width (2.5.0) - uri (0.13.2) + uri (0.13.3) useragent (0.16.11) warden (1.2.9) rack (>= 2.0.9) From edc7af28b99a654949d526513bbefc5b499f3018 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Dec 2025 22:10:52 +0000 Subject: [PATCH 2/6] Bump trix in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [trix](https://github.com/basecamp/trix). Updates `trix` from 2.1.15 to 2.1.16 - [Release notes](https://github.com/basecamp/trix/releases) - [Commits](https://github.com/basecamp/trix/compare/v2.1.15...v2.1.16) --- updated-dependencies: - dependency-name: trix dependency-version: 2.1.16 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 551427e..1e40830 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "postcss-cli": "^11.0.0", "sass": "^1.70.0", "sortablejs": "^1.15.6", - "trix": "^2.1.15" + "trix": "^2.1.16" }, "scripts": { "build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets", diff --git a/yarn.lock b/yarn.lock index 0c79b36..6333b99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3863,10 +3863,10 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" -trix@^2.1.15: - version "2.1.15" - resolved "https://registry.yarnpkg.com/trix/-/trix-2.1.15.tgz#fabad796ea779a8ae96522402fbc214cbfc4015f" - integrity sha512-LoaXWczdTUV8+3Box92B9b1iaDVbxD14dYemZRxi3PwY+AuDm97BUJV2aHLBUFPuDABhxp0wzcbf0CxHCVmXiw== +trix@^2.1.16: + version "2.1.16" + resolved "https://registry.yarnpkg.com/trix/-/trix-2.1.16.tgz#601be839258b87cc83019915650c50eb7cbc161e" + integrity sha512-XtZgWI+oBvLzX7CWnkIf+ZWC+chL+YG/TkY43iMTV0Zl+CJjn18B1GJUCEWJ8qgfpcyMBuysnNAfPWiv2sV14A== dependencies: dompurify "^3.2.5" From 39cb44542508fa2dbc0f4c905f23f1bb62a00382 Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Tue, 13 Jan 2026 12:07:23 -0500 Subject: [PATCH 3/6] Enhance judging instructions functionality and UI - Updated JudgingRoundsController to handle selected judge assignments, allowing users to send instructions to specific judges or all judges if none are selected. - Added `instructions_sent_at` column to RoundJudgeAssignments model to track when instructions are sent. - Improved the manage judges view with a modal for selecting judges to receive instructions, including visual feedback for sent instructions. - Implemented error handling for cases where no judges are selected, redirecting with an alert message. - Enabled mailer previews in production and staging with authorization for enhanced testing capabilities. --- app/controllers/judging_rounds_controller.rb | 20 +++- app/models/round_judge_assignment.rb | 13 +-- .../contest_instances/_manage_judges.html.erb | 98 ++++++++++++++++--- config/initializers/mailer_previews.rb | 26 +++++ ...ions_sent_at_to_round_judge_assignments.rb | 5 + db/schema.rb | 3 +- spec/factories/round_judge_assignments.rb | 13 +-- 7 files changed, 151 insertions(+), 27 deletions(-) create mode 100644 config/initializers/mailer_previews.rb create mode 100644 db/migrate/20260113165213_add_instructions_sent_at_to_round_judge_assignments.rb diff --git a/app/controllers/judging_rounds_controller.rb b/app/controllers/judging_rounds_controller.rb index 81622f1..713409f 100644 --- a/app/controllers/judging_rounds_controller.rb +++ b/app/controllers/judging_rounds_controller.rb @@ -107,12 +107,30 @@ def send_instructions return end + # Get selected judge assignment IDs, or use all if none selected + selected_assignment_ids = params[:judge_assignment_ids]&.compact_blank || [] + + if selected_assignment_ids.any? + assignments = @judging_round.round_judge_assignments.active.includes(:user).where(id: selected_assignment_ids) + else + # If no selection, send to all (backward compatibility) + assignments = @judging_round.round_judge_assignments.active.includes(:user) + end + + if assignments.empty? + redirect_to container_contest_description_contest_instance_judging_assignments_path( + @container, @contest_description, @contest_instance + ), alert: 'No judges selected.' + return + end + sent_count = 0 failed_emails = [] - @judging_round.round_judge_assignments.active.includes(:user).each do |assignment| + assignments.each do |assignment| begin JudgingInstructionsMailer.send_instructions(assignment).deliver_later + assignment.update_column(:instructions_sent_at, Time.current) sent_count += 1 rescue => e failed_emails << assignment.user.email diff --git a/app/models/round_judge_assignment.rb b/app/models/round_judge_assignment.rb index ef0798f..c9d9ed2 100644 --- a/app/models/round_judge_assignment.rb +++ b/app/models/round_judge_assignment.rb @@ -2,12 +2,13 @@ # # Table name: round_judge_assignments # -# id :bigint not null, primary key -# active :boolean default(TRUE) -# created_at :datetime not null -# updated_at :datetime not null -# judging_round_id :bigint not null -# user_id :bigint not null +# id :bigint not null, primary key +# active :boolean default(TRUE) +# instructions_sent_at :datetime +# created_at :datetime not null +# updated_at :datetime not null +# judging_round_id :bigint not null +# user_id :bigint not null # # Indexes # diff --git a/app/views/contest_instances/_manage_judges.html.erb b/app/views/contest_instances/_manage_judges.html.erb index 8aff54b..7ae312e 100644 --- a/app/views/contest_instances/_manage_judges.html.erb +++ b/app/views/contest_instances/_manage_judges.html.erb @@ -7,16 +7,12 @@
Round <%= round.round_number %>
<% if round.judges.any? %> - <%= button_to send_instructions_container_contest_description_contest_instance_judging_round_path( - @container, @contest_description, contest_instance, round - ), - method: :post, - class: "btn btn-sm btn-outline-success", - data: { - turbo_confirm: "Send judging instructions to #{pluralize(round.judges.count, 'judge')}?" - } do %> + <% end %> <%= round.active ? 'Active' : (round.completed ? 'Completed' : 'Pending') %> @@ -36,13 +32,18 @@ Judges Assigned:
- <% if round.judges.any? %> - <% round.judges.each do |judge| %> - + <% if assignments.any? %> + <% assignments.each do |assignment| %> + <% judge = assignment.user %> + + data-bs-title="<%= h(judge.display_name_or_first_name_last_name) %>
<%= h(display_email(judge.email)) %><% if assignment.instructions_sent_at %>
Instructions sent: <%= I18n.l(assignment.instructions_sent_at, format: :short) %><% end %>"> <%= judge.display_name_and_uid %> + <% if assignment.instructions_sent_at %> + + <% end %>
<% end %> <% else %> @@ -91,6 +92,77 @@ <% end %>
+ + + <% end %> diff --git a/config/initializers/mailer_previews.rb b/config/initializers/mailer_previews.rb new file mode 100644 index 0000000..9d87510 --- /dev/null +++ b/config/initializers/mailer_previews.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Enable mailer previews in production and staging with authorization +# This initializer allows mailer previews to be accessed by authorized users (Collection Administrators and Axis Mundi) + +if Rails.env.production? || Rails.env.staging? + Rails.application.config.to_prepare do + # Override Rails::MailersController to add authentication and authorization + if defined?(Rails::MailersController) + Rails::MailersController.class_eval do + include Devise::Controllers::Helpers if defined?(Devise::Controllers::Helpers) + + before_action :authenticate_user! + before_action :authorize_mailer_preview_access! + + private + + def authorize_mailer_preview_access! + unless current_user&.axis_mundi? || current_user&.administrator? + redirect_to root_path, alert: 'You are not authorized to access mailer previews.' + end + end + end + end + end +end diff --git a/db/migrate/20260113165213_add_instructions_sent_at_to_round_judge_assignments.rb b/db/migrate/20260113165213_add_instructions_sent_at_to_round_judge_assignments.rb new file mode 100644 index 0000000..329db07 --- /dev/null +++ b/db/migrate/20260113165213_add_instructions_sent_at_to_round_judge_assignments.rb @@ -0,0 +1,5 @@ +class AddInstructionsSentAtToRoundJudgeAssignments < ActiveRecord::Migration[7.2] + def change + add_column :round_judge_assignments, :instructions_sent_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index eab24cc..7b6ddcb 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: 2025_06_05_210643) do +ActiveRecord::Schema[7.2].define(version: 2026_01_13_165213) do create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "name", null: false t.text "body", size: :long @@ -298,6 +298,7 @@ t.boolean "active", default: true t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.datetime "instructions_sent_at" t.index ["judging_round_id", "user_id"], name: "index_round_judge_assignments_on_judging_round_id_and_user_id", unique: true t.index ["judging_round_id"], name: "index_round_judge_assignments_on_judging_round_id" t.index ["user_id"], name: "index_round_judge_assignments_on_user_id" diff --git a/spec/factories/round_judge_assignments.rb b/spec/factories/round_judge_assignments.rb index 7927528..0fbf133 100644 --- a/spec/factories/round_judge_assignments.rb +++ b/spec/factories/round_judge_assignments.rb @@ -2,12 +2,13 @@ # # Table name: round_judge_assignments # -# id :bigint not null, primary key -# active :boolean default(TRUE) -# created_at :datetime not null -# updated_at :datetime not null -# judging_round_id :bigint not null -# user_id :bigint not null +# id :bigint not null, primary key +# active :boolean default(TRUE) +# instructions_sent_at :datetime +# created_at :datetime not null +# updated_at :datetime not null +# judging_round_id :bigint not null +# user_id :bigint not null # # Indexes # From e41f86a12b1b1ffd49298e35173083814b14d74e Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Tue, 13 Jan 2026 15:22:20 -0500 Subject: [PATCH 4/6] Implement functionality to send copies of judging instructions to collection administrators - Updated JudgingRoundsController to include collection administrator emails when sending judging instructions, based on user selection. - Enhanced the manage judges view to add a checkbox for sending copies to collection administrators, displaying their emails dynamically. - Modified JudgingInstructionsMailer to accept CC emails for collection administrators, ensuring they receive relevant instructions. This improves communication with administrators and streamlines the judging process. --- app/controllers/judging_rounds_controller.rb | 9 ++++++++- app/mailers/judging_instructions_mailer.rb | 5 ++++- .../contest_instances/_manage_judges.html.erb | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/controllers/judging_rounds_controller.rb b/app/controllers/judging_rounds_controller.rb index 713409f..0841cce 100644 --- a/app/controllers/judging_rounds_controller.rb +++ b/app/controllers/judging_rounds_controller.rb @@ -124,12 +124,19 @@ def send_instructions return end + # Get collection administrator emails if copy requested + admin_emails = [] + if params[:send_copy_to_admin] == '1' + admin_emails = @container.assignments.container_administrators.includes(:user).map { |a| a.user.normalize_email } + end + sent_count = 0 failed_emails = [] assignments.each do |assignment| begin - JudgingInstructionsMailer.send_instructions(assignment).deliver_later + mail = JudgingInstructionsMailer.send_instructions(assignment, cc_emails: admin_emails) + mail.deliver_later assignment.update_column(:instructions_sent_at, Time.current) sent_count += 1 rescue => e diff --git a/app/mailers/judging_instructions_mailer.rb b/app/mailers/judging_instructions_mailer.rb index 8fc2fb5..d407ece 100644 --- a/app/mailers/judging_instructions_mailer.rb +++ b/app/mailers/judging_instructions_mailer.rb @@ -1,5 +1,5 @@ class JudgingInstructionsMailer < ApplicationMailer - def send_instructions(round_judge_assignment) + def send_instructions(round_judge_assignment, cc_emails: []) @round_judge_assignment = round_judge_assignment @judge = @round_judge_assignment.user @judging_round = @round_judge_assignment.judging_round @@ -23,6 +23,9 @@ def send_instructions(round_judge_assignment) subject: subject } + # Add CC to collection administrators if provided + mail_options[:cc] = cc_emails if cc_emails.any? + # Override reply_to with container's contact_email if present # If not present, the default reply-to from ApplicationMailer will be used mail_options[:reply_to] = @container.contact_email if @container.contact_email.present? diff --git a/app/views/contest_instances/_manage_judges.html.erb b/app/views/contest_instances/_manage_judges.html.erb index 7ae312e..60ce1d8 100644 --- a/app/views/contest_instances/_manage_judges.html.erb +++ b/app/views/contest_instances/_manage_judges.html.erb @@ -154,6 +154,23 @@ <% end %> +
+
+ <%= check_box_tag "send_copy_to_admin", + "1", + false, + id: "send_copy_to_admin_#{round.id}", + class: "form-check-input" %> + <%= label_tag "send_copy_to_admin_#{round.id}", + "Send a copy to Collection Administrator(s)", + class: "form-check-label" %> + <% admin_emails = @container.assignments.container_administrators.includes(:user).map { |a| a.user.email } %> + <% if admin_emails.any? %> + + Copy will be sent to: <%= admin_emails.join(', ') %> + + <% end %> +