From 51f77ec0f1147eeff711615fc2e3db6c337002bb Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Thu, 15 Oct 2026 22:39:00 +0200 Subject: [PATCH] fix(admin): link the member for ban and member-note activities `activity_subject` passed Ban and MemberNote trackables through unchanged, so the activity log built `[:admin, trackable]` links for them. Neither has an admin show route: bans are a write-only resource nested under members (index/new/create) and member notes only have a create route. Rendering an admin member profile with such an activity raised `undefined method admin_ban_path` and returned 500. Both record types describe an action about a member, so link that member. Regression specs cover the helper mapping and the rendered profile page. --- app/helpers/application_helper.rb | 4 +++- .../admin/members_controller_spec.rb | 21 ++++++++++++++++ spec/helpers/application_helper_spec.rb | 24 +++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 14bfed6c8..3e5ab85bf 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -96,13 +96,15 @@ def sponsorship_level_title(level) end # Admin-page subject of a member activity: the entity the action was about. - # Returns nil when the subject is the member themselves (e.g. logins, bans). + # Returns nil when the subject is the member themselves (e.g. logins). + # Ban and MemberNote records have no admin show route, so link their member. def activity_subject(activity) case (trackable = activity.trackable) when WorkshopInvitation then trackable.workshop when Invitation then trackable.event when MeetingInvitation then trackable.meeting when Member then nil + when Ban, MemberNote then trackable.member else trackable end end diff --git a/spec/controllers/admin/members_controller_spec.rb b/spec/controllers/admin/members_controller_spec.rb index 3b3267b8a..b850d8d3e 100644 --- a/spec/controllers/admin/members_controller_spec.rb +++ b/spec/controllers/admin/members_controller_spec.rb @@ -171,6 +171,27 @@ expect(response.body).to include("href=\"#{admin_event_path(invitation.event)}\"") end + + it 'links the banned member for a ban activity' do + banned = Fabricate(:member) + ban = Fabricate(:ban, member: banned) + MemberActivityRecorder.record(actor: member, key: 'member.banned', + trackable: ban, recipient: banned) + + get :show, params: { id: member.id } + + expect(response.body).to include("href=\"#{admin_member_path(banned)}\"") + end + + it 'links the noted member for a member-note activity' do + note = Fabricate(:member_note, author: member) + MemberActivityRecorder.record(actor: member, key: 'member_note.created', + trackable: note, recipient: note.member) + + get :show, params: { id: member.id } + + expect(response.body).to include("href=\"#{admin_member_path(note.member)}\"") + end end end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 3dc2bde3f..6d56f7ef2 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -63,6 +63,30 @@ end end + describe '#activity_subject' do + def activity_with(trackable) + PublicActivity::Activity.new(key: 'test', trackable:) + end + + it 'returns the banned member for a ban, since bans have no admin route' do + ban = Fabricate(:ban, member: Fabricate(:member)) + + expect(helper.activity_subject(activity_with(ban))).to eq(ban.member) + end + + it 'returns the noted member for a member note, since notes have no admin route' do + note = Fabricate(:member_note) + + expect(helper.activity_subject(activity_with(note))).to eq(note.member) + end + + it 'returns nil when the trackable is the member themselves' do + member = Fabricate(:member) + + expect(helper.activity_subject(activity_with(member))).to be_nil + end + end + describe '#stylesheet_link_tag and #javascript_include_tag' do around do |example| ApplicationHelper::HEAD_ASSET_TAG_CACHE.clear