diff --git a/app/controllers/admin/workshops_controller.rb b/app/controllers/admin/workshops_controller.rb index d6ac0f6a0..0802c8676 100644 --- a/app/controllers/admin/workshops_controller.rb +++ b/app/controllers/admin/workshops_controller.rb @@ -27,11 +27,10 @@ def new def create resolve_chapter_name_to_id @workshop = Workshop.new(workshop_params) + @workshop.created_by = current_user authorize(@workshop) if workshop_type_valid? && @workshop.save - assign_organisers_or_default - assign_host(host_id) - redirect_to admin_workshop_path(@workshop), notice: I18n.t('admin.messages.workshop.created') + finish_creation else flash[:warning] = @workshop.errors.full_messages; render 'new' end @@ -226,6 +225,14 @@ def assign_organisers(organiser_ids) revoke_organiser_access(organiser_ids) end + def finish_creation + MemberActivityRecorder.record(actor: current_user, key: 'workshop.created', + trackable: @workshop) + assign_organisers_or_default + assign_host(host_id) + redirect_to admin_workshop_path(@workshop), notice: I18n.t('admin.messages.workshop.created') + end + def assign_organisers_or_default if organiser_ids.present? assign_organisers(organiser_ids) diff --git a/app/models/workshop.rb b/app/models/workshop.rb index 036af1967..6c47b5ae8 100644 --- a/app/models/workshop.rb +++ b/app/models/workshop.rb @@ -21,6 +21,7 @@ class Workshop < ApplicationRecord has_many :invitation_logs, as: :loggable belongs_to :chapter + belongs_to :created_by, class_name: 'Member', optional: true, inverse_of: false default_scope { order('date_and_time DESC') } scope :students, -> { joins(:invitations).where(invitation: { name: 'Student', attended: true }) } diff --git a/app/services/invitation_logger.rb b/app/services/invitation_logger.rb index 314dc95ab..d1e73e09c 100644 --- a/app/services/invitation_logger.rb +++ b/app/services/invitation_logger.rb @@ -8,7 +8,7 @@ def initialize(loggable, initiator, audience, action, chapter_id: nil) @log = nil end - def start_batch + def start_batch # rubocop:disable Metrics/MethodLength @log = InvitationLog.create!( loggable: @loggable, initiator: @initiator, @@ -18,6 +18,10 @@ def start_batch started_at: Time.current, status: :running ) + + MemberActivityRecorder.record(actor: @initiator, key: 'invitation.send_batch', trackable: @loggable) + + @log end def log_success(member, invitation = nil) diff --git a/db/migrate/20260908085056_add_created_by_id_to_workshops.rb b/db/migrate/20260908085056_add_created_by_id_to_workshops.rb new file mode 100644 index 000000000..75fed0573 --- /dev/null +++ b/db/migrate/20260908085056_add_created_by_id_to_workshops.rb @@ -0,0 +1,8 @@ +class AddCreatedByIdToWorkshops < ActiveRecord::Migration[8.1] + def change + add_column :workshops, :created_by_id, :integer + safety_assured do + add_foreign_key :workshops, :members, column: :created_by_id, on_delete: :nullify + end + end +end diff --git a/db/migrate/20260908085057_add_created_by_id_index_to_workshops.rb b/db/migrate/20260908085057_add_created_by_id_index_to_workshops.rb new file mode 100644 index 000000000..1175d5a2b --- /dev/null +++ b/db/migrate/20260908085057_add_created_by_id_index_to_workshops.rb @@ -0,0 +1,7 @@ +class AddCreatedByIdIndexToWorkshops < ActiveRecord::Migration[8.1] + disable_ddl_transaction! + + def change + add_index :workshops, :created_by_id, algorithm: :concurrently + end +end diff --git a/db/schema.rb b/db/schema.rb index d149c1c8b..2191103ab 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[8.1].define(version: 2026_08_31_100100) do +ActiveRecord::Schema[8.1].define(version: 2026_09_08_085057) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -634,6 +634,7 @@ t.string "check_in_code" t.integer "coach_spaces", default: 0 t.datetime "created_at", precision: nil + t.integer "created_by_id" t.datetime "date_and_time", precision: nil t.text "description" t.datetime "ends_at", precision: nil @@ -647,12 +648,14 @@ t.string "title" t.datetime "updated_at", precision: nil t.boolean "virtual", default: false - t.index ["check_in_code"], name: "index_workshops_on_check_in_code", unique: true t.index ["chapter_id"], name: "index_workshops_on_chapter_id" + t.index ["check_in_code"], name: "index_workshops_on_check_in_code", unique: true + t.index ["created_by_id"], name: "index_workshops_on_created_by_id" t.index ["date_and_time"], name: "index_workshops_on_date_and_time" end add_foreign_key "invitation_log_entries", "invitation_logs" add_foreign_key "invitation_logs", "members", column: "initiator_id" add_foreign_key "member_email_deliveries", "members" + add_foreign_key "workshops", "members", column: "created_by_id", on_delete: :nullify end diff --git a/spec/controllers/admin/workshops_controller_spec.rb b/spec/controllers/admin/workshops_controller_spec.rb index 8619a33a8..1f2db20ac 100644 --- a/spec/controllers/admin/workshops_controller_spec.rb +++ b/spec/controllers/admin/workshops_controller_spec.rb @@ -167,6 +167,26 @@ def assigns(symbol) post :create, params: { workshop: { rsvp_close_local_date: '01/12/2020', rsvp_close_local_time: '15:00', host: '' } } end.not_to raise_error end + + it 'stamps created_by and records workshop.created' do + post :create, params: { workshop: { + chapter_id: workshop.chapter.id, + local_date: (Time.zone.now + 1.week).strftime('%d/%m/%Y'), + local_time: '15:00', + local_end_time: '17:00', + virtual: '1', + slack_channel: '#test', + slack_channel_link: 'https://slack.com/test', + coach_spaces: 5, + student_spaces: 15, + host: nil + } } + + created = Workshop.unscoped.order(:id).last + expect(created.created_by).to eq(admin) + expect(PublicActivity::Activity.exists?(owner: admin, key: 'workshop.created', + trackable: created)).to be(true) + end end describe 'DELETE #destroy' do diff --git a/spec/services/invitation_logger_spec.rb b/spec/services/invitation_logger_spec.rb index 6a71320bd..4c94f6d06 100644 --- a/spec/services/invitation_logger_spec.rb +++ b/spec/services/invitation_logger_spec.rb @@ -157,4 +157,14 @@ expect(log.reload.failure_count).to eq 0 end end + + describe '#activity_recording' do + it 'records invitation.send_batch for the initiator' do + described_class.new(workshop, initiator, 'all', 'invite').start_batch + + expect(PublicActivity::Activity.exists?(owner: initiator, + key: 'invitation.send_batch', + trackable: workshop)).to be(true) + end + end end