diff options
author | Claire <claire.github-309c@sitedethib.com> | 2022-01-17 10:45:25 +0100 |
---|---|---|
committer | Claire <claire.github-309c@sitedethib.com> | 2022-01-17 10:45:25 +0100 |
commit | b3bf32a21e4dfc43737f50f15d3f258c16d0cf83 (patch) | |
tree | 37daa7d7725d3676115eea2c4d4a215bdd95c06d /app/models | |
parent | 430d4427916b44e6a7c16db1899dfef2eec140fc (diff) | |
parent | 14f436c457560862fafabd753eb314c8b8a8e674 (diff) |
Merge branch 'main' into glitch-soc/merge-upstream
Conflicts: - `app/views/admin/reports/show.html.haml`: Conflicts due to glitch-soc's theming system.
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/account_warning.rb | 22 | ||||
-rw-r--r-- | app/models/admin/account_action.rb | 28 | ||||
-rw-r--r-- | app/models/admin/status_batch_action.rb | 92 | ||||
-rw-r--r-- | app/models/admin/status_filter.rb | 41 | ||||
-rw-r--r-- | app/models/concerns/account_associations.rb | 2 | ||||
-rw-r--r-- | app/models/form/status_batch.rb | 45 | ||||
-rw-r--r-- | app/models/report.rb | 66 | ||||
-rw-r--r-- | app/models/report_filter.rb | 2 |
8 files changed, 201 insertions, 97 deletions
diff --git a/app/models/account_warning.rb b/app/models/account_warning.rb index 5efc924d5..fc0d988fd 100644 --- a/app/models/account_warning.rb +++ b/app/models/account_warning.rb @@ -10,14 +10,30 @@ # text :text default(""), not null # created_at :datetime not null # updated_at :datetime not null +# report_id :bigint(8) +# status_ids :string is an Array # class AccountWarning < ApplicationRecord - enum action: %i(none disable sensitive silence suspend), _suffix: :action + enum action: { + none: 0, + disable: 1_000, + delete_statuses: 1_500, + sensitive: 2_000, + silence: 3_000, + suspend: 4_000, + }, _suffix: :action belongs_to :account, inverse_of: :account_warnings - belongs_to :target_account, class_name: 'Account', inverse_of: :targeted_account_warnings + belongs_to :target_account, class_name: 'Account', inverse_of: :strikes + belongs_to :report, optional: true - scope :latest, -> { order(created_at: :desc) } + has_one :appeal, dependent: :destroy + + scope :latest, -> { order(id: :desc) } scope :custom, -> { where.not(text: '') } + + def statuses + Status.with_discarded.where(id: status_ids || []) + end end diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb index bf222391f..d3be4be3f 100644 --- a/app/models/admin/account_action.rb +++ b/app/models/admin/account_action.rb @@ -33,7 +33,7 @@ class Admin::AccountAction def save! ApplicationRecord.transaction do process_action! - process_warning! + process_strike! end process_email! @@ -74,20 +74,14 @@ class Admin::AccountAction 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? + def process_strike! + @warning = target_account.strikes.create!( + account: current_account, + report: report, + action: type, + text: text_for_warning, + status_ids: status_ids + ) end def process_reports! @@ -143,7 +137,7 @@ class Admin::AccountAction end def process_email! - UserMailer.warning(target_account.user, warning, status_ids).deliver_later! if warnable? + UserMailer.warning(target_account.user, warning).deliver_later! if warnable? end def warnable? @@ -151,7 +145,7 @@ class Admin::AccountAction end def status_ids - report.status_ids if report && include_statuses + report.status_ids if with_report? && include_statuses end def reports diff --git a/app/models/admin/status_batch_action.rb b/app/models/admin/status_batch_action.rb new file mode 100644 index 000000000..319deff98 --- /dev/null +++ b/app/models/admin/status_batch_action.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +class Admin::StatusBatchAction + include ActiveModel::Model + include AccountableConcern + include Authorization + + attr_accessor :current_account, :type, + :status_ids, :report_id + + def save! + process_action! + end + + private + + def statuses + Status.with_discarded.where(id: status_ids) + end + + def process_action! + return if status_ids.empty? + + case type + when 'delete' + handle_delete! + when 'report' + handle_report! + when 'remove_from_report' + handle_remove_from_report! + end + end + + def handle_delete! + statuses.each { |status| authorize(status, :destroy?) } + + ApplicationRecord.transaction do + statuses.each do |status| + status.discard + 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 + ) + + statuses.each { |status| Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true) } unless target_account.local? + end + + UserMailer.warning(target_account.user, @warning).deliver_later! if target_account.local? + RemovalWorker.push_bulk(status_ids) { |status_id| [status_id, preserve: target_account.local?, immediate: !target_account.local?] } + end + + def handle_report! + @report = Report.new(report_params) unless with_report? + @report.status_ids = (@report.status_ids + status_ids.map(&:to_i)).uniq + @report.save! + + @report_id = @report.id + end + + def handle_remove_from_report! + return unless with_report? + + report.status_ids -= status_ids.map(&:to_i) + report.save! + end + + def report + @report ||= Report.find(report_id) if report_id.present? + end + + def with_report? + !report.nil? + end + + def target_account + @target_account ||= statuses.first.account + end + + def report_params + { account: current_account, target_account: target_account } + end +end diff --git a/app/models/admin/status_filter.rb b/app/models/admin/status_filter.rb new file mode 100644 index 000000000..ce5bb5f46 --- /dev/null +++ b/app/models/admin/status_filter.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class Admin::StatusFilter + KEYS = %i( + media + id + report_id + ).freeze + + attr_reader :params + + def initialize(account, params) + @account = account + @params = params + end + + def results + scope = @account.statuses.where(visibility: [:public, :unlisted]) + + params.each do |key, value| + next if %w(page report_id).include?(key.to_s) + + scope.merge!(scope_for(key, value.to_s.strip)) if value.present? + end + + scope + end + + private + + def scope_for(key, value) + case key.to_s + when 'media' + Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id) + when 'id' + Status.where(id: value) + else + raise "Unknown filter: #{key}" + end + end +end diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account_associations.rb index f9e7a3bea..bbe269e8f 100644 --- a/app/models/concerns/account_associations.rb +++ b/app/models/concerns/account_associations.rb @@ -42,7 +42,7 @@ module AccountAssociations 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 + has_many :strikes, 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/status_batch.rb b/app/models/form/status_batch.rb deleted file mode 100644 index c4943a7ea..000000000 --- a/app/models/form/status_batch.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -class Form::StatusBatch - include ActiveModel::Model - include AccountableConcern - - attr_accessor :status_ids, :action, :current_account - - def save - case action - when 'nsfw_on', 'nsfw_off' - change_sensitive(action == 'nsfw_on') - when 'delete' - delete_statuses - end - end - - private - - def change_sensitive(sensitive) - media_attached_status_ids = MediaAttachment.where(status_id: status_ids).pluck(:status_id) - - ApplicationRecord.transaction do - Status.where(id: media_attached_status_ids).reorder(nil).find_each do |status| - status.update!(sensitive: sensitive) - log_action :update, status - end - end - - true - rescue ActiveRecord::RecordInvalid - false - end - - def delete_statuses - Status.where(id: status_ids).reorder(nil).find_each do |status| - status.discard - RemovalWorker.perform_async(status.id, immediate: true) - Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true) - log_action :destroy, status - end - - true - end -end diff --git a/app/models/report.rb b/app/models/report.rb index ef41547d9..ceb15133b 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -6,7 +6,6 @@ # id :bigint(8) not null, primary key # status_ids :bigint(8) default([]), not null, is an Array # comment :text default(""), not null -# action_taken :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null # account_id :bigint(8) not null @@ -15,9 +14,14 @@ # assigned_account_id :bigint(8) # uri :string # forwarded :boolean +# category :integer default("other"), not null +# action_taken_at :datetime +# rule_ids :bigint(8) is an Array # class Report < ApplicationRecord + self.ignored_columns = %w(action_taken) + include Paginable include RateLimitable @@ -30,11 +34,17 @@ class Report < ApplicationRecord has_many :notes, class_name: 'ReportNote', foreign_key: :report_id, inverse_of: :report, dependent: :destroy - scope :unresolved, -> { where(action_taken: false) } - scope :resolved, -> { where(action_taken: true) } + scope :unresolved, -> { where(action_taken_at: nil) } + scope :resolved, -> { where.not(action_taken_at: nil) } scope :with_accounts, -> { includes([:account, :target_account, :action_taken_by_account, :assigned_account].index_with({ user: [:invite_request, :invite] })) } - validates :comment, length: { maximum: 1000 } + validates :comment, length: { maximum: 1_000 } + + enum category: { + other: 0, + spam: 1_000, + violation: 2_000, + } def local? false # Force uri_for to use uri attribute @@ -47,13 +57,17 @@ class Report < ApplicationRecord end def statuses - Status.with_discarded.where(id: status_ids).includes(:account, :media_attachments, :mentions) + Status.with_discarded.where(id: status_ids) end def media_attachments MediaAttachment.where(status_id: status_ids) end + def rules + Rule.with_discarded.where(id: rule_ids) + end + def assign_to_self!(current_account) update!(assigned_account_id: current_account.id) end @@ -63,22 +77,19 @@ class Report < ApplicationRecord end def resolve!(acting_account) - if account_id == -99 && target_account.trust_level == Account::TRUST_LEVELS[:untrusted] - # This is an automated report and it is being dismissed, so it's - # a false positive, in which case update the account's trust level - # to prevent further spam checks - - target_account.update(trust_level: Account::TRUST_LEVELS[:trusted]) - end - - RemovalWorker.push_bulk(Status.with_discarded.discarded.where(id: status_ids).pluck(:id)) { |status_id| [status_id, { immediate: true }] } - update!(action_taken: true, action_taken_by_account_id: acting_account.id) + update!(action_taken_at: Time.now.utc, action_taken_by_account_id: acting_account.id) end def unresolve! - update!(action_taken: false, action_taken_by_account_id: nil) + update!(action_taken_at: nil, action_taken_by_account_id: nil) + end + + def action_taken? + action_taken_at.present? end + alias action_taken action_taken? + def unresolved? !action_taken? end @@ -88,29 +99,24 @@ class Report < ApplicationRecord end def history - time_range = created_at..updated_at - - sql = [ + subquery = [ Admin::ActionLog.where( target_type: 'Report', - target_id: id, - created_at: time_range - ).unscope(:order), + target_id: id + ).unscope(:order).arel, Admin::ActionLog.where( target_type: 'Account', - target_id: target_account_id, - created_at: time_range - ).unscope(:order), + target_id: target_account_id + ).unscope(:order).arel, Admin::ActionLog.where( target_type: 'Status', - target_id: status_ids, - created_at: time_range - ).unscope(:order), - ].map { |query| "(#{query.to_sql})" }.join(' UNION ALL ') + target_id: status_ids + ).unscope(:order).arel, + ].reduce { |union, query| Arel::Nodes::UnionAll.new(union, query) } - Admin::ActionLog.from("(#{sql}) AS admin_action_logs") + Admin::ActionLog.from(Arel::Nodes::As.new(subquery, Admin::ActionLog.arel_table)) end def set_uri diff --git a/app/models/report_filter.rb b/app/models/report_filter.rb index a91a6baeb..dc444a552 100644 --- a/app/models/report_filter.rb +++ b/app/models/report_filter.rb @@ -19,7 +19,7 @@ class ReportFilter scope = Report.unresolved params.each do |key, value| - scope = scope.merge scope_for(key, value) + scope = scope.merge scope_for(key, value), rewhere: true end scope |