From 3c033c4352f8b156887cd7157b4a89c23a545838 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 22 Dec 2018 20:02:09 +0100 Subject: Add moderation warnings (#9519) * Add moderation warnings Replace individual routes for disabling, silencing, and suspending a user, as well as the report update route, with a unified account action controller that allows you to select an action (none, disable, silence, suspend) as well as whether it should generate an e-mail notification with optional custom text. That notification, with the optional custom text, is saved as a warning. Additionally, there are warning presets you can configure to save time when performing the above. * Use Account#local_username_and_domain --- app/models/account.rb | 8 ++ app/models/account_warning.rb | 23 ++++ app/models/account_warning_preset.rb | 15 +++ app/models/admin/account_action.rb | 134 +++++++++++++++++++++++ app/models/concerns/account_associations.rb | 2 + app/models/form/admin_suspension_confirmation.rb | 7 -- 6 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 app/models/account_warning.rb create mode 100644 app/models/account_warning_preset.rb create mode 100644 app/models/admin/account_action.rb delete mode 100644 app/models/form/admin_suspension_confirmation.rb (limited to 'app/models') diff --git a/app/models/account.rb b/app/models/account.rb index 5a7a9c580..16ef6c187 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -155,6 +155,14 @@ class Account < ApplicationRecord ResolveAccountService.new.call(acct) end + def silence! + update!(silenced: true) + end + + def unsilence! + update!(silenced: false) + end + def suspend! transaction do user&.disable! if local? diff --git a/app/models/account_warning.rb b/app/models/account_warning.rb new file mode 100644 index 000000000..157e6c04d --- /dev/null +++ b/app/models/account_warning.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: account_warnings +# +# id :bigint(8) not null, primary key +# account_id :bigint(8) +# target_account_id :bigint(8) +# action :integer default("none"), not null +# text :text default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class AccountWarning < ApplicationRecord + enum action: %i(none disable silence suspend), _suffix: :action + + belongs_to :account, inverse_of: :account_warnings + belongs_to :target_account, class_name: 'Account', inverse_of: :targeted_account_warnings + + scope :latest, -> { order(created_at: :desc) } + scope :custom, -> { where.not(text: '') } +end diff --git a/app/models/account_warning_preset.rb b/app/models/account_warning_preset.rb new file mode 100644 index 000000000..ba8ceabb3 --- /dev/null +++ b/app/models/account_warning_preset.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: account_warning_presets +# +# id :bigint(8) not null, primary key +# text :text default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class AccountWarningPreset < ApplicationRecord + validates :text, presence: true +end diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb new file mode 100644 index 000000000..84c3f880d --- /dev/null +++ b/app/models/admin/account_action.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +class Admin::AccountAction + include ActiveModel::Model + include AccountableConcern + include Authorization + + TYPES = %w( + none + disable + silence + suspend + ).freeze + + attr_accessor :target_account, + :current_account, + :type, + :text, + :report_id, + :warning_preset_id, + :send_email_notification + + attr_reader :warning + + def save! + ApplicationRecord.transaction do + process_action! + process_warning! + end + + queue_email! + process_reports! + end + + def report + @report ||= Report.find(report_id) if report_id.present? + end + + def with_report? + !report.nil? + end + + class << self + def types_for_account(account) + if account.local? + TYPES + else + TYPES - %w(none disable) + end + end + end + + private + + def process_action! + case type + when 'disable' + handle_disable! + when 'silence' + handle_silence! + when 'suspend' + handle_suspend! + end + end + + def process_warning! + return unless warnable? + + authorize(target_account, :warn?) + + @warning = AccountWarning.create!(target_account: target_account, + account: current_account, + action: type, + text: text_for_warning) + + # 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? + end + + def process_reports! + return if report_id.blank? + + authorize(report, :update?) + + if type == 'none' + log_action(:resolve, report) + report.resolve!(current_account) + else + Report.where(target_account: target_account).unresolved.update_all(action_taken: true, action_taken_by_account_id: current_account.id) + end + end + + def handle_disable! + authorize(target_account.user, :disable?) + log_action(:disable, target_account.user) + target_account.user&.disable! + end + + def handle_silence! + authorize(target_account, :silence?) + log_action(:silence, target_account) + target_account.silence! + end + + def handle_suspend! + authorize(target_account, :suspend?) + log_action(:suspend, target_account) + target_account.suspend! + queue_suspension_worker! + end + + def text_for_warning + [warning_preset&.text, text].compact.join("\n\n") + end + + def queue_suspension_worker! + Admin::SuspensionWorker.perform_async(target_account.id) + end + + def queue_email! + return unless warnable? + + UserMailer.warning(target_account.user, warning).deliver_later! + end + + def warnable? + send_email_notification && target_account.local? + end + + def warning_preset + @warning_preset ||= AccountWarningPreset.find(warning_preset_id) if warning_preset_id.present? + end +end diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account_associations.rb index ae50860ed..a894b5eed 100644 --- a/app/models/concerns/account_associations.rb +++ b/app/models/concerns/account_associations.rb @@ -39,6 +39,8 @@ module AccountAssociations # Moderation notes has_many :account_moderation_notes, dependent: :destroy, inverse_of: :account has_many :targeted_moderation_notes, class_name: 'AccountModerationNote', foreign_key: :target_account_id, dependent: :destroy, inverse_of: :target_account + has_many :account_warnings, dependent: :destroy, inverse_of: :account + has_many :targeted_account_warnings, class_name: 'AccountWarning', foreign_key: :target_account_id, dependent: :destroy, inverse_of: :target_account # Lists (that the account is on, not owned by the account) has_many :list_accounts, inverse_of: :account, dependent: :destroy diff --git a/app/models/form/admin_suspension_confirmation.rb b/app/models/form/admin_suspension_confirmation.rb deleted file mode 100644 index c34b5b30e..000000000 --- a/app/models/form/admin_suspension_confirmation.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class Form::AdminSuspensionConfirmation - include ActiveModel::Model - - attr_accessor :acct, :report_id -end -- cgit