about summary refs log tree commit diff
path: root/app/models/report.rb
blob: c3a0c4c8b214ea7e2a90cc195fc64de6d82556cb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# frozen_string_literal: true

# == Schema Information
#
# Table name: reports
#
#  id                         :bigint(8)        not null, primary key
#  status_ids                 :bigint(8)        default([]), not null, is an Array
#  comment                    :text             default(""), not null
#  created_at                 :datetime         not null
#  updated_at                 :datetime         not null
#  account_id                 :bigint(8)        not null
#  action_taken_by_account_id :bigint(8)
#  target_account_id          :bigint(8)        not null
#  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

  rate_limit by: :account, family: :reports

  belongs_to :account
  belongs_to :target_account, class_name: 'Account'
  belongs_to :action_taken_by_account, class_name: 'Account', optional: true
  belongs_to :assigned_account, class_name: 'Account', optional: true

  has_many :notes, class_name: 'ReportNote', inverse_of: :report, dependent: :destroy
  has_many :notifications, as: :activity, dependent: :destroy

  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: 1_000 }
  validates :rule_ids, absence: true, unless: :violation?

  validate :validate_rule_ids

  enum category: {
    other: 0,
    spam: 1_000,
    violation: 2_000,
  }

  def local?
    false # Force uri_for to use uri attribute
  end

  before_validation :set_uri, only: :create

  after_create_commit :trigger_webhooks

  def object_type
    :flag
  end

  def statuses
    Status.with_discarded.where(id: status_ids)
  end

  def media_attachments_count
    statuses_to_query = []
    count = 0

    statuses.pluck(:id, :ordered_media_attachment_ids).each do |id, ordered_ids|
      if ordered_ids.nil?
        statuses_to_query << id
      else
        count += ordered_ids.size
      end
    end

    count += MediaAttachment.where(status_id: statuses_to_query).count unless statuses_to_query.empty?
    count
  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

  def unassign!
    update!(assigned_account_id: nil)
  end

  def resolve!(acting_account)
    update!(action_taken_at: Time.now.utc, action_taken_by_account_id: acting_account.id)
  end

  def unresolve!
    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

  def unresolved_siblings?
    Report.where.not(id: id).where(target_account_id: target_account_id).unresolved.exists?
  end

  def to_log_human_identifier
    id
  end

  def history
    subquery = [
      Admin::ActionLog.where(
        target_type: 'Report',
        target_id: id
      ).unscope(:order).arel,

      Admin::ActionLog.where(
        target_type: 'Account',
        target_id: target_account_id
      ).unscope(:order).arel,

      Admin::ActionLog.where(
        target_type: 'Status',
        target_id: status_ids
      ).unscope(:order).arel,
    ].reduce { |union, query| Arel::Nodes::UnionAll.new(union, query) }

    Admin::ActionLog.from(Arel::Nodes::As.new(subquery, Admin::ActionLog.arel_table))
  end

  private

  def set_uri
    self.uri = ActivityPub::TagManager.instance.generate_uri_for(self) if uri.nil? && account.local?
  end

  def validate_rule_ids
    return unless violation?

    errors.add(:rule_ids, I18n.t('reports.errors.invalid_rules')) unless rules.size == rule_ids&.size
  end

  def trigger_webhooks
    TriggerWebhookWorker.perform_async('report.created', 'Report', id)
  end
end