Split status batch and moderation actions (#37970)

This commit is contained in:
David Roetzel
2026-02-25 11:07:46 +01:00
committed by GitHub
parent 919b1e69b8
commit e2c9426c77
8 changed files with 166 additions and 145 deletions

View File

@@ -13,7 +13,7 @@ class Admin::Reports::ActionsController < Admin::BaseController
case action_from_button
when 'delete', 'mark_as_sensitive'
Admin::StatusBatchAction.new(status_batch_action_params).save!
Admin::ModerationAction.new(moderation_action_params).save!
when 'silence', 'suspend'
Admin::AccountAction.new(account_action_params).save!
else
@@ -25,9 +25,8 @@ class Admin::Reports::ActionsController < Admin::BaseController
private
def status_batch_action_params
def moderation_action_params
shared_params
.merge(status_ids: @report.status_ids)
end
def account_action_params

View File

@@ -78,8 +78,6 @@ module Admin
'report'
elsif params[:remove_from_report]
'remove_from_report'
elsif params[:delete]
'delete'
end
end
end

View File

@@ -66,20 +66,6 @@ class Admin::AccountAction < Admin::BaseAction
end
end
def process_strike!
@warning = target_account.strikes.create!(
account: current_account,
report: report,
action: type,
text: text_for_warning,
status_ids: status_ids
)
# A log entry is only interesting if the warning contains
# custom text from someone. Otherwise it's just noise.
log_action(:create, @warning) if @warning.text.present? && type == 'none'
end
def process_reports!
# If we're doing "mark as resolved" on a single report,
# then we want to keep other reports open in case they
@@ -131,17 +117,6 @@ class Admin::AccountAction < Admin::BaseAction
queue_suspension_worker! if type == 'suspend'
end
def process_notification!
return unless warnable?
UserMailer.warning(target_account.user, warning).deliver_later!
LocalNotificationWorker.perform_async(target_account.id, warning.id, 'AccountWarning', 'moderation_warning')
end
def warnable?
send_email_notification? && target_account.local?
end
def status_ids
report.status_ids if with_report? && include_statuses?
end

View File

@@ -39,4 +39,27 @@ class Admin::BaseAction
def with_report?
!report.nil?
end
private
def process_strike!(action = type)
@warning = target_account.strikes.create!(
account: current_account,
report: report,
action:,
text: text_for_warning,
status_ids: status_ids
)
end
def process_notification!
return unless warnable?
UserMailer.warning(target_account.user, warning).deliver_later!
LocalNotificationWorker.perform_async(target_account.id, warning.id, 'AccountWarning', 'moderation_warning')
end
def warnable?
send_email_notification? && target_account.local?
end
end

View File

@@ -0,0 +1,84 @@
# frozen_string_literal: true
class Admin::ModerationAction < Admin::BaseAction
TYPES = %w(
delete
mark_as_sensitive
).freeze
validates :report_id, presence: true
private
def status_ids
report.status_ids
end
def statuses
@statuses ||= Status.with_discarded.where(id: status_ids).reorder(nil)
end
def process_action!
case type
when 'delete'
handle_delete!
when 'mark_as_sensitive'
handle_mark_as_sensitive!
end
end
def handle_delete!
statuses.each { |status| authorize([:admin, status], :destroy?) }
ApplicationRecord.transaction do
statuses.each do |status|
status.discard_with_reblogs
log_action(:destroy, status)
end
report.resolve!(current_account)
log_action(:resolve, report)
process_strike!(:delete_statuses)
statuses.each { |status| Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true) } unless target_account.local?
end
process_notification!
RemovalWorker.push_bulk(status_ids) { |status_id| [status_id, { 'preserve' => target_account.local?, 'immediate' => !target_account.local? }] }
end
def handle_mark_as_sensitive!
representative_account = Account.representative
# Can't use a transaction here because UpdateStatusService queues
# Sidekiq jobs
statuses.includes(:media_attachments, preview_cards_status: :preview_card).find_each do |status|
next if status.discarded? || !(status.with_media? || status.with_preview_card?)
authorize([:admin, status], :update?)
if target_account.local?
UpdateStatusService.new.call(status, representative_account.id, sensitive: true)
else
status.update(sensitive: true)
end
log_action(:update, status)
report.resolve!(current_account)
log_action(:resolve, report)
end
process_strike!(:mark_statuses_as_sensitive)
process_notification!
end
def target_account
report.target_account
end
def text_for_warning = text
end

View File

@@ -2,8 +2,6 @@
class Admin::StatusBatchAction < Admin::BaseAction
TYPES = %w(
delete
mark_as_sensitive
report
remove_from_report
).freeze
@@ -20,10 +18,6 @@ class Admin::StatusBatchAction < Admin::BaseAction
return if status_ids.empty?
case type
when 'delete'
handle_delete!
when 'mark_as_sensitive'
handle_mark_as_sensitive!
when 'report'
handle_report!
when 'remove_from_report'
@@ -31,71 +25,6 @@ class Admin::StatusBatchAction < Admin::BaseAction
end
end
def handle_delete!
statuses.each { |status| authorize([:admin, status], :destroy?) }
ApplicationRecord.transaction do
statuses.each do |status|
status.discard_with_reblogs
log_action(:destroy, status)
end
if with_report?
report.resolve!(current_account)
log_action(:resolve, report)
end
@warning = target_account.strikes.create!(
action: :delete_statuses,
account: current_account,
report: report,
status_ids: status_ids,
text: text
)
statuses.each { |status| Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true) } unless target_account.local?
end
process_notification!
RemovalWorker.push_bulk(status_ids) { |status_id| [status_id, { 'preserve' => target_account.local?, 'immediate' => !target_account.local? }] }
end
def handle_mark_as_sensitive!
representative_account = Account.representative
# Can't use a transaction here because UpdateStatusService queues
# Sidekiq jobs
statuses.includes(:media_attachments, preview_cards_status: :preview_card).find_each do |status|
next if status.discarded? || !(status.with_media? || status.with_preview_card?)
authorize([:admin, status], :update?)
if target_account.local?
UpdateStatusService.new.call(status, representative_account.id, sensitive: true)
else
status.update(sensitive: true)
end
log_action(:update, status)
if with_report?
report.resolve!(current_account)
log_action(:resolve, report)
end
end
@warning = target_account.strikes.create!(
action: :mark_statuses_as_sensitive,
account: current_account,
report: report,
status_ids: status_ids,
text: text
)
process_notification!
end
def handle_report!
@report = Report.new(report_params) unless with_report?
@report.status_ids = (@report.status_ids + allowed_status_ids).uniq
@@ -111,17 +40,6 @@ class Admin::StatusBatchAction < Admin::BaseAction
report.save!
end
def process_notification!
return unless warnable?
UserMailer.warning(target_account.user, @warning).deliver_later!
LocalNotificationWorker.perform_async(target_account.id, @warning.id, 'AccountWarning', 'moderation_warning')
end
def warnable?
send_email_notification && target_account.local?
end
def target_account
@target_account ||= statuses.first.account
end

View File

@@ -0,0 +1,57 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::ModerationAction do
subject do
described_class.new(
current_account:,
type:,
report_id:,
text:
)
end
let(:current_account) { Fabricate(:admin_user).account }
let(:target_account) { Fabricate(:account) }
let(:statuses) { Fabricate.times(2, :status, account: target_account) }
let(:status_ids) { statuses.map(&:id) }
let(:report) { Fabricate(:report, target_account:, status_ids:) }
let(:report_id) { report.id }
let(:text) { 'test' }
describe '#save!' do
context 'when `type` is `delete`' do
let(:type) { 'delete' }
it 'discards the statuses' do
subject.save!
statuses.each do |status|
expect(status.reload).to be_discarded
end
expect(report.reload).to be_action_taken
end
end
context 'when `type` is `mark_as_sensitive`' do
let(:type) { 'mark_as_sensitive' }
before do
preview_card = Fabricate(:preview_card)
statuses.each do |status|
PreviewCardsStatus.create!(status:, preview_card:)
end
end
it 'marks the statuses as sensitive' do
subject.save!
statuses.each do |status|
expect(status.reload).to be_sensitive
end
expect(report.reload).to be_action_taken
end
end
end
end

View File

@@ -22,39 +22,6 @@ RSpec.describe Admin::StatusBatchAction do
let(:text) { 'test' }
describe '#save!' do
context 'when `type` is `delete`' do
let(:type) { 'delete' }
it 'discards the statuses' do
subject.save!
statuses.each do |status|
expect(status.reload).to be_discarded
end
expect(report.reload).to be_action_taken
end
end
context 'when `type` is `mark_as_sensitive`' do
let(:type) { 'mark_as_sensitive' }
before do
preview_card = Fabricate(:preview_card)
statuses.each do |status|
PreviewCardsStatus.create!(status:, preview_card:)
end
end
it 'marks the statuses as sensitive' do
subject.save!
statuses.each do |status|
expect(status.reload).to be_sensitive
end
expect(report.reload).to be_action_taken
end
end
context 'when `type` is `report`' do
let(:report_id) { nil }
let(:type) { 'report' }