FIX: Show error message if user is already silenced or suspended (#10988)

Users could be silenced or suspended by two staff members at the same time and
would not be aware of it. This commit shows an error message if another penalty
has been applied.
This commit is contained in:
Bianca Nenciu 2020-11-03 17:38:56 +02:00 committed by GitHub
parent d384e744a8
commit d2116f0029
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 78 additions and 14 deletions

View File

@ -1,12 +1,13 @@
import I18n from "I18n"; import I18n from "I18n";
import ModalFunctionality from "discourse/mixins/modal-functionality"; import ModalFunctionality from "discourse/mixins/modal-functionality";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { extractError } from "discourse/lib/ajax-error";
import Mixin from "@ember/object/mixin"; import Mixin from "@ember/object/mixin";
import { next } from "@ember/runloop"; import { next } from "@ember/runloop";
import { Promise } from "rsvp"; import { Promise } from "rsvp";
import bootbox from "bootbox"; import bootbox from "bootbox";
export default Mixin.create(ModalFunctionality, { export default Mixin.create(ModalFunctionality, {
errorMessage: null,
reason: null, reason: null,
message: null, message: null,
postEdit: null, postEdit: null,
@ -18,6 +19,7 @@ export default Mixin.create(ModalFunctionality, {
resetModal() { resetModal() {
this.setProperties({ this.setProperties({
errorMessage: null,
reason: null, reason: null,
message: null, message: null,
loadingUser: true, loadingUser: true,
@ -66,6 +68,8 @@ export default Mixin.create(ModalFunctionality, {
callback(result); callback(result);
} }
}) })
.catch(popupAjaxError); .catch((error) => {
this.set("errorMessage", extractError(error));
});
}, },
}); });

View File

@ -352,28 +352,17 @@ const AdminUser = User.extend({
type: "PUT", type: "PUT",
}) })
.then((result) => this.setProperties(result.unsilence)) .then((result) => this.setProperties(result.unsilence))
.catch((e) => {
const error = I18n.t("admin.user.unsilence_failed", {
error: this._formatError(e),
});
bootbox.alert(error);
})
.finally(() => this.set("silencingUser", false)); .finally(() => this.set("silencingUser", false));
}, },
silence(data) { silence(data) {
this.set("silencingUser", true); this.set("silencingUser", true);
return ajax(`/admin/users/${this.id}/silence`, { return ajax(`/admin/users/${this.id}/silence`, {
type: "PUT", type: "PUT",
data, data,
}) })
.then((result) => this.setProperties(result.silence)) .then((result) => this.setProperties(result.silence))
.catch((e) => {
const error = I18n.t("admin.user.silence_failed", {
error: this._formatError(e),
});
bootbox.alert(error);
})
.finally(() => this.set("silencingUser", false)); .finally(() => this.set("silencingUser", false));
}, },

View File

@ -1,6 +1,10 @@
{{#d-modal-body title="admin.user.silence_modal_title"}} {{#d-modal-body title="admin.user.silence_modal_title"}}
{{#conditional-loading-spinner condition=loadingUser}} {{#conditional-loading-spinner condition=loadingUser}}
{{#if errorMessage}}
<div class="alert alert-error">{{errorMessage}}</div>
{{/if}}
<div class="until-controls"> <div class="until-controls">
<label> <label>
{{future-date-input {{future-date-input

View File

@ -1,6 +1,10 @@
{{#d-modal-body title="admin.user.suspend_modal_title"}} {{#d-modal-body title="admin.user.suspend_modal_title"}}
{{#conditional-loading-spinner condition=loadingUser}} {{#conditional-loading-spinner condition=loadingUser}}
{{#if errorMessage}}
<div class="alert alert-error">{{errorMessage}}</div>
{{/if}}
{{#if user.canSuspend}} {{#if user.canSuspend}}
<div class="until-controls"> <div class="until-controls">
<label> <label>

View File

@ -93,6 +93,16 @@ class Admin::UsersController < Admin::AdminController
def suspend def suspend
guardian.ensure_can_suspend!(@user) guardian.ensure_can_suspend!(@user)
if @user.suspended?
suspend_record = @user.suspend_record
message = I18n.t("user.already_suspended",
staff: suspend_record.acting_user.username,
time_ago: FreedomPatches::Rails4.time_ago_in_words(suspend_record.created_at, true, scope: :'datetime.distance_in_words_verbose')
)
return render json: failed_json.merge(message: message), status: 409
end
params.require([:suspend_until, :reason]) params.require([:suspend_until, :reason])
@user.suspended_till = params[:suspend_until] @user.suspended_till = params[:suspend_until]
@ -315,6 +325,15 @@ class Admin::UsersController < Admin::AdminController
def silence def silence
guardian.ensure_can_silence_user! @user guardian.ensure_can_silence_user! @user
if @user.silenced?
silenced_record = @user.silenced_record
message = I18n.t("user.already_silenced",
staff: silenced_record.acting_user.username,
time_ago: FreedomPatches::Rails4.time_ago_in_words(silenced_record.created_at, true, scope: :'datetime.distance_in_words_verbose')
)
return render json: failed_json.merge(message: message), status: 409
end
message = params[:message] message = params[:message]
silencer = UserSilencer.new( silencer = UserSilencer.new(

View File

@ -2507,6 +2507,8 @@ en:
same_ip_address: "Same IP address (%{ip_address}) as other users" same_ip_address: "Same IP address (%{ip_address}) as other users"
inactive_user: "Inactive user" inactive_user: "Inactive user"
email_in_spam_header: "User's first email was flagged as spam" email_in_spam_header: "User's first email was flagged as spam"
already_silenced: "User was already silenced by %{staff} %{time_ago}."
already_suspended: "User was already suspended by %{staff} %{time_ago}."
reviewables_reminder: reviewables_reminder:
submitted: submitted:

View File

@ -149,6 +149,27 @@ RSpec.describe Admin::UsersController do
expect(log.details).to match(/because I said so/) expect(log.details).to match(/because I said so/)
end end
it "checks if user is suspended" do
put "/admin/users/#{user.id}/suspend.json", params: {
suspend_until: 5.hours.from_now,
reason: "because I said so"
}
put "/admin/users/#{user.id}/suspend.json", params: {
suspend_until: 5.hours.from_now,
reason: "because I said so too"
}
expect(response.status).to eq(409)
expect(response.parsed_body["message"]).to eq(
I18n.t(
"user.already_suspended",
staff: admin.username,
time_ago: FreedomPatches::Rails4.time_ago_in_words(user.suspend_record.created_at, true, scope: :'datetime.distance_in_words_verbose')
)
)
end
it "requires suspend_until and reason" do it "requires suspend_until and reason" do
expect(user).not_to be_suspended expect(user).not_to be_suspended
put "/admin/users/#{user.id}/suspend.json", params: {} put "/admin/users/#{user.id}/suspend.json", params: {}
@ -749,6 +770,27 @@ RSpec.describe Admin::UsersController do
reg_user.reload reg_user.reload
expect(reg_user).to be_silenced expect(reg_user).to be_silenced
end end
it "checks if user is silenced" do
put "/admin/users/#{user.id}/silence.json", params: {
silenced_till: 5.hours.from_now,
reason: "because I said so"
}
put "/admin/users/#{user.id}/silence.json", params: {
silenced_till: 5.hours.from_now,
reason: "because I said so too"
}
expect(response.status).to eq(409)
expect(response.parsed_body["message"]).to eq(
I18n.t(
"user.already_silenced",
staff: admin.username,
time_ago: FreedomPatches::Rails4.time_ago_in_words(user.silenced_record.created_at, true, scope: :'datetime.distance_in_words_verbose')
)
)
end
end end
describe '#unsilence' do describe '#unsilence' do