mirror of
https://github.com/discourse/discourse.git
synced 2026-08-08 14:34:02 +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"
/>
51 lines
1.2 KiB
Ruby
Vendored
51 lines
1.2 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
module PageObjects
|
|
module Modals
|
|
class PenalizeUser < PageObjects::Modals::Base
|
|
def initialize(penalty_type)
|
|
@penalty_type = penalty_type
|
|
end
|
|
|
|
def similar_users
|
|
modal.all("table tbody tr td:nth-child(2)").map(&:text)
|
|
end
|
|
|
|
def select_similar_user(username)
|
|
modal.find("table tbody tr", text: username).check
|
|
end
|
|
|
|
def modal
|
|
find(".d-modal.#{@penalty_type}-user-modal")
|
|
end
|
|
|
|
def suspend(reason, date: "tomorrow")
|
|
fill_in_suspend_reason(reason)
|
|
set_future_date(date)
|
|
perform
|
|
end
|
|
|
|
def fill_in_suspend_reason(reason)
|
|
find("input.suspend-reason").fill_in with: reason
|
|
end
|
|
|
|
def fill_in_silence_reason(reason)
|
|
find("input.silence-reason").fill_in with: reason
|
|
end
|
|
|
|
def set_future_date(date)
|
|
select = PageObjects::Components::SelectKit.new(".future-date-input details")
|
|
select.expand
|
|
select.select_row_by_value(date)
|
|
end
|
|
|
|
def perform
|
|
find(".perform-penalize").click
|
|
end
|
|
|
|
def has_error_message?(message)
|
|
expect(find("#modal-alert").text).to eq(message)
|
|
end
|
|
end
|
|
end
|
|
end
|