FEATURE: Admins can flag posts so they can review them later. (#12311)

Staff can send a post to the review queue by clicking the "Flag Post" button next to "Take Action...". Clicking it flags the post using the "Notify moderators" score type and hides it. A custom message will be sent to the user.
This commit is contained in:
Roman Rizzi 2021-03-11 08:21:24 -03:00 committed by GitHub
parent 0902e56162
commit 8fcad73b36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 110 additions and 6 deletions

View File

@ -12,7 +12,10 @@ export default Component.extend({
return; return;
} }
this.element.querySelector("#radio_" + nameKey).checked = "true"; const selector = this.element.querySelector("#radio_" + nameKey);
if (selector) {
selector.checked = "true";
}
}, },
@observes("nameKey") @observes("nameKey")

View File

@ -266,6 +266,17 @@ export default Controller.extend(ModalFunctionality, {
this.set("model.hidden", true); this.set("model.hidden", true);
}, },
flagForReview() {
const notifyModeratorsID = 7;
const notifyModerators = this.flagsAvailable.find(
(f) => f.id === notifyModeratorsID
);
this.set("selected", notifyModerators);
this.send("createFlag", { queue_for_review: true });
this.set("model.hidden", true);
},
changePostActionType(action) { changePostActionType(action) {
this.set("selected", action); this.set("selected", action);
}, },

View File

@ -53,6 +53,7 @@ export default RestModel.extend({
message: opts.message, message: opts.message,
is_warning: opts.isWarning, is_warning: opts.isWarning,
take_action: opts.takeAction, take_action: opts.takeAction,
queue_for_review: opts.queue_for_review,
flag_topic: this.flagTopic ? true : false, flag_topic: this.flagTopic ? true : false,
}, },
returnXHR: true, returnXHR: true,

View File

@ -40,6 +40,13 @@
performAction=(action "takeAction") performAction=(action "takeAction")
reviewableUpdating=submitDisabled reviewableUpdating=submitDisabled
}} }}
{{d-button
class="btn-danger"
action=(action "flagForReview")
icon="exclamation-triangle"
label="flagging.flag_for_review"
}}
{{/if}} {{/if}}
{{#if showDeleteSpammer}} {{#if showDeleteSpammer}}

View File

@ -16,7 +16,8 @@ class PostActionsController < ApplicationController
is_warning: params[:is_warning], is_warning: params[:is_warning],
message: params[:message], message: params[:message],
take_action: params[:take_action] == 'true', take_action: params[:take_action] == 'true',
flag_topic: params[:flag_topic] == 'true' flag_topic: params[:flag_topic] == 'true',
queue_for_review: params[:queue_for_review] == 'true'
) )
result = creator.perform result = creator.perform

View File

@ -522,7 +522,7 @@ class Post < ActiveRecord::Base
(topic.present? && (topic.private_message? || topic.category&.read_restricted)) (topic.present? && (topic.private_message? || topic.category&.read_restricted))
end end
def hide!(post_action_type_id, reason = nil) def hide!(post_action_type_id, reason = nil, custom_message: nil)
return if hidden? return if hidden?
reason ||= hidden_at ? reason ||= hidden_at ?
@ -554,11 +554,16 @@ class Post < ActiveRecord::Base
) )
} }
message = custom_message
if message.nil?
message = hiding_again ? :post_hidden_again : :post_hidden
end
Jobs.enqueue_in( Jobs.enqueue_in(
5.seconds, 5.seconds,
:send_system_message, :send_system_message,
user_id: user.id, user_id: user.id,
message_type: hiding_again ? :post_hidden_again : :post_hidden, message_type: message,
message_options: options message_options: options
) )
end end

View File

@ -3246,6 +3246,7 @@ en:
notify_action: "Message" notify_action: "Message"
official_warning: "Official Warning" official_warning: "Official Warning"
delete_spammer: "Delete Spammer" delete_spammer: "Delete Spammer"
flag_for_review: "Flag Post"
# keys ending with _MF use message format, see https://meta.discourse.org/t/message-format-support-for-localization/7035 for details # keys ending with _MF use message format, see https://meta.discourse.org/t/message-format-support-for-localization/7035 for details
delete_confirm_MF: "You are about to delete {POSTS, plural, one {<b>#</b> post} other {<b>#</b> posts}} and {TOPICS, plural, one {<b>#</b> topic} other {<b>#</b> topics}} from this user, remove their account, block signups from their IP address <b>{ip_address}</b>, and add their email address <b>{email}</b> to a permanent block list. Are you sure this user is really a spammer?" delete_confirm_MF: "You are about to delete {POSTS, plural, one {<b>#</b> post} other {<b>#</b> posts}} and {TOPICS, plural, one {<b>#</b> topic} other {<b>#</b> topics}} from this user, remove their account, block signups from their IP address <b>{ip_address}</b>, and add their email address <b>{email}</b> to a permanent block list. Are you sure this user is really a spammer?"

View File

@ -2820,6 +2820,20 @@ en:
The community flagged this post and now it is hidden. **Because this post has been hidden more than once, your post will now remain hidden until it is handled by a staff member.** The community flagged this post and now it is hidden. **Because this post has been hidden more than once, your post will now remain hidden until it is handled by a staff member.**
For additional guidance, please refer to our [community guidelines](%{base_url}/guidelines). For additional guidance, please refer to our [community guidelines](%{base_url}/guidelines).
queued_by_staff:
title: "Post Needs Approval"
subject_template: "Post hidden by staff, awaiting approval"
text_body_template: |
Hello,
This is an automated message from %{site_name} to let you know that your post was hidden.
<%{base_url}%{url}>
Your post will remain hidden until a staff member reviews it.
For additional guidance, please refer to our [community guidelines](%{base_url}/guidelines).
flags_disagreed: flags_disagreed:
title: "Flagged post restored by staff" title: "Flagged post restored by staff"
@ -4907,6 +4921,7 @@ en:
email_spam: "This email was flagged as spam by the header defined in `email_in_spam_header`." email_spam: "This email was flagged as spam by the header defined in `email_in_spam_header`."
suspect_user: "This new user entered profile information without reading any topics or posts, which strongly suggests they may be a spammer. See `approve_suspect_users`." suspect_user: "This new user entered profile information without reading any topics or posts, which strongly suggests they may be a spammer. See `approve_suspect_users`."
contains_media: "This post includes embedded media. See `review_media_unless_trust_level`." contains_media: "This post includes embedded media. See `review_media_unless_trust_level`."
queued_by_staff: "A staff member thinks this post needs review. It'll remain hidden until then."
actions: actions:
agree: agree:

View File

@ -39,6 +39,7 @@ class PostActionCreator
take_action: false, take_action: false,
flag_topic: false, flag_topic: false,
created_at: nil, created_at: nil,
queue_for_review: false,
reason: nil reason: nil
) )
@created_by = created_by @created_by = created_by
@ -54,7 +55,13 @@ class PostActionCreator
@message = message @message = message
@flag_topic = flag_topic @flag_topic = flag_topic
@meta_post = nil @meta_post = nil
@reason = reason @reason = reason
@queue_for_review = queue_for_review
if reason.nil? && @queue_for_review
@reason = 'queued_by_staff'
end
end end
def post_can_act? def post_can_act?
@ -71,7 +78,7 @@ class PostActionCreator
def perform def perform
result = CreateResult.new result = CreateResult.new
unless post_can_act? if !post_can_act? || (@queue_for_review && !guardian.is_staff?)
result.forbidden = true result.forbidden = true
result.add_error(I18n.t("invalid_access")) result.add_error(I18n.t("invalid_access"))
return result return result
@ -186,7 +193,20 @@ private
def auto_hide_if_needed def auto_hide_if_needed
return if @post.hidden? return if @post.hidden?
return if !@created_by.staff? && @post.user&.staff? return if !@created_by.staff? && @post.user&.staff?
return unless PostActionType.auto_action_flag_types.include?(@post_action_name)
not_auto_action_flag_type = !PostActionType.auto_action_flag_types.include?(@post_action_name)
return if not_auto_action_flag_type && !@queue_for_review
if @queue_for_review
@post.topic.update_status('visible', false, @created_by) if @post.is_first_post?
@post.hide!(
@post_action_type_id,
Post.hidden_reasons[:flag_threshold_reached],
custom_message: :queued_by_staff
)
return
end
if trusted_spam_flagger? if trusted_spam_flagger?
@post.hide!(@post_action_type_id, Post.hidden_reasons[:flagged_by_tl3_user]) @post.hide!(@post_action_type_id, Post.hidden_reasons[:flagged_by_tl3_user])
@ -312,6 +332,7 @@ private
targets_topic: @targets_topic targets_topic: @targets_topic
} }
) )
result.reviewable_score = result.reviewable.add_score( result.reviewable_score = result.reviewable.add_score(
@created_by, @created_by,
@post_action_type_id, @post_action_type_id,

View File

@ -219,4 +219,43 @@ describe PostActionCreator do
expect(reviewable.reload).to be_approved expect(reviewable.reload).to be_approved
end end
end end
context "queue_for_review" do
fab!(:admin) { Fabricate(:admin) }
it 'fails if the user is not a staff member' do
creator = PostActionCreator.new(
user, post,
PostActionType.types[:notify_moderators], queue_for_review: true
)
result = creator.perform
expect(result.success?).to eq(false)
end
it 'creates a new reviewable and hides the post' do
result = build_creator.perform
expect(result.success?).to eq(true)
score = result.reviewable.reviewable_scores.last
expect(score.reason).to eq('queued_by_staff')
expect(post.reload.hidden?).to eq(true)
end
it 'hides the topic even if it has replies' do
Fabricate(:post, topic: post.topic)
result = build_creator.perform
expect(post.topic.reload.visible?).to eq(false)
end
def build_creator
PostActionCreator.new(
admin, post,
PostActionType.types[:notify_moderators], queue_for_review: true
)
end
end
end end