UX/DEV: Review queue redesign fixes (#20239)
* UX: add type tag and design update * UX: clarify status copy in reviewQ * DEV: switch to selectKit * UX: color approve/reject buttons in RQ * DEV: regroup actions * UX: add type tag and design update * UX: clarify status copy in reviewQ * Join questions for flagged post with "or" with new I18n function * Move ReviewableScores component out of context * Add CSS classes to reviewable-item based on human type * UX: add table header for scoring * UX: don't display % score * UX: prefix modifier class with dash * UX: reviewQ flag table styling * UX: consistent use of ignore icon * DEV: only show context question on pending status * UX: only show table headers on pending status * DEV: reviewQ regroup actions for hidden posts * UX: reviewQ > approve/reject buttons * UX: reviewQ add fadeout * UX: reviewQ styling * DEV: move scores back into component * UX: reviewQ mobile styling * UX: score table on mobile * UX: reviewQ > move meta info outside table * UX: reviewQ > score layout fixes * DEV: readd `agree_and_keep` and fix the spec tests. * Fix the spec tests * fix the quint test * DEV: readd deleting replies * UX: reviewQ copy tweaks * DEV: readd test for ignore + delete replies * Remove old * FIX: Add perform_ignore back in for backwards compat * DEV: add an action alias `ignore` for `ignore_and_do_nothing`. --------- Co-authored-by: Martin Brennan <martin@discourse.org> Co-authored-by: Vinoth Kannan <svkn.87@gmail.com>
This commit is contained in:
parent
67c0498f64
commit
e52bbc1230
|
@ -1,11 +1,11 @@
|
|||
{{#if this.multiple}}
|
||||
<DropdownSelectBox
|
||||
@class="reviewable-action-dropdown"
|
||||
@class="reviewable-action-dropdown btn-icon-text"
|
||||
@nameProperty="label"
|
||||
@content={{this.bundle.actions}}
|
||||
@onChange={{action "performById"}}
|
||||
@options={{hash
|
||||
icon=this.bundle.icon
|
||||
showCaret=true
|
||||
disabled=this.reviewableUpdating
|
||||
placement=this.placement
|
||||
translatedNone=this.bundle.label
|
||||
|
@ -19,7 +19,6 @@
|
|||
" "
|
||||
this.first.button_class
|
||||
}}
|
||||
@icon={{this.first.icon}}
|
||||
@action={{action "perform" this.first}}
|
||||
@translatedLabel={{this.first.label}}
|
||||
@disabled={{this.reviewableUpdating}}
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
@tagName=""
|
||||
/>
|
||||
<div class="post-body">
|
||||
<div class="post-body__scroll">
|
||||
{{#if this.reviewable.blank_post}}
|
||||
<p>{{i18n "review.deleted_post"}}</p>
|
||||
{{else}}
|
||||
{{html-safe this.reviewable.cooked}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
<PluginOutlet
|
||||
@name="after-reviewable-flagged-post-body"
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
class="reviewable-item {{this.customClasses}}"
|
||||
>
|
||||
<div class="reviewable-meta-data">
|
||||
<span class="reviewable-type">{{this.reviewable.humanType}}</span>
|
||||
<span
|
||||
class={{concat-class "reviewable-type" this.reviewable.humanTypeCssClass}}
|
||||
>{{this.reviewable.humanType}}</span>
|
||||
{{#if this.reviewable.reply_count}}
|
||||
<span class="reply-count">{{i18n
|
||||
"review.replies"
|
||||
|
@ -65,6 +67,15 @@
|
|||
{{/component}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if (eq this.reviewable.type "ReviewableFlaggedPost")}}
|
||||
{{#if (eq this.reviewable.status 0)}}
|
||||
<h3 class="reviewable-item__context-question">
|
||||
{{this.reviewable.flaggedPostContextQuestion}}
|
||||
</h3>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<div class="reviewable-actions">
|
||||
{{#if this.reviewable.last_performing_username}}
|
||||
<div class="stale-help">{{html-safe
|
||||
|
|
|
@ -4,25 +4,18 @@
|
|||
{{avatar this.rs.user imageSize="tiny"}}
|
||||
{{this.rs.user.username}}
|
||||
</UserLink>
|
||||
<UserFlagPercentage
|
||||
@agreed={{this.rs.agree_stats.agreed}}
|
||||
@disagreed={{this.rs.agree_stats.disagreed}}
|
||||
@ignored={{this.rs.agree_stats.ignored}}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{{d-icon this.rs.score_type.icon}}
|
||||
{{this.title}}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{format-date this.rs.created_at format="tiny"}}
|
||||
</td>
|
||||
|
||||
{{#if this.showStatus}}
|
||||
<td class="reviewable-score-spacer">
|
||||
{{d-icon "angle-double-right"}}
|
||||
<td>
|
||||
{{d-icon this.rs.score_type.icon}}
|
||||
{{this.title}}
|
||||
</td>
|
||||
|
||||
{{#if this.showStatus}}
|
||||
<td class="reviewed-by">
|
||||
{{#if this.rs.reviewed_by}}
|
||||
<UserLink @user={{this.rs.reviewed_by}}>
|
||||
|
@ -34,46 +27,17 @@
|
|||
{{/if}}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{reviewable-status this.rs.status}}
|
||||
</td>
|
||||
<td>
|
||||
{{#if this.rs.reviewed_by}}
|
||||
{{format-date this.rs.reviewed_at format="tiny"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{reviewable-status this.rs.status}}
|
||||
</td>
|
||||
|
||||
{{else}}
|
||||
<td colspan="4"></td>
|
||||
{{/if}}
|
||||
</tr>
|
||||
|
||||
{{#if this.rs.reason}}
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<div class="reviewable-score-reason">{{html-safe this.rs.reason}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.rs.reviewable_conversation}}
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<div class="reviewable-conversation">
|
||||
{{#each
|
||||
this.rs.reviewable_conversation.conversation_posts
|
||||
as |p index|
|
||||
}}
|
||||
<ReviewableConversationPost @post={{p}} @index={{index}} />
|
||||
{{/each}}
|
||||
<div class="controls">
|
||||
<a
|
||||
href={{this.rs.reviewable_conversation.permalink}}
|
||||
class="btn btn-small"
|
||||
>
|
||||
{{i18n "review.conversation.view_full"}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/if}}
|
|
@ -1,9 +1,44 @@
|
|||
{{#if this.reviewable.reviewable_scores}}
|
||||
<div class="reviewable-scores__table-wrapper">
|
||||
<table class="reviewable-scores">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n "review.scores.submitted_by"}}</th>
|
||||
<th>{{i18n "review.scores.date"}}</th>
|
||||
<th>{{i18n "review.scores.type"}}</th>
|
||||
<th>{{i18n "review.scores.reviewed_by"}}</th>
|
||||
<th>{{i18n "review.scores.reviewed_timestamp"}}</th>
|
||||
<th>{{i18n "review.scores.status"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each this.reviewable.reviewable_scores as |rs|}}
|
||||
<ReviewableScore @rs={{rs}} @reviewable={{this.reviewable}} />
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{#each this.reviewable.reviewable_scores as |rs|}}
|
||||
{{#if rs.reason}}
|
||||
<div class="reviewable-score-reason">{{html-safe rs.reason}}</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if rs.reviewable_conversation}}
|
||||
<div class="reviewable-conversation">
|
||||
{{#each rs.reviewable_conversation.conversation_posts as |p index|}}
|
||||
<ReviewableConversationPost @post={{p}} @index={{index}} />
|
||||
{{/each}}
|
||||
<div class="controls">
|
||||
<a
|
||||
href={{rs.reviewable_conversation.permalink}}
|
||||
class="btn btn-small"
|
||||
>
|
||||
{{i18n "review.conversation.view_full"}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
|
||||
{{/if}}
|
|
@ -33,12 +33,10 @@ export function htmlStatus(status) {
|
|||
let icon = data.icon ? iconHTML(data.icon) : "";
|
||||
|
||||
return `
|
||||
<span class='status'>
|
||||
<span class="${data.name}">
|
||||
${icon}
|
||||
${I18n.t("review.statuses." + data.name + ".title")}
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import categoryFromId from "discourse-common/utils/category-macro";
|
||||
import { dasherize, underscore } from "@ember/string";
|
||||
import I18n from "I18n";
|
||||
import { Promise } from "rsvp";
|
||||
import RestModel from "discourse/models/rest";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { underscore } from "@ember/string";
|
||||
|
||||
export const PENDING = 0;
|
||||
export const APPROVED = 1;
|
||||
|
@ -14,17 +14,50 @@ export const DELETED = 4;
|
|||
|
||||
const Reviewable = RestModel.extend({
|
||||
@discourseComputed("type", "topic")
|
||||
humanType(type, topic) {
|
||||
resolvedType(type, topic) {
|
||||
// Display "Queued Topic" if the post will create a topic
|
||||
if (type === "ReviewableQueuedPost" && !topic) {
|
||||
type = "ReviewableQueuedTopic";
|
||||
return "ReviewableQueuedTopic";
|
||||
}
|
||||
|
||||
return I18n.t(`review.types.${underscore(type)}.title`, {
|
||||
return type;
|
||||
},
|
||||
|
||||
@discourseComputed("resolvedType")
|
||||
humanType(resolvedType) {
|
||||
return I18n.t(`review.types.${underscore(resolvedType)}.title`, {
|
||||
defaultValue: "",
|
||||
});
|
||||
},
|
||||
|
||||
@discourseComputed("humanType")
|
||||
humanTypeCssClass(humanType) {
|
||||
return "-" + dasherize(humanType);
|
||||
},
|
||||
|
||||
@discourseComputed
|
||||
flaggedPostContextQuestion() {
|
||||
const uniqueReviewableScores =
|
||||
this.reviewable_scores.uniqBy("score_type.type");
|
||||
|
||||
if (uniqueReviewableScores.length === 1) {
|
||||
if (uniqueReviewableScores[0].score_type.type === "notify_moderators") {
|
||||
return I18n.t("review.context_question.something_else_wrong");
|
||||
}
|
||||
}
|
||||
|
||||
const listOfQuestions = I18n.listJoiner(
|
||||
uniqueReviewableScores
|
||||
.map((score) => score.score_type.title.toLowerCase())
|
||||
.uniq(),
|
||||
I18n.t("review.context_question.delimiter")
|
||||
);
|
||||
|
||||
return I18n.t("review.context_question.is_this_post", {
|
||||
reviewable_human_score_types: listOfQuestions,
|
||||
});
|
||||
},
|
||||
|
||||
category: categoryFromId("category_id"),
|
||||
|
||||
update(updates) {
|
||||
|
|
|
@ -114,7 +114,9 @@ acceptance("Review", function (needs) {
|
|||
);
|
||||
|
||||
assert.strictEqual(
|
||||
query(".reviewable-flagged-post .post-body").innerHTML.trim(),
|
||||
query(
|
||||
".reviewable-flagged-post .post-body .post-body__scroll"
|
||||
).innerHTML.trim(),
|
||||
"<b>cooked content</b>"
|
||||
);
|
||||
|
||||
|
|
|
@ -266,6 +266,19 @@ I18n.toHumanSize = function (number, options) {
|
|||
return number;
|
||||
};
|
||||
|
||||
I18n.listJoiner = function (listOfStrings, delimiter) {
|
||||
if (listOfStrings.length === 1) {
|
||||
return listOfStrings[0];
|
||||
}
|
||||
|
||||
if (listOfStrings.length === 2) {
|
||||
return listOfStrings[0] + " " + delimiter + " " + listOfStrings[1];
|
||||
}
|
||||
|
||||
var lastString = listOfStrings.pop();
|
||||
return listOfStrings.concat(delimiter).join(`, `) + " " + lastString;
|
||||
};
|
||||
|
||||
I18n.pluralizer = function (locale) {
|
||||
var pluralizer = this.pluralizationRules[locale];
|
||||
if (pluralizer !== undefined) return pluralizer;
|
||||
|
|
|
@ -209,8 +209,8 @@
|
|||
}
|
||||
|
||||
.reviewable-item {
|
||||
padding-top: 2em;
|
||||
border-top: 1px solid var(--primary-low);
|
||||
background: var(--primary-very-low);
|
||||
padding: 1.5rem;
|
||||
|
||||
.topic-statuses {
|
||||
font-size: var(--font-up-2);
|
||||
|
@ -225,6 +225,21 @@
|
|||
align-items: baseline;
|
||||
.reviewable-type {
|
||||
margin-right: 0.25em;
|
||||
padding: 0.25em 0.5em;
|
||||
text-transform: uppercase;
|
||||
font-size: var(--font-down-2);
|
||||
color: var(--secondary);
|
||||
border-radius: 8px;
|
||||
&.-flagged-post,
|
||||
&.-user,
|
||||
&.-flagged-chat-message,
|
||||
&.-aksimet-flagged-post,
|
||||
&.-aksimet-flagged-user {
|
||||
background-color: var(--danger-medium);
|
||||
}
|
||||
&.-queued-post {
|
||||
background-color: var(--tertiary);
|
||||
}
|
||||
}
|
||||
.reply-count {
|
||||
margin-left: 1em;
|
||||
|
@ -244,16 +259,28 @@
|
|||
.reviewable-contents {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 2em;
|
||||
margin: 1.5rem 0 1rem;
|
||||
background: var(--secondary);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.reviewable-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
white-space: nowrap;
|
||||
|
||||
&.approve-post {
|
||||
background-color: var(--success);
|
||||
color: var(--secondary);
|
||||
}
|
||||
&.reject-post {
|
||||
background-color: var(--danger);
|
||||
color: var(--secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.reviewable-action,
|
||||
|
@ -285,9 +312,18 @@
|
|||
}
|
||||
|
||||
.reviewable-scores {
|
||||
margin-top: 1.5rem;
|
||||
min-width: 50%;
|
||||
color: var(--primary-high);
|
||||
|
||||
&__table-wrapper {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.reviewed-by {
|
||||
.date {
|
||||
margin-left: 0.5em;
|
||||
|
@ -313,6 +349,16 @@
|
|||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.approved,
|
||||
.approved svg {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.rejected,
|
||||
.rejected svg {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
tbody {
|
||||
border-width: 1px;
|
||||
td {
|
||||
|
@ -324,27 +370,19 @@
|
|||
@include ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
td:last-of-type {
|
||||
width: 100%;
|
||||
white-space: normal;
|
||||
}
|
||||
> tr > th {
|
||||
text-align: left;
|
||||
}
|
||||
> tr > th,
|
||||
> tr > td {
|
||||
&:not(:empty) {
|
||||
padding: 0.5em 1em 0.5em 0;
|
||||
padding: 0.5em;
|
||||
}
|
||||
@include breakpoint("mobile-large") {
|
||||
@include ellipsis;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
}
|
||||
.reviewable-score-spacer {
|
||||
padding-right: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -373,6 +411,7 @@
|
|||
}
|
||||
|
||||
.reviewable-item {
|
||||
margin-block: 3rem;
|
||||
.show-raw-email {
|
||||
color: var(--primary-medium);
|
||||
font-size: var(--font-down-2);
|
||||
|
@ -425,10 +464,26 @@
|
|||
}
|
||||
|
||||
.post-body {
|
||||
position: relative;
|
||||
max-width: var(--topic-body-width);
|
||||
max-height: 300px;
|
||||
margin-top: 0.5em;
|
||||
|
||||
&__scroll {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 1.5em;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(var(--secondary-rgb), 0),
|
||||
rgba(var(--secondary-rgb), 100%)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
p,
|
||||
aside {
|
||||
|
@ -453,6 +508,10 @@
|
|||
margin-right: 0.75em;
|
||||
}
|
||||
}
|
||||
|
||||
&__context-question {
|
||||
margin-block: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.editable-fields {
|
||||
|
@ -486,28 +545,3 @@
|
|||
.flag-modal .modal-inner-container .select-kit.reviewable-action-dropdown {
|
||||
width: initial;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1000px) {
|
||||
table.reviewable-scores {
|
||||
width: 100%;
|
||||
display: block;
|
||||
tbody {
|
||||
width: calc(100% - 5px);
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
tr.reviewable-score {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto 1fr;
|
||||
}
|
||||
td.reviewable-score-spacer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint("mobile-large") {
|
||||
tr.reviewable-score {
|
||||
grid-template-columns: auto auto auto;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,34 @@
|
|||
}
|
||||
|
||||
.reviewable-scores {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
overflow-x: scroll;
|
||||
border-top: 1px solid var(--primary-low);
|
||||
thead {
|
||||
tr {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
display: flex;
|
||||
border: 0;
|
||||
|
||||
.reviewable-score {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
tr {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +85,6 @@
|
|||
|
||||
> div,
|
||||
> button {
|
||||
flex: 0 1 47%;
|
||||
margin-right: 0.25em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ module Jobs
|
|||
.where("created_at < ?", SiteSetting.auto_handle_queued_age.to_i.days.ago)
|
||||
.each do |reviewable|
|
||||
if reviewable.is_a?(ReviewableFlaggedPost)
|
||||
reviewable.perform(Discourse.system_user, :ignore, expired: true)
|
||||
reviewable.perform(Discourse.system_user, :ignore_and_do_nothing, expired: true)
|
||||
elsif reviewable.is_a?(ReviewableQueuedPost)
|
||||
reviewable.perform(Discourse.system_user, :reject_post)
|
||||
elsif reviewable.is_a?(ReviewableUser)
|
||||
|
|
|
@ -10,6 +10,7 @@ class ReviewableFlaggedPost < Reviewable
|
|||
agree_and_silence: :agree_and_keep,
|
||||
agree_and_suspend: :agree_and_keep,
|
||||
disagree_and_restore: :disagree,
|
||||
ignore_and_do_nothing: :ignore,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -56,6 +57,20 @@ class ReviewableFlaggedPost < Reviewable
|
|||
build_action(actions, :agree_and_keep, icon: "thumbs-up", bundle: agree)
|
||||
end
|
||||
|
||||
if guardian.can_delete_post_or_topic?(post)
|
||||
build_action(actions, :delete_and_agree, icon: "far-trash-alt", bundle: agree)
|
||||
|
||||
if post.reply_count > 0
|
||||
build_action(
|
||||
actions,
|
||||
:delete_and_agree_replies,
|
||||
icon: "far-trash-alt",
|
||||
bundle: agree,
|
||||
confirm: true,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if guardian.can_suspend?(target_created_by)
|
||||
build_action(
|
||||
actions,
|
||||
|
@ -74,48 +89,43 @@ class ReviewableFlaggedPost < Reviewable
|
|||
end
|
||||
|
||||
build_action(actions, :agree_and_restore, icon: "far-eye", bundle: agree) if post.user_deleted?
|
||||
|
||||
if post.hidden?
|
||||
build_action(actions, :disagree_and_restore, icon: "thumbs-down")
|
||||
else
|
||||
build_action(actions, :disagree, icon: "thumbs-down")
|
||||
end
|
||||
|
||||
build_action(actions, :ignore, icon: "external-link-alt")
|
||||
|
||||
delete_user_actions(actions) if potential_spam? && guardian.can_delete_user?(target_created_by)
|
||||
|
||||
if guardian.can_delete_post_or_topic?(post)
|
||||
delete =
|
||||
ignore =
|
||||
actions.add_bundle(
|
||||
"#{id}-delete",
|
||||
icon: "far-trash-alt",
|
||||
label: "reviewables.actions.delete.title",
|
||||
"#{id}-ignore",
|
||||
icon: "thumbs-up",
|
||||
label: "reviewables.actions.ignore.title",
|
||||
)
|
||||
build_action(actions, :delete_and_ignore, icon: "external-link-alt", bundle: delete)
|
||||
|
||||
if !post.hidden?
|
||||
build_action(actions, :ignore_and_do_nothing, icon: "external-link-alt", bundle: ignore)
|
||||
end
|
||||
if guardian.can_delete_post_or_topic?(post)
|
||||
build_action(actions, :delete_and_ignore, icon: "far-trash-alt", bundle: ignore)
|
||||
if post.reply_count > 0
|
||||
build_action(
|
||||
actions,
|
||||
:delete_and_ignore_replies,
|
||||
icon: "external-link-alt",
|
||||
icon: "far-trash-alt",
|
||||
confirm: true,
|
||||
bundle: delete,
|
||||
bundle: ignore,
|
||||
)
|
||||
end
|
||||
build_action(actions, :delete_and_agree, icon: "thumbs-up", bundle: delete)
|
||||
if post.reply_count > 0
|
||||
build_action(
|
||||
actions,
|
||||
:delete_and_agree_replies,
|
||||
icon: "external-link-alt",
|
||||
bundle: delete,
|
||||
confirm: true,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
delete_user_actions(actions) if potential_spam? && guardian.can_delete_user?(target_created_by)
|
||||
end
|
||||
|
||||
def perform_ignore(performed_by, args)
|
||||
perform_ignore_and_do_nothing(performed_by, args)
|
||||
end
|
||||
|
||||
def perform_ignore_and_do_nothing(performed_by, args)
|
||||
actions =
|
||||
PostAction
|
||||
.active
|
||||
|
@ -221,13 +231,13 @@ class ReviewableFlaggedPost < Reviewable
|
|||
end
|
||||
|
||||
def perform_delete_and_ignore(performed_by, args)
|
||||
result = perform_ignore(performed_by, args)
|
||||
result = perform_ignore_and_do_nothing(performed_by, args)
|
||||
destroyer(performed_by, post).destroy
|
||||
result
|
||||
end
|
||||
|
||||
def perform_delete_and_ignore_replies(performed_by, args)
|
||||
result = perform_ignore(performed_by, args)
|
||||
result = perform_ignore_and_do_nothing(performed_by, args)
|
||||
PostDestroyer.delete_with_replies(performed_by, post, self)
|
||||
|
||||
result
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ReviewableScoreTypeSerializer < ApplicationSerializer
|
||||
attributes :id, :title, :reviewable_priority, :icon
|
||||
attributes :id, :title, :reviewable_priority, :icon, :type
|
||||
|
||||
def type
|
||||
ReviewableScore.types[id]
|
||||
end
|
||||
|
||||
# Allow us to share post action type translations for backwards compatibility
|
||||
def title
|
||||
|
|
|
@ -581,28 +581,34 @@ en:
|
|||
scores:
|
||||
about: "This score is calculated based on the trust level of the reporter, the accuracy of their previous flags, and the priority of the item being reported."
|
||||
score: "Score"
|
||||
date: "Date"
|
||||
type: "Type"
|
||||
date: "Report date"
|
||||
type: "Reason"
|
||||
status: "Status"
|
||||
submitted_by: "Submitted By"
|
||||
reviewed_by: "Reviewed By"
|
||||
submitted_by: "Reported by"
|
||||
reviewed_by: "Reviewed by"
|
||||
reviewed_timestamp: "Review date"
|
||||
|
||||
statuses:
|
||||
pending:
|
||||
title: "Pending"
|
||||
approved:
|
||||
title: "Approved"
|
||||
title: "Flag approved"
|
||||
rejected:
|
||||
title: "Rejected"
|
||||
title: "Flag rejected"
|
||||
ignored:
|
||||
title: "Ignored"
|
||||
title: "Flag ignored"
|
||||
deleted:
|
||||
title: "Deleted"
|
||||
title: "Topic or post deleted"
|
||||
reviewed:
|
||||
title: "(all reviewed)"
|
||||
all:
|
||||
title: "(everything)"
|
||||
|
||||
context_question:
|
||||
is_this_post: "Is this post %{reviewable_human_score_types}?"
|
||||
delimiter: "or"
|
||||
something_else_wrong: "Is there something else wrong with this post?"
|
||||
|
||||
types:
|
||||
reviewable_flagged_post:
|
||||
title: "Flagged Post"
|
||||
|
|
|
@ -2456,7 +2456,7 @@ en:
|
|||
search_tokenize_japanese_enabled: "You must disable 'search_tokenize_japanese' before enabling this setting."
|
||||
discourse_connect_cannot_be_enabled_if_second_factor_enforced: "You cannot enable DiscourseConnect if 2FA is enforced."
|
||||
delete_rejected_email_after_days: "This setting cannot be set lower than the delete_email_logs_after_days setting or greater than %{max}"
|
||||
invalid_uncategorized_category_setting: "The \"Uncategorized\" category cannot be selected if 'allow uncategorized topics' is not enabled."
|
||||
invalid_uncategorized_category_setting: 'The "Uncategorized" category cannot be selected if ''allow uncategorized topics'' is not enabled.'
|
||||
enable_new_notifications_menu_not_legacy_navigation_menu: "You must set `navigation_menu` to `legacy` before enabling this setting."
|
||||
invalid_search_ranking_weights: "Value is invalid for search_ranking_weights site setting. Example: '{0.1,0.2,0.3,1.0}'. Note that maximum value for each weight is 1.0."
|
||||
|
||||
|
@ -5133,50 +5133,53 @@ en:
|
|||
|
||||
actions:
|
||||
agree:
|
||||
title: "Agree..."
|
||||
title: "Yes"
|
||||
agree_and_keep:
|
||||
title: "Keep Post"
|
||||
description: "Agree with flag and keep the post unchanged."
|
||||
title: "Keep post"
|
||||
description: "Agree with flag but keep this post unchanged."
|
||||
agree_and_keep_hidden:
|
||||
title: "Keep Post Hidden"
|
||||
description: "Agree with flag and leave the post hidden."
|
||||
title: "Keep post hidden"
|
||||
description: "Agree with flag and keep the post hidden."
|
||||
agree_and_suspend:
|
||||
title: "Suspend User"
|
||||
title: "Suspend user"
|
||||
description: "Agree with flag and suspend the user."
|
||||
agree_and_silence:
|
||||
title: "Silence User"
|
||||
title: "Silence user"
|
||||
description: "Agree with flag and silence the user."
|
||||
agree_and_restore:
|
||||
title: "Restore Post"
|
||||
title: "Restore post"
|
||||
description: "Restore the post so that all users can see it."
|
||||
agree_and_hide:
|
||||
title: "Hide Post"
|
||||
description: "Hide this post and automatically send the user a message urging them to edit it."
|
||||
title: "Hide post"
|
||||
description: "Agree with flag and hide this post + automatically send the user a message urging them to edit it."
|
||||
delete_single:
|
||||
title: "Delete"
|
||||
delete:
|
||||
title: "Delete..."
|
||||
delete_and_ignore:
|
||||
title: "Delete Post and Ignore"
|
||||
description: "Delete post; if the first post, delete the topic as well"
|
||||
title: "Ignore flag and delete post"
|
||||
description: "Ignore the flag by removing it from the queue and delete the post; if the first post, delete the topic as well. "
|
||||
delete_and_ignore_replies:
|
||||
title: "Delete Post + Replies and Ignore"
|
||||
description: "Delete post and all of its replies; if the first post, delete the topic as well"
|
||||
title: "Ignore flag, delete post and replies"
|
||||
description: "Ignore the flag by removing it from the queue, delete the post and all of its replies; if the first post, delete the topic as well"
|
||||
confirm: "Are you sure you want to delete the replies to the post as well?"
|
||||
delete_and_agree:
|
||||
title: "Delete Post and Agree"
|
||||
description: "Delete post; if the first post, delete the topic as well"
|
||||
title: "Delete post"
|
||||
description: "Agree with flag and delete this post; if the first post, delete the topic as well."
|
||||
delete_and_agree_replies:
|
||||
title: "Delete Post + Replies and Agree"
|
||||
description: "Delete post and all of its replies; if the first post, delete the topic as well"
|
||||
title: "Delete post and replies"
|
||||
description: "Agree with flag and delete this post and all of its replies; if the first post, delete the topic as well."
|
||||
confirm: "Are you sure you want to delete the replies to the post as well?"
|
||||
disagree_and_restore:
|
||||
title: "Disagree and Restore Post"
|
||||
title: "No, restore post"
|
||||
description: "Restore the post so that all users can see it."
|
||||
disagree:
|
||||
title: "Disagree"
|
||||
title: "No"
|
||||
ignore:
|
||||
title: "Ignore"
|
||||
ignore_and_do_nothing:
|
||||
title: "Do nothing"
|
||||
description: "Ignore the flag by removing it from the queue without taking any action. Hidden posts will stay hidden and be handled by the auto-tools."
|
||||
approve:
|
||||
title: "Approve"
|
||||
approve_post:
|
||||
|
|
|
@ -357,7 +357,7 @@ class PostDestroyer
|
|||
end
|
||||
|
||||
def ignore(reviewable)
|
||||
reviewable.perform_ignore(@user, post_was_deleted: true)
|
||||
reviewable.perform_ignore_and_do_nothing(@user, post_was_deleted: true)
|
||||
reviewable.transition_to(:ignored, @user)
|
||||
end
|
||||
|
||||
|
|
|
@ -232,7 +232,7 @@ export function setup(helper) {
|
|||
});
|
||||
|
||||
helper.buildCookFunction((opts, generateCookFunction) => {
|
||||
if (!opts.discourse.additionalOptions) {
|
||||
if (!opts.discourse.additionalOptions?.chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ module DiscourseNarrativeBot
|
|||
opts[:delete_removed_posts_after] = 1
|
||||
|
||||
result = PostActionCreator.notify_moderators(self.discobot_user, post)
|
||||
result.reviewable.perform(self.discobot_user, :ignore)
|
||||
result.reviewable.perform(self.discobot_user, :ignore_and_do_nothing)
|
||||
end
|
||||
|
||||
PostDestroyer.new(@user, post, opts).destroy
|
||||
|
|
|
@ -53,7 +53,7 @@ RSpec.describe Jobs::TruncateUserFlagStats do
|
|||
|
||||
r0.perform(Discourse.system_user, :agree_and_keep)
|
||||
r1.perform(Discourse.system_user, :disagree)
|
||||
r2.perform(Discourse.system_user, :ignore)
|
||||
r2.perform(Discourse.system_user, :ignore_and_do_nothing)
|
||||
r3.perform(Discourse.system_user, :agree_and_keep)
|
||||
|
||||
user.user_stat.reload
|
||||
|
|
|
@ -191,7 +191,7 @@ RSpec.describe PostActionCreator do
|
|||
end
|
||||
|
||||
describe "When the post was already reviewed by staff" do
|
||||
before { reviewable.perform(admin, :ignore) }
|
||||
before { reviewable.perform(admin, :ignore_and_do_nothing) }
|
||||
|
||||
it "fails because the post was recently reviewed" do
|
||||
freeze_time 10.seconds.from_now
|
||||
|
|
|
@ -94,7 +94,7 @@ RSpec.describe PostDestroyer do
|
|||
expect(reply1.deleted_at).to eq(nil)
|
||||
|
||||
# ignore the flag, we should be able to delete the stub
|
||||
reviewable.perform(Discourse.system_user, :ignore)
|
||||
reviewable.perform(Discourse.system_user, :ignore_and_do_nothing)
|
||||
PostDestroyer.destroy_stubs
|
||||
|
||||
reply1.reload
|
||||
|
@ -938,7 +938,7 @@ RSpec.describe PostDestroyer do
|
|||
|
||||
it "should not send the flags_agreed_and_post_deleted message if flags were ignored" do
|
||||
expect(ReviewableFlaggedPost.pending.count).to eq(1)
|
||||
flag_result.reviewable.perform(moderator, :ignore)
|
||||
flag_result.reviewable.perform(moderator, :ignore_and_do_nothing)
|
||||
second_post.reload
|
||||
expect(ReviewableFlaggedPost.pending.count).to eq(0)
|
||||
|
||||
|
|
|
@ -975,7 +975,8 @@ RSpec.describe PostAction do
|
|||
end
|
||||
|
||||
it "creates events for ignored" do
|
||||
events = DiscourseEvent.track_events { reviewable.perform(moderator, :ignore) }
|
||||
events =
|
||||
DiscourseEvent.track_events { reviewable.perform(moderator, :ignore_and_do_nothing) }
|
||||
|
||||
reviewed_event = events.find { |e| e[:event_name] == :flag_reviewed }
|
||||
expect(reviewed_event).to be_present
|
||||
|
|
|
@ -200,7 +200,7 @@ RSpec.describe Post do
|
|||
|
||||
it "is_flagged? is true if flag was deferred" do
|
||||
result = PostActionCreator.off_topic(user, post)
|
||||
result.reviewable.perform(admin, :ignore)
|
||||
result.reviewable.perform(admin, :ignore_and_do_nothing)
|
||||
expect(post.reload.is_flagged?).to eq(true)
|
||||
end
|
||||
|
||||
|
@ -214,7 +214,7 @@ RSpec.describe Post do
|
|||
result = PostActionCreator.spam(user, post)
|
||||
expect(post.reviewable_flag).to eq(result.reviewable)
|
||||
|
||||
result.reviewable.perform(admin, :ignore)
|
||||
result.reviewable.perform(admin, :ignore_and_do_nothing)
|
||||
expect(post.reviewable_flag).to be_nil
|
||||
end
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ RSpec.describe ReviewableFlaggedPost, type: :model do
|
|||
expect(actions.has?(:delete_user)).to eq(true)
|
||||
expect(actions.has?(:delete_user_block)).to eq(true)
|
||||
expect(actions.has?(:disagree)).to eq(true)
|
||||
expect(actions.has?(:ignore)).to eq(true)
|
||||
expect(actions.has?(:ignore_and_do_nothing)).to eq(true)
|
||||
expect(actions.has?(:delete_and_ignore)).to eq(true)
|
||||
expect(actions.has?(:delete_and_ignore_replies)).to eq(false)
|
||||
expect(actions.has?(:delete_and_agree)).to eq(true)
|
||||
|
@ -67,7 +67,6 @@ RSpec.describe ReviewableFlaggedPost, type: :model do
|
|||
it "returns delete replies options if there are replies" do
|
||||
post.update(reply_count: 3)
|
||||
expect(reviewable.actions_for(guardian).has?(:delete_and_agree_replies)).to eq(true)
|
||||
expect(reviewable.actions_for(guardian).has?(:delete_and_ignore_replies)).to eq(true)
|
||||
end
|
||||
|
||||
it "returns appropriate actions for a hidden post" do
|
||||
|
@ -168,7 +167,7 @@ RSpec.describe ReviewableFlaggedPost, type: :model do
|
|||
end
|
||||
|
||||
it "ignores the flags" do
|
||||
reviewable.perform(moderator, :ignore)
|
||||
reviewable.perform(moderator, :ignore_and_do_nothing)
|
||||
expect(reviewable).to be_ignored
|
||||
expect(score.reload).to be_ignored
|
||||
end
|
||||
|
@ -273,7 +272,7 @@ RSpec.describe ReviewableFlaggedPost, type: :model do
|
|||
expect(post.hidden).to eq(false)
|
||||
expect(post.hidden_at).to be_blank
|
||||
|
||||
reviewable.perform(moderator, :ignore)
|
||||
reviewable.perform(moderator, :ignore_and_do_nothing)
|
||||
expect(pending_count).to eq(0)
|
||||
|
||||
post.reload
|
||||
|
@ -340,6 +339,11 @@ RSpec.describe ReviewableFlaggedPost, type: :model do
|
|||
it "ignores flagged responses" do
|
||||
SiteSetting.notify_users_after_responses_deleted_on_flagged_post = true
|
||||
flagged_reply = Fabricate(:reviewable_flagged_post, target: reply)
|
||||
Fabricate(
|
||||
:post,
|
||||
reply_to_post_number: flagged_reply.target.post_number,
|
||||
topic: flagged_reply.target.topic,
|
||||
)
|
||||
flagged_post.perform(moderator, :delete_and_agree_replies)
|
||||
|
||||
expect(flagged_reply.reload).to be_ignored
|
||||
|
@ -366,7 +370,7 @@ RSpec.describe ReviewableFlaggedPost, type: :model do
|
|||
let(:reviewable) { Fabricate(:reviewable_flagged_post, score: expected_score) }
|
||||
|
||||
it "doesn't recalculate the score after ignore" do
|
||||
reviewable.perform(moderator, :ignore)
|
||||
reviewable.perform(moderator, :ignore_and_do_nothing)
|
||||
|
||||
expect(reviewable.score).to eq(expected_score)
|
||||
end
|
||||
|
|
|
@ -173,7 +173,7 @@ RSpec.describe Reviewable, type: :model do
|
|||
it "can filter by who reviewed the flag" do
|
||||
reviewable = Fabricate(:reviewable_flagged_post)
|
||||
admin = Fabricate(:admin)
|
||||
reviewable.perform(admin, :ignore)
|
||||
reviewable.perform(admin, :ignore_and_do_nothing)
|
||||
|
||||
reviewables = Reviewable.list_for(user, status: :all, reviewed_by: admin.username)
|
||||
|
||||
|
@ -438,7 +438,7 @@ RSpec.describe Reviewable, type: :model do
|
|||
|
||||
it "increases flags_ignored when ignored" do
|
||||
expect(user.user_stat.flags_ignored).to eq(0)
|
||||
reviewable.perform(Discourse.system_user, :ignore)
|
||||
reviewable.perform(Discourse.system_user, :ignore_and_do_nothing)
|
||||
expect(user.user_stat.reload.flags_ignored).to eq(1)
|
||||
end
|
||||
|
||||
|
|
|
@ -1955,7 +1955,10 @@ RSpec.describe User do
|
|||
.perform(moderator, :agree_and_keep)
|
||||
|
||||
post_deferred = Fabricate(:post)
|
||||
PostActionCreator.inappropriate(user, post_deferred).reviewable.perform(moderator, :ignore)
|
||||
PostActionCreator
|
||||
.inappropriate(user, post_deferred)
|
||||
.reviewable
|
||||
.perform(moderator, :ignore_and_do_nothing)
|
||||
|
||||
post_disagreed = Fabricate(:post)
|
||||
PostActionCreator
|
||||
|
|
|
@ -2268,7 +2268,7 @@ RSpec.describe PostsController do
|
|||
r2 = PostActionCreator.inappropriate(moderator, post_disagreed).reviewable
|
||||
|
||||
r0.perform(admin, :agree_and_keep)
|
||||
r1.perform(admin, :ignore)
|
||||
r1.perform(admin, :ignore_and_do_nothing)
|
||||
r2.perform(admin, :disagree)
|
||||
|
||||
sign_in(Fabricate(:moderator))
|
||||
|
|
Loading…
Reference in New Issue