Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: Add Revise... option for queued post reviewable #23454

Merged
merged 6 commits into from Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,67 @@
<DModal
class="revise-and-reject-reviewable"
@closeModal={{@closeModal}}
@title={{i18n "review.revise_and_reject_post.title"}}
>
<:body>
<div class="revise-and-reject-reviewable__queued-post">
<ReviewableQueuedPost @reviewable={{@model.reviewable}} @tagName="" />
</div>

<div class="control-group">
<label class="control-label" for="reason">{{i18n
"review.revise_and_reject_post.reason"
}}</label>
<ComboBox
@name="reason"
@content={{this.configuredReasons}}
@value={{this.reason}}
@onChange={{action (mut this.reason)}}
@class="revise-and-reject-reviewable__reason"
/>
</div>

{{#if this.showCustomReason}}
<div class="control-group">
<label class="control-label" for="custom_reason">{{i18n
"review.revise_and_reject_post.custom_reason"
}}</label>
<Input
name="custom_reason"
class="revise-and-reject-reviewable__custom-reason"
@type="text"
@value={{this.customReason}}
/>
</div>
{{/if}}

<div class="control-group">
<label class="control-label" for="feedback">{{i18n
"review.revise_and_reject_post.feedback"
}}
<span class="revise-and-reject-reviewable__optional">({{i18n
"review.revise_and_reject_post.optional"
}})</span>
</label>
<DTextarea
@name="feedback"
@value={{this.feedback}}
@onChange={{action (mut this.feedback)}}
@class="revise-and-reject-reviewable__feedback"
/>
</div>
</:body>
<:footer>
<DButton
class="btn-primary"
@action={{this.rejectAndSendPM}}
@disabled={{this.sendPMDisabled}}
@label="review.revise_and_reject_post.send_pm"
/>
<DButton
class="btn-flat d-modal-cancel"
@action={{@closeModal}}
@label="cancel"
/>
</:footer>
</DModal>
@@ -0,0 +1,62 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { isEmpty } from "@ember/utils";
import { popupAjaxError } from "discourse/lib/ajax-error";
import I18n from "I18n";

const OTHER_REASON = "other_reason";

export default class ReviseAndRejectPostReviewable extends Component {
@service siteSettings;

@tracked reason;
@tracked customReason;
@tracked feedback;
@tracked submitting = false;

get configuredReasons() {
const reasons = this.siteSettings.reviewable_revision_reasons
.split("|")
.filter(Boolean)
.map((reason) => ({ id: reason, name: reason }))
.concat([
{
id: OTHER_REASON,
name: I18n.t("review.revise_and_reject_post.other_reason"),
},
]);
return reasons;
}

get showCustomReason() {
return this.reason === OTHER_REASON;
}

get sendPMDisabled() {
return (
isEmpty(this.reason) ||
(this.reason === OTHER_REASON && isEmpty(this.customReason)) ||
this.submitting
);
}

@action
async rejectAndSendPM() {
this.submitting = true;

try {
await this.args.model.performConfirmed(this.args.model.action, {
revise_reason: this.reason,
revise_custom_reason: this.customReason,
revise_feedback: this.feedback,
});
this.args.closeModal();
} catch (error) {
popupAjaxError(error);
} finally {
this.submitting = false;
}
}
}
17 changes: 15 additions & 2 deletions app/assets/javascripts/discourse/app/components/reviewable-item.js
Expand Up @@ -4,6 +4,7 @@ import { action, set } from "@ember/object";
import { inject as service } from "@ember/service";
import { classify, dasherize } from "@ember/string";
import ExplainReviewableModal from "discourse/components/modal/explain-reviewable";
import ReviseAndRejectPostReviewable from "discourse/components/modal/revise-and-reject-post-reviewable";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import optionalService from "discourse/lib/optional-service";
Expand All @@ -15,7 +16,13 @@ import I18n from "I18n";
let _components = {};

const pluginReviewableParams = {};
const actionModalClassMap = {};

// The mappings defined here are default core mappings, and cannot be overridden
// by plugins.
const defaultActionModalClassMap = {
revise_and_reject_post: ReviseAndRejectPostReviewable,
};
const actionModalClassMap = { ...defaultActionModalClassMap };

export function addPluginReviewableParam(reviewableType, param) {
pluginReviewableParams[reviewableType]
Expand All @@ -24,6 +31,11 @@ export function addPluginReviewableParam(reviewableType, param) {
}

export function registerReviewableActionModal(actionName, modalClass) {
if (Object.keys(defaultActionModalClassMap).includes(actionName)) {
throw new Error(
`Cannot override default action modal class for ${actionName} (mapped to ${defaultActionModalClassMap[actionName].name})!`
);
}
actionModalClassMap[actionName] = modalClass;
}

Expand Down Expand Up @@ -135,7 +147,7 @@ export default Component.extend({
},

@bind
_performConfirmed(performableAction) {
_performConfirmed(performableAction, additionalData = {}) {
let reviewable = this.reviewable;

let performAction = () => {
Expand All @@ -145,6 +157,7 @@ export default Component.extend({
const data = {
send_email: reviewable.sendEmail,
reject_reason: reviewable.rejectReason,
...additionalData,
};

(pluginReviewableParams[reviewable.type] || []).forEach((param) => {
Expand Down
1 change: 1 addition & 0 deletions app/assets/stylesheets/common/base/_index.scss
Expand Up @@ -41,6 +41,7 @@
@import "request_access";
@import "request-group-membership-form";
@import "reviewables";
@import "revise-and-reject-post-reviewable";
@import "rtl";
@import "search-menu";
@import "search";
Expand Down
@@ -0,0 +1,46 @@
.modal.revise-and-reject-reviewable {
.modal-inner-container {
max-width: 30em;
}

.modal-body {
.control-label {
font-weight: 700;
}

.select-kit {
width: 100%;
summary {
height: 100%;
}
}
}

.revise-and-reject-reviewable__optional {
margin-left: 0.5em;
color: var(--primary-low-mid);
}

.revise-and-reject-reviewable__custom-reason {
width: 100%;
}

.revise-and-reject-reviewable__queued-post {
@extend .reviewable-item;

padding: 1em;
margin: 0 0 1em 0;

.post-topic .title-text {
font-size: var(--font-up-1);
}

.post-body {
margin: 0;

p {
margin: 0;
}
Comment on lines +41 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This default margin on p is hurting us more often than helping :(

}
}
}
7 changes: 2 additions & 5 deletions app/controllers/reviewables_controller.rb
Expand Up @@ -222,11 +222,8 @@ def perform
return render_json_error(error)
end

if reviewable.type == "ReviewableUser"
args.merge!(
reject_reason: params[:reject_reason],
send_email: params[:send_email] != "false",
)
if reviewable.type_class.respond_to?(:additional_args)
args.merge!(reviewable.type_class.additional_args(params) || {})
end

plugin_params =
Expand Down
4 changes: 4 additions & 0 deletions app/models/reviewable.rb
Expand Up @@ -547,6 +547,10 @@ def basic_serializer
TYPE_TO_BASIC_SERIALIZER[self.type.to_sym] || BasicReviewableSerializer
end

def type_class
Reviewable.sti_class_for(self.type)
end

def self.lookup_serializer_for(type)
"#{type}Serializer".constantize
rescue NameError
Expand Down
32 changes: 32 additions & 0 deletions app/models/reviewable_queued_post.rb
Expand Up @@ -16,6 +16,16 @@ class ReviewableQueuedPost < Reviewable

after_commit :compute_user_stats, only: %i[create update]

def self.additional_args(params)
return {} if params[:revise_reason].blank?

{
revise_reason: params[:revise_reason],
revise_feedback: params[:revise_feedback],
revise_custom_reason: params[:revise_custom_reason],
}
end

def updatable_reviewable_scores
# Approvals are possible for already rejected queued posts. We need the
# scores to be updated when this happens.
Expand Down Expand Up @@ -57,6 +67,10 @@ def build_actions(actions, guardian, args)
a.label = "reviewables.actions.reject_post.title"
end
end

actions.add(:revise_and_reject_post) do |a|
a.label = "reviewables.actions.revise_and_reject_post.title"
end
end

actions.add(:delete) if guardian.can_delete?(self)
Expand Down Expand Up @@ -147,6 +161,24 @@ def perform_reject_post(performed_by, args)
create_result(:success, :rejected)
end

def perform_revise_and_reject_post(performed_by, args)
pm_translation_args = {
topic_title: self.topic.title,
topic_url: self.topic.url,
reason: args[:revise_custom_reason].presence || args[:revise_reason],
feedback: args[:revise_feedback],
original_post: self.payload["raw"],
site_name: SiteSetting.title,
}
SystemMessage.create_from_system_user(
self.target_created_by,
:reviewable_queued_post_revise_and_reject,
pm_translation_args,
)
StaffActionLogger.new(performed_by).log_post_rejected(self, DateTime.now) if performed_by.staff?
create_result(:success, :rejected)
end

def perform_delete(performed_by, args)
create_result(:success, :deleted)
end
Expand Down
4 changes: 4 additions & 0 deletions app/models/reviewable_user.rb
Expand Up @@ -5,6 +5,10 @@ def self.create_for(user)
create(created_by_id: Discourse.system_user.id, target: user)
end

def self.additional_args(params)
{ reject_reason: params[:reject_reason], send_email: params[:send_email] != "false" }
end

def build_actions(actions, guardian, args)
return unless pending?

Expand Down
8 changes: 8 additions & 0 deletions config/locales/client.en.yml
Expand Up @@ -484,6 +484,14 @@ en:
type_bonus:
name: "type bonus"
title: "Certain reviewable types can be assigned a bonus by staff to make them a higher priority."
revise_and_reject_post:
title: "Revise"
reason: "Reason"
send_pm: "Send PM"
feedback: "Feedback"
custom_reason: "Give a clear description of the reason"
other_reason: "Other..."
optional: "optional"
stale_help: "This reviewable has been resolved by <b>%{username}</b>."
claim_help:
optional: "You can claim this item to prevent others from reviewing it."
Expand Down
26 changes: 26 additions & 0 deletions config/locales/server.en.yml
Expand Up @@ -2302,6 +2302,7 @@ en:
approve_new_topics_unless_trust_level: "New topics for users below this trust level must be approved"
approve_unless_staged: "New topics and posts for staged users must be approved"
notify_about_queued_posts_after: "If there are posts that have been waiting to be reviewed for more than this many hours, send a notification to all moderators. Set to 0 to disable these notifications."
reviewable_revision_reasons: "List of reasons that can be selected when rejecting a reviewable queued post with a revision. Other is always available as well, which allows for a custom reason to be entered."
auto_close_messages_post_count: "Maximum number of posts allowed in a message before it is automatically closed (0 to disable)"
auto_close_topics_post_count: "Maximum number of posts allowed in a topic before it is automatically closed (0 to disable)"
auto_close_topics_create_linked_topic: "Create a new linked topic when a topic is auto-closed based on 'auto close topics post count' setting"
Expand Down Expand Up @@ -2985,6 +2986,29 @@ en:

For additional guidance, please refer to our [community guidelines](%{base_url}/guidelines).

reviewable_queued_post_revise_and_reject:
title: "Feedback on your post"
subject_template: "Feedback on your post in %{topic_title}"
text_body_template: |
Hi %{username},

We've reviewed your post in [%{topic_title}](%{topic_url}) and have some feedback for you.

Reason: %{reason}

Feedback: %{feedback}

You can edit your original post below and re-submit to make the suggested changes, or reply to this message if you have any questions.

--------

%{original_post}

--------

Thanks,
%{site_name} Moderators

post_hidden_again:
title: "Post Hidden again"
subject_template: "Post hidden by community flags, staff notified"
Expand Down Expand Up @@ -5227,6 +5251,8 @@ en:
title: "No"
discard_post:
title: "Discard Post"
revise_and_reject_post:
title: "Revise Post..."
ignore:
title: "Ignore"
ignore_and_do_nothing:
Expand Down