FEATURE: Penalty history improvements (#13359)
* FEATURE: add penalty history when silencing a user Display penalty history (last 6 months) when silencing/suspending a user * FEATURE: allow default penalty values to be chosen Adds a site setting that designates default penalty values in hours. Silence/suspend modals will auto-fill in the default values, but otherwise will still allow moderators to pick and overwrite values as normal. First silence/suspend: first value Second silence/suspend: second value etc. Penalty counts are forgiven at the same rate as tl3 promotion requirements do. Co-authored-by: jjaffeux <j.jaffeux@gmail.com>
This commit is contained in:
parent
729a9856f8
commit
d87a0216bb
|
@ -0,0 +1,22 @@
|
|||
import Component from "@ember/component";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["penalty-history"],
|
||||
|
||||
@discourseComputed("user.penalty_counts.suspended")
|
||||
suspendedCountClass(count) {
|
||||
if (count > 0) {
|
||||
return "danger";
|
||||
}
|
||||
return "";
|
||||
},
|
||||
|
||||
@discourseComputed("user.penalty_counts.silenced")
|
||||
silencedCountClass(count) {
|
||||
if (count > 0) {
|
||||
return "danger";
|
||||
}
|
||||
return "";
|
||||
},
|
||||
});
|
|
@ -12,6 +12,10 @@ export default Controller.extend(PenaltyController, {
|
|||
this.setProperties({ silenceUntil: null, silencing: false });
|
||||
},
|
||||
|
||||
finishedSetup() {
|
||||
this.set("silenceUntil", this.user?.next_penalty);
|
||||
},
|
||||
|
||||
@discourseComputed("silenceUntil", "reason", "silencing")
|
||||
submitDisabled(silenceUntil, reason, silencing) {
|
||||
return silencing || isEmpty(silenceUntil) || !reason || reason.length < 1;
|
||||
|
|
|
@ -12,6 +12,10 @@ export default Controller.extend(PenaltyController, {
|
|||
this.setProperties({ suspendUntil: null, suspending: false });
|
||||
},
|
||||
|
||||
finishedSetup() {
|
||||
this.set("suspendUntil", this.user?.next_penalty);
|
||||
},
|
||||
|
||||
@discourseComputed("suspendUntil", "reason", "suspending")
|
||||
submitDisabled(suspendUntil, reason, suspending) {
|
||||
return suspending || isEmpty(suspendUntil) || !reason || reason.length < 1;
|
||||
|
|
|
@ -48,7 +48,6 @@ export default Service.extend({
|
|||
|
||||
_showControlModal(type, user, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
let controller = showModal(`admin-${type}-user`, {
|
||||
admin: true,
|
||||
modalClass: `${type}-user-modal`,
|
||||
|
@ -65,6 +64,8 @@ export default Service.extend({
|
|||
before: opts.before,
|
||||
successCallback: opts.successCallback,
|
||||
});
|
||||
|
||||
controller.finishedSetup();
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<div class="suspended-count {{suspendedCountClass}}" title={{i18n "admin.user.last_six_months"}}>
|
||||
<label>{{i18n "admin.user.suspended_count"}}</label>
|
||||
<span>{{user.penalty_counts.suspended}}</span>
|
||||
</div>
|
||||
<div class="silenced-count {{silencedCountClass}}" title={{i18n "admin.user.last_six_months"}}>
|
||||
<label>{{i18n "admin.user.silenced_count"}}</label>
|
||||
<span>{{user.penalty_counts.silenced}}</span>
|
||||
</div>
|
|
@ -5,6 +5,8 @@
|
|||
<div class="alert alert-error">{{errorMessage}}</div>
|
||||
{{/if}}
|
||||
|
||||
{{admin-penalty-history user=user}}
|
||||
|
||||
<div class="until-controls">
|
||||
<label>
|
||||
{{future-date-input
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
{{/if}}
|
||||
|
||||
{{#if user.canSuspend}}
|
||||
{{admin-penalty-history user=user}}
|
||||
|
||||
<div class="until-controls">
|
||||
<label>
|
||||
{{future-date-input
|
||||
|
|
|
@ -14,9 +14,10 @@ export default Component.extend({
|
|||
displayLabel: null,
|
||||
labelClasses: null,
|
||||
|
||||
timeInputDisabled: empty("date"),
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
if (this.input) {
|
||||
const datetime = moment(this.input);
|
||||
this.setProperties({
|
||||
|
@ -27,8 +28,6 @@ export default Component.extend({
|
|||
}
|
||||
},
|
||||
|
||||
timeInputDisabled: empty("date"),
|
||||
|
||||
@observes("date", "time")
|
||||
_updateInput() {
|
||||
if (!this.date) {
|
||||
|
|
|
@ -35,4 +35,24 @@
|
|||
height: 10em;
|
||||
}
|
||||
}
|
||||
.penalty-history {
|
||||
margin-bottom: 1em;
|
||||
padding-bottom: 0.5em;
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
display: flex;
|
||||
& > * {
|
||||
flex-basis: 100%;
|
||||
text-align: center;
|
||||
padding: 1em 0;
|
||||
font-weight: 600;
|
||||
label {
|
||||
font-weight: 600;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.danger {
|
||||
background-color: var(--danger);
|
||||
color: var(--secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ class AdminDetailedUserSerializer < AdminUserSerializer
|
|||
:full_suspend_reason,
|
||||
:suspended_till,
|
||||
:silence_reason,
|
||||
:penalty_counts,
|
||||
:next_penalty,
|
||||
:primary_group_id,
|
||||
:badge_count,
|
||||
:warnings_received_count,
|
||||
|
@ -96,6 +98,20 @@ class AdminDetailedUserSerializer < AdminUserSerializer
|
|||
object.silence_reason
|
||||
end
|
||||
|
||||
def penalty_counts
|
||||
TrustLevel3Requirements.new(object).penalty_counts
|
||||
end
|
||||
|
||||
def next_penalty
|
||||
step_number = penalty_counts.total
|
||||
steps = SiteSetting.penalty_step_hours.split('|')
|
||||
step_number = [step_number, steps.length].min
|
||||
penalty_hours = steps[step_number]
|
||||
Integer(penalty_hours, 10).hours.from_now
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
def silenced_by
|
||||
object.silenced_record.try(:acting_user)
|
||||
end
|
||||
|
|
|
@ -4858,6 +4858,7 @@ en:
|
|||
penalty_post_edit: "Edit the post"
|
||||
penalty_post_none: "Do nothing"
|
||||
penalty_count: "Penalty Count"
|
||||
penalty_history: "Penalty History"
|
||||
clear_penalty_history:
|
||||
title: "Clear Penalty History"
|
||||
description: "users with penalties cannot reach TL3"
|
||||
|
@ -4997,6 +4998,9 @@ en:
|
|||
trust_level_unlocked_tip: "trust level is unlocked, system will may promote or demote user"
|
||||
lock_trust_level: "Lock Trust Level"
|
||||
unlock_trust_level: "Unlock Trust Level"
|
||||
silenced_count: "Silenced"
|
||||
suspended_count: "Suspended"
|
||||
last_six_months: "Last 6 months"
|
||||
tl3_requirements:
|
||||
title: "Requirements for Trust Level 3"
|
||||
table_title:
|
||||
|
|
|
@ -2160,6 +2160,7 @@ en:
|
|||
share_anonymized_statistics: "Share anonymized usage statistics."
|
||||
|
||||
auto_handle_queued_age: "Automatically handle records that are waiting to be reviewed after this many days. Flags will be ignored. Queued posts and users will be rejected. Set to 0 to disable this feature."
|
||||
penalty_step_hours: "Default penalties for silencing or suspending users in hours. First offense defaults to the first value, second offense defaults to the second value, etc."
|
||||
svg_icon_subset: "Add additional FontAwesome 5 icons that you would like to include in your assets. Use prefix 'fa-' for solid icons, 'far-' for regular icons and 'fab-' for brand icons."
|
||||
max_prints_per_hour_per_user: "Maximum number of /print page impressions (set to 0 to disable)"
|
||||
|
||||
|
|
|
@ -2270,6 +2270,11 @@ uncategorized:
|
|||
default: 60
|
||||
min: 0
|
||||
|
||||
penalty_step_hours:
|
||||
default: "24|72|168|720"
|
||||
type: "list"
|
||||
list_type: "compact"
|
||||
|
||||
svg_icon_subset:
|
||||
default: ""
|
||||
type: "list"
|
||||
|
|
Loading…
Reference in New Issue