Class: Moderate::Appeal

Inherits:
ApplicationRecord show all
Defined in:
lib/moderate/models/appeal.rb

Overview

A DSA Art. 20 internal complaint against a moderation decision.

The Digital Services Act requires platforms to give affected users an “effective internal complaint-handling system” that is FREE, ELECTRONIC, open for AT LEAST SIX MONTHS after the decision, and decided by a HUMAN (not “solely on the basis of automated means”). This model encodes each of those:

- free + electronic: it's just a record + a queue (no payment path exists);
- six-month window: an appeal is refused once the report's appeal window has
  closed (`report_must_still_be_appealable`);
- human-decided: there is no auto-decide path — uphold!/reject! (in a service)
  require a moderator and a note;
- against a DECISION: an appeal can only be opened on an already-resolved
  report (`report_must_be_closed`).

Source: eur-lex.europa.eu/eli/reg/2022/2065/oj (Art. 20)

Constant Summary collapse

STATUSES =

Validated by the ‘validates :status, inclusion` below — NOT a DB constraint, so the vocabulary can grow without a host migration. `upheld` overturns the original decision; `rejected` confirms it.

%w[open upheld rejected].freeze
SOURCES =

Who lodged the complaint. ‘notifier` = the person who filed the original notice; `affected_user` = the content owner whose content was actioned; the rest are operational. Validated by the model (inclusion), not a DB constraint.

%w[notifier affected_user admin other].freeze

Instance Method Summary collapse

Instance Method Details

#closed?Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/moderate/models/appeal.rb', line 72

def closed?
  status.in?(%w[upheld rejected])
end

#open?Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/moderate/models/appeal.rb', line 68

def open?
  status == "open"
end

#reject!(by:, note:) ⇒ Object



93
94
95
# File 'lib/moderate/models/appeal.rb', line 93

def reject!(by:, note:)
  Moderate::Services::ResolveAppeal.new(self, by: by).reject!(note: note)
end

#rejected?Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/moderate/models/appeal.rb', line 80

def rejected?
  status == "rejected"
end

#upheld?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'lib/moderate/models/appeal.rb', line 76

def upheld?
  status == "upheld"
end

#uphold!(by:, note:) ⇒ Object

Decide this appeal — model-level sugar over Moderate::Services::ResolveAppeal, so a host can write ‘appeal.uphold!(by: moderator, note: “…”)` / `appeal.reject!(by: moderator, note: “…”)` instead of constructing the service. The service does the real work (audit, the appeal-decision notification, and —for an upheld appeal — reversing the original decision); these only forward.



89
90
91
# File 'lib/moderate/models/appeal.rb', line 89

def uphold!(by:, note:)
  Moderate::Services::ResolveAppeal.new(self, by: by).uphold!(note: note)
end