mirror of
https://github.com/discourse/discourse.git
synced 2026-08-06 09:05:23 +08:00
Previously, suspect-user reviewables ("user needs approval" flags) and
queued-post reviewables could only be resolved by deleting the user or
rejecting the post outright — a reviewer mistake on an automated false
positive was irreversible, and sites with no-deletion policies had to
resolve the flag and then manually suspend the user from their admin
page. Requested in [meta t/408891](https://meta.discourse.org/t/408891)
and [meta t/225660](https://meta.discourse.org/t/225660).
This change adds guardian-gated **Silence user** / **Suspend user**
resolutions to both queues via a shared `build_penalty_actions` helper,
using the same penalize-modal flow the flagged-post and
review-every-post queues already use (staff log linked back to the
reviewable). Along the way it:
- hides penalty actions that are already active everywhere (previously a
resolve-then-409 dead end), and fixes the penalize modal's unawaited
`before()` race so the penalty is only applied once the reviewable
action succeeded;
- narrows the rejected-user **scrub** affordance to records whose
identity snapshot is the last remaining copy (user deleted, or
renamed/anonymized after a failed deletion), admin-gated to match the
endpoint;
- shows an active penalty (localized end date and reason) on the
reviewable user card, so it's clear why a penalty option is absent;
- preloads `anonymous_user_master` on the queue since the new
`silenced?` gates would otherwise lazy-load it per row;
- rewrites the action descriptions in one consistent voice.
<img width="460" height="313" alt="2026-07-31 @ 07 46 57"
src="https://github.com/user-attachments/assets/87d5bf49-97e0-47a4-939e-16da3f55c6e1"
/>
94 lines
1.8 KiB
Ruby
Vendored
94 lines
1.8 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
class FlaggedUserSerializer < BasicUserSerializer
|
|
attributes :can_delete_all_posts,
|
|
:can_be_deleted,
|
|
:post_count,
|
|
:topic_count,
|
|
:ip_address,
|
|
:email,
|
|
:custom_fields,
|
|
:flags_agreed,
|
|
:flags_disagreed,
|
|
:flags_ignored,
|
|
:created_at,
|
|
:custom_fields,
|
|
:post_count,
|
|
:trust_level,
|
|
:silenced_count,
|
|
:suspended_count,
|
|
:rejected_posts_count,
|
|
:silenced_till,
|
|
:silence_reason,
|
|
:suspended_till,
|
|
:suspend_reason
|
|
|
|
def can_delete_all_posts
|
|
scope.can_delete_all_posts?(object)
|
|
end
|
|
|
|
def can_be_deleted
|
|
scope.can_delete_user?(object)
|
|
end
|
|
|
|
def ip_address
|
|
object.ip_address.try(:to_s)
|
|
end
|
|
|
|
def include_ip_address?
|
|
scope.can_see_ip?
|
|
end
|
|
|
|
def flags_agreed
|
|
object.user_stat.flags_agreed
|
|
end
|
|
|
|
def flags_disagreed
|
|
object.user_stat.flags_disagreed
|
|
end
|
|
|
|
def flags_ignored
|
|
object.user_stat.flags_ignored
|
|
end
|
|
|
|
def silenced_count
|
|
object.number_of_silencings
|
|
end
|
|
|
|
def suspended_count
|
|
object.number_of_suspensions
|
|
end
|
|
|
|
def rejected_posts_count
|
|
object.number_of_rejected_posts
|
|
end
|
|
|
|
def custom_fields
|
|
fields = User.allowed_user_custom_fields(scope)
|
|
|
|
result = {}
|
|
fields.each { |k| result[k] = object.custom_fields[k] if object.custom_fields[k].present? }
|
|
|
|
result
|
|
end
|
|
|
|
def include_email?
|
|
scope.can_check_emails?(scope.user)
|
|
end
|
|
|
|
def include_silenced_till?
|
|
object.silenced?
|
|
end
|
|
|
|
def include_silence_reason?
|
|
object.silenced? && object.silence_reason.present?
|
|
end
|
|
|
|
def include_suspended_till?
|
|
object.suspended?
|
|
end
|
|
|
|
def include_suspend_reason?
|
|
object.suspended? && object.suspend_reason.present?
|
|
end
|
|
end
|