diff --git a/app/controllers/admin/bans_controller.rb b/app/controllers/admin/bans_controller.rb index 2456a1bcb..4dc93b30d 100644 --- a/app/controllers/admin/bans_controller.rb +++ b/app/controllers/admin/bans_controller.rb @@ -10,6 +10,8 @@ def create @ban.added_by = current_user if @ban.save + MemberActivityRecorder.record(actor: current_user, key: 'member.banned', + recipient: @ban.member, trackable: @ban) MemberMailer.ban(@ban.member, @ban).deliver_now redirect_to [:admin, @member], notice: t('.success') else diff --git a/app/controllers/admin/chapters/organisers_controller.rb b/app/controllers/admin/chapters/organisers_controller.rb index 23492852a..7aa277722 100644 --- a/app/controllers/admin/chapters/organisers_controller.rb +++ b/app/controllers/admin/chapters/organisers_controller.rb @@ -14,6 +14,8 @@ def create member = Member.find(params[:organiser][:organiser]) member.add_role(:organiser, @chapter) + MemberActivityRecorder.record(actor: current_user, key: 'organiser_role.granted', + recipient: member, trackable: @chapter) redirect_to admin_chapter_organisers_path(@chapter), notice: 'Successfully added organiser.' end @@ -24,6 +26,8 @@ def destroy member = Member.find(params[:id]) member.remove_role(:organiser, @chapter) + MemberActivityRecorder.record(actor: current_user, key: 'organiser_role.revoked', + recipient: member, trackable: @chapter) redirect_to admin_chapter_organisers_path(@chapter), notice: 'Successfully removed organiser.' end diff --git a/app/controllers/admin/invitation_controller.rb b/app/controllers/admin/invitation_controller.rb index a8eff1bb4..2250fa20d 100644 --- a/app/controllers/admin/invitation_controller.rb +++ b/app/controllers/admin/invitation_controller.rb @@ -3,9 +3,12 @@ class Admin::InvitationController < Admin::ApplicationController # event invitations + # rubocop:disable Metrics/AbcSize def update invitation = Invitation.find_by(token: params[:invitation][:id]) invitation.update(attending: true, verified: true, verified_by: current_user, source: Invitation::SOURCE_ADMIN) + MemberActivityRecorder.record(actor: current_user, key: 'invitation.verified', + trackable: invitation, recipient: invitation.member) EventInvitationMailer.attending(invitation.event, invitation.member, invitation).deliver_now @@ -14,10 +17,14 @@ def update notice: "You have verified #{invitation.member.full_name}'s spot at the event!" ) end + # rubocop:enable Metrics/AbcSize + # rubocop:disable Metrics/AbcSize def verify invitation = Invitation.find_by(token: params[:invitation_id]) invitation.update(verified: true, verified_by_id: current_user.id, source: Invitation::SOURCE_ADMIN) + MemberActivityRecorder.record(actor: current_user, key: 'invitation.verified', + trackable: invitation, recipient: invitation.member) EventInvitationMailer.attending(invitation.event, invitation.member, invitation).deliver_now @@ -26,6 +33,7 @@ def verify notice: "You have verified #{invitation.member.full_name}'s spot at the event!" ) end + # rubocop:enable Metrics/AbcSize def cancel invitation = Invitation.find_by(token: params[:invitation_id]) diff --git a/app/controllers/admin/invitations_controller.rb b/app/controllers/admin/invitations_controller.rb index 7054e79fc..bd7076784 100644 --- a/app/controllers/admin/invitations_controller.rb +++ b/app/controllers/admin/invitations_controller.rb @@ -40,6 +40,8 @@ def update_attendance(attending:, attended:) def update_to_attended @invitation.update(attended: true, source: Invitation::SOURCE_ADMIN) + MemberActivityRecorder.record(actor: current_user, key: 'invitation.rsvp_override', + trackable: @invitation, recipient: @invitation.member) end def update_to_unattended @@ -55,6 +57,11 @@ def update_to_attending source: Invitation::SOURCE_ADMIN ) + if update_successful + MemberActivityRecorder.record(actor: current_user, key: 'invitation.rsvp_override', + trackable: @invitation, recipient: @invitation.member) + end + { message: update_successful ? attending_successful : attending_failed, error: !update_successful @@ -74,6 +81,8 @@ def attending_failed def update_to_not_attending @invitation.update!(attending: false, last_overridden_by_id: current_user.id) + MemberActivityRecorder.record(actor: current_user, key: 'invitation.rsvp_override', + trackable: @invitation, recipient: @invitation.member) { message: "You have removed #{@invitation.member.full_name} from the workshop.", diff --git a/app/controllers/admin/meeting_invitations_controller.rb b/app/controllers/admin/meeting_invitations_controller.rb index cea0f419e..2b293070a 100644 --- a/app/controllers/admin/meeting_invitations_controller.rb +++ b/app/controllers/admin/meeting_invitations_controller.rb @@ -6,6 +6,8 @@ def update attended = params.permit(:attended)[:attended] @invitation.update(attending: status, attended:) + MemberActivityRecorder.record(actor: current_user, key: 'meeting_invitation.updated', + trackable: @invitation, recipient: @invitation.member) redirect_to [:admin, @invitation.meeting], notice: t('admin.messages.invitation.update_rsvp', name: @invitation.member.full_name) @@ -25,6 +27,8 @@ def create if invitation.save MeetingInvitationMailer.approve_from_waitlist(meeting, member).deliver_now + MemberActivityRecorder.record(actor: current_user, key: 'meeting_invitation.created', + trackable: invitation, recipient: member) redirect_to [:admin, meeting], notice: t('admin.messages.invitation.rsvp_member', name: member.full_name) else redirect_to [:admin, meeting], notice: t('admin.messages.invitation.rsvp_error', name: member.full_name) diff --git a/app/controllers/admin/member_notes_controller.rb b/app/controllers/admin/member_notes_controller.rb index 5b99f655f..03c03fe6c 100644 --- a/app/controllers/admin/member_notes_controller.rb +++ b/app/controllers/admin/member_notes_controller.rb @@ -4,7 +4,12 @@ def create authorize @note @note.author = current_user - flash[:error] = @note.errors.full_messages unless @note.save + if @note.save + MemberActivityRecorder.record(actor: current_user, key: 'member_note.created', + trackable: @note, recipient: @note.member) + else + flash[:error] = @note.errors.full_messages + end redirect_back fallback_location: root_path end diff --git a/app/controllers/admin/members_controller.rb b/app/controllers/admin/members_controller.rb index 27483b61f..a649f6815 100644 --- a/app/controllers/admin/members_controller.rb +++ b/app/controllers/admin/members_controller.rb @@ -39,11 +39,14 @@ def events def update_subscriptions subscription = @member.subscriptions.find_by!(group_id: params[:group]) + group = subscription.group SubscriptionMailingListService.unsubscribe(subscription) flash[:notice] = t('.unsubscribe', member: @member.full_name, - chapter: subscription.group.chapter.city, - group: subscription.group.name) + chapter: group.chapter.city, + group: group.name) subscription.destroy + MemberActivityRecorder.record(actor: current_user, key: 'subscription.admin_updated', + trackable: group, recipient: @member) redirect_back fallback_location: root_path end diff --git a/spec/controllers/admin/bans_controller_spec.rb b/spec/controllers/admin/bans_controller_spec.rb index 9b1d24b4f..ce1d14e4b 100644 --- a/spec/controllers/admin/bans_controller_spec.rb +++ b/spec/controllers/admin/bans_controller_spec.rb @@ -24,4 +24,17 @@ expect(response.body).to include("value=\"#{expected}\"") end end + + describe 'POST #create' do + it 'records member.banned' do + expect do + post :create, params: { member_id: member.id, ban: { reason: 'spam', note: 'banned member', + explanation: 'test', permanent: '1', + expires_at: 1.month.from_now.to_s } } + end.to change { + PublicActivity::Activity.exists?(owner: admin, key: 'member.banned', + recipient: member) + }.from(false).to(true) + end + end end diff --git a/spec/controllers/admin/chapters/organisers_controller_spec.rb b/spec/controllers/admin/chapters/organisers_controller_spec.rb new file mode 100644 index 000000000..81066820c --- /dev/null +++ b/spec/controllers/admin/chapters/organisers_controller_spec.rb @@ -0,0 +1,33 @@ +require 'rails_helper' + +RSpec.describe Admin::Chapters::OrganisersController do + let(:admin) { Fabricate(:chapter_organiser) } + let(:chapter) { Fabricate(:chapter) } + let(:member) { Fabricate(:member) } + + before do + login_as_admin(admin) + end + + describe 'POST #create' do + it 'records organiser_role.granted' do + post :create, params: { + chapter_id: chapter.id, organiser: { organiser: member.id } + } + + expect(PublicActivity::Activity.exists?(owner: admin, key: 'organiser_role.granted', + recipient: member)).to be(true) + end + end + + describe 'DELETE #destroy' do + before { member.add_role(:organiser, chapter) } + + it 'records organiser_role.revoked' do + delete :destroy, params: { chapter_id: chapter.id, id: member.id } + + expect(PublicActivity::Activity.exists?(owner: admin, key: 'organiser_role.revoked', + recipient: member)).to be(true) + end + end +end diff --git a/spec/controllers/admin/invitation_controller_spec.rb b/spec/controllers/admin/invitation_controller_spec.rb new file mode 100644 index 000000000..377fb1496 --- /dev/null +++ b/spec/controllers/admin/invitation_controller_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +RSpec.describe Admin::InvitationController do + describe 'POST #verify' do + let(:invitation) { Fabricate(:invitation, attending: false, verified: nil) } + let(:admin) { Fabricate(:chapter_organiser) } + + before do + admin.add_role(:admin) + login admin + request.env['HTTP_REFERER'] = '/admin/member/3' + end + + it 'records invitation.verified' do + post :verify, params: { event_id: invitation.event.id, invitation_id: invitation.token } + + expect(PublicActivity::Activity.exists?(owner: admin, key: 'invitation.verified', + recipient: invitation.member)).to be(true) + end + end +end diff --git a/spec/controllers/admin/invitations_controller_spec.rb b/spec/controllers/admin/invitations_controller_spec.rb index 8fd8b01c5..6fd2e067d 100644 --- a/spec/controllers/admin/invitations_controller_spec.rb +++ b/spec/controllers/admin/invitations_controller_spec.rb @@ -110,4 +110,19 @@ expect(response).to redirect_to(admin_workshop_rsvp_path(workshop, q: 'Zoe', page: 2)) end end + + context 'when recording activity' do + before do + admin.add_role(:organiser, workshop.chapter) + login admin + request.env['HTTP_REFERER'] = '/admin/member/3' + end + + it 'records invitation.rsvp_override when admin forces attending' do + put :update, params: { workshop_id: workshop.id, id: invitation.token, attending: 'true' } + + expect(PublicActivity::Activity.exists?(owner: admin, key: 'invitation.rsvp_override', + recipient: invitation.member)).to be(true) + end + end end diff --git a/spec/controllers/admin/member_notes_controller_spec.rb b/spec/controllers/admin/member_notes_controller_spec.rb index 7e46f114d..a9a5ab5a1 100644 --- a/spec/controllers/admin/member_notes_controller_spec.rb +++ b/spec/controllers/admin/member_notes_controller_spec.rb @@ -34,5 +34,15 @@ post :create, params: { member_note: { note: ' ', member_id: member.id } } end.not_to(change { MemberNote.all.count }) end + + it 'records member_note.created' do + member = Fabricate(:member) + login admin + request.env['HTTP_REFERER'] = '/admin/member/3' + + post :create, params: { member_note: { member_id: member.id, note: 'context' } } + + expect(PublicActivity::Activity.exists?(key: 'member_note.created', recipient: member)).to be(true) + end end end diff --git a/spec/requests/admin_activity_member_admin_spec.rb b/spec/requests/admin_activity_member_admin_spec.rb new file mode 100644 index 000000000..f32f27f86 --- /dev/null +++ b/spec/requests/admin_activity_member_admin_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Admin member management activity' do + let(:admin) { Fabricate(:member) } + let(:chapter) { Fabricate(:chapter) } + let(:member) { Fabricate(:member) } + let(:group) { Fabricate(:group) } + + before do + admin.add_role(:admin) + Fabricate(:auth_service, member: admin, provider: 'github', uid: 'admin-uid-1') + mock_auth_hash(provider: 'github', uid: 'admin-uid-1', email: admin.email) + post '/auth/github/callback' + end + + describe 'subscription changes' do + it 'records subscription.admin_updated when admin removes a subscription' do + member.subscriptions.create!(group:) + + get admin_member_update_subscriptions_path(member), params: { group: group.id } + + expect(PublicActivity::Activity.exists?(owner: admin, key: 'subscription.admin_updated', + recipient: member)).to be(true) + end + end + + describe 'meeting invitations' do + it 'records meeting_invitation.created on admin invite' do + meeting = Fabricate(:meeting) + + post admin_meeting_invitations_path, + params: { meeting_invitations: { member: member.id, meeting_id: meeting.slug } } + + expect(PublicActivity::Activity.exists?(owner: admin, key: 'meeting_invitation.created', + recipient: member)).to be(true) + end + + it 'records meeting_invitation.updated on admin update' do + meeting = Fabricate(:meeting) + invitation = Fabricate(:meeting_invitation, member:, meeting:) + + patch admin_meeting_invitation_path(invitation), + params: { attendance_status: 'true' } + + expect(PublicActivity::Activity.exists?(owner: admin, key: 'meeting_invitation.updated', + recipient: member)).to be(true) + end + end +end