mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 11:44:49 +08:00
FEATURE: Flag count in post menu
This change shows a notification number besides the flag icon in the post menu if there is reviewable content associated with the post. Additionally, if there is pending stuff to review, the icon has a red background. We have also removed the list of links below a post with the flag status. A reviewer is meant to click the number beside the flag icon to view the flags. As a consequence of losing those links, we've removed the ability to undo or ignore flags below a post.
This commit is contained in:
parent
e6843afa9e
commit
31e100530f
|
@ -1,19 +1,5 @@
|
|||
import { userPath } from "discourse/lib/url";
|
||||
|
||||
function actionDescription(action, acted, count) {
|
||||
if (acted) {
|
||||
if (count <= 1) {
|
||||
return I18n.t(`post.actions.by_you.${action}`);
|
||||
} else {
|
||||
return I18n.t(`post.actions.by_you_and_others.${action}`, {
|
||||
count: count - 1
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return I18n.t(`post.actions.by_others.${action}`, { count });
|
||||
}
|
||||
}
|
||||
|
||||
const _additionalAttributes = [];
|
||||
|
||||
export function includeAttributes(...attributes) {
|
||||
|
@ -62,6 +48,10 @@ export function transformBasicPost(post) {
|
|||
canRecover: post.can_recover,
|
||||
canEdit: post.can_edit,
|
||||
canFlag: !Ember.isEmpty(post.get("flagsAvailable")),
|
||||
canReviewTopic: false,
|
||||
reviewableId: post.reviewable_id,
|
||||
reviewableScoreCount: post.reviewable_score_count,
|
||||
reviewableScorePendingCount: post.reviewable_score_pending_count,
|
||||
version: post.version,
|
||||
canRecoverTopic: false,
|
||||
canDeletedTopic: false,
|
||||
|
@ -121,6 +111,7 @@ export default function transformPost(
|
|||
postAtts.canViewRawEmail =
|
||||
currentUser && (currentUser.id === post.user_id || currentUser.staff);
|
||||
postAtts.canReplyAsNewTopic = details.can_reply_as_new_topic;
|
||||
postAtts.canReviewTopic = !!details.can_review_topic;
|
||||
postAtts.isWarning = topic.is_warning;
|
||||
postAtts.links = post.get("internalLinks");
|
||||
postAtts.replyDirectlyBelow =
|
||||
|
@ -208,22 +199,17 @@ export default function transformPost(
|
|||
if (post.actions_summary) {
|
||||
postAtts.actionsSummary = post.actions_summary
|
||||
.filter(a => {
|
||||
return a.actionType.name_key !== "like" && a.count > 0;
|
||||
return a.actionType.name_key !== "like" && a.acted;
|
||||
})
|
||||
.map(a => {
|
||||
const acted = a.acted;
|
||||
const action = a.actionType.name_key;
|
||||
const count = a.count;
|
||||
|
||||
return {
|
||||
id: a.id,
|
||||
postId: post.id,
|
||||
action,
|
||||
acted,
|
||||
count,
|
||||
canUndo: a.can_undo,
|
||||
canIgnoreFlags: a.can_defer_flags,
|
||||
description: actionDescription(action, acted, count)
|
||||
description: I18n.t(`post.actions.by_you.${action}`)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -33,8 +33,6 @@ export default RestModel.extend({
|
|||
act(post, opts) {
|
||||
if (!opts) opts = {};
|
||||
|
||||
const action = this.get("actionType.name_key");
|
||||
|
||||
// Mark it as acted
|
||||
this.setProperties({
|
||||
acted: true,
|
||||
|
@ -43,13 +41,7 @@ export default RestModel.extend({
|
|||
can_undo: true
|
||||
});
|
||||
|
||||
if (action === "notify_moderators" || action === "notify_user") {
|
||||
this.set("can_undo", false);
|
||||
this.set("can_defer_flags", false);
|
||||
}
|
||||
|
||||
// Create our post action
|
||||
const self = this;
|
||||
return ajax("/post_actions", {
|
||||
type: "POST",
|
||||
data: {
|
||||
|
@ -62,8 +54,8 @@ export default RestModel.extend({
|
|||
},
|
||||
returnXHR: true
|
||||
})
|
||||
.then(function(data) {
|
||||
if (!self.get("flagTopic")) {
|
||||
.then(data => {
|
||||
if (!this.get("flagTopic")) {
|
||||
post.updateActionsSummary(data.result);
|
||||
}
|
||||
const remaining = parseInt(
|
||||
|
@ -74,9 +66,9 @@ export default RestModel.extend({
|
|||
);
|
||||
return { acted: true, remaining, max };
|
||||
})
|
||||
.catch(function(error) {
|
||||
.catch(error => {
|
||||
popupAjaxError(error);
|
||||
self.removeAction(post);
|
||||
this.removeAction(post);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -92,12 +84,5 @@ export default RestModel.extend({
|
|||
post.updateActionsSummary(result);
|
||||
return { acted: false };
|
||||
});
|
||||
},
|
||||
|
||||
deferFlags(post) {
|
||||
return ajax("/post_actions/defer_flags", {
|
||||
type: "POST",
|
||||
data: { post_action_type_id: this.get("id"), id: post.get("id") }
|
||||
}).then(() => this.set("count", 0));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -74,57 +74,11 @@ createWidget("action-link", {
|
|||
}
|
||||
});
|
||||
|
||||
createWidget("actions-summary-item", {
|
||||
tagName: "div.post-action",
|
||||
buildKey: attrs => `actions-summary-item-${attrs.id}`,
|
||||
|
||||
defaultState() {
|
||||
return { users: null };
|
||||
},
|
||||
|
||||
template: hbs`
|
||||
{{#if state.users}}
|
||||
{{small-user-list users=state.users description=(concat "post.actions.people." attrs.action)}}
|
||||
{{else}}
|
||||
{{action-link action="whoActed" text=attrs.description}}
|
||||
{{/if}}
|
||||
|
||||
{{#if attrs.canUndo}}
|
||||
{{action-link action="undo" className="undo" text=(i18n (concat "post.actions.undo." attrs.action))}}
|
||||
{{/if}}
|
||||
|
||||
{{#if attrs.canIgnoreFlags}}
|
||||
{{action-link action="deferFlags" className="defer-flags" text=(i18n "post.actions.defer_flags" count=attrs.count)}}
|
||||
{{/if}}
|
||||
`,
|
||||
|
||||
whoActed() {
|
||||
const attrs = this.attrs;
|
||||
const state = this.state;
|
||||
return this.store
|
||||
.find("post-action-user", {
|
||||
id: attrs.postId,
|
||||
post_action_type_id: attrs.id
|
||||
})
|
||||
.then(users => {
|
||||
state.users = users.map(avatarAtts);
|
||||
});
|
||||
},
|
||||
|
||||
undo() {
|
||||
this.sendWidgetAction("undoPostAction", this.attrs.id);
|
||||
},
|
||||
|
||||
deferFlags() {
|
||||
this.sendWidgetAction("deferPostActionFlags", this.attrs.id);
|
||||
}
|
||||
});
|
||||
|
||||
export default createWidget("actions-summary", {
|
||||
tagName: "section.post-actions",
|
||||
template: hbs`
|
||||
{{#each attrs.actionsSummary as |as|}}
|
||||
{{actions-summary-item attrs=as}}
|
||||
<div class='post-action'>{{as.description}}</div>
|
||||
<div class='clearfix'></div>
|
||||
{{/each}}
|
||||
{{#if attrs.deleted_at}}
|
||||
|
|
|
@ -64,7 +64,8 @@ registerButton("like", attrs => {
|
|||
const button = {
|
||||
action: "like",
|
||||
icon: attrs.liked ? "d-liked" : "d-unliked",
|
||||
className
|
||||
className,
|
||||
before: "like-count"
|
||||
};
|
||||
|
||||
// If the user has already liked the post and doesn't have permission
|
||||
|
@ -99,7 +100,7 @@ registerButton("like-count", attrs => {
|
|||
return {
|
||||
action: "toggleWhoLiked",
|
||||
title,
|
||||
className: `like-count highlight-action ${additionalClass}`,
|
||||
className: `button-count like-count highlight-action ${additionalClass}`,
|
||||
contents: count,
|
||||
icon,
|
||||
iconRight: true,
|
||||
|
@ -108,14 +109,30 @@ registerButton("like-count", attrs => {
|
|||
}
|
||||
});
|
||||
|
||||
registerButton("flag-count", attrs => {
|
||||
let className = "button-count";
|
||||
if (attrs.reviewableScorePendingCount > 0) {
|
||||
className += " has-pending";
|
||||
}
|
||||
return {
|
||||
className,
|
||||
contents: h("span", attrs.reviewableScoreCount.toString()),
|
||||
url: `/review/${attrs.reviewableId}`
|
||||
};
|
||||
});
|
||||
|
||||
registerButton("flag", attrs => {
|
||||
if (attrs.canFlag && !attrs.hidden) {
|
||||
return {
|
||||
if (attrs.reviewableId || (attrs.canFlag && !attrs.hidden)) {
|
||||
let button = {
|
||||
action: "showFlags",
|
||||
title: "post.controls.flag",
|
||||
icon: "flag",
|
||||
className: "create-flag"
|
||||
};
|
||||
if (attrs.reviewableId) {
|
||||
button.before = "flag-count";
|
||||
}
|
||||
return button;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -323,7 +340,12 @@ export default createWidget("post-menu", {
|
|||
attachButton(name) {
|
||||
let buttonAtts = buildButton(name, this);
|
||||
if (buttonAtts) {
|
||||
return this.attach(this.settings.buttonType, buttonAtts);
|
||||
let button = this.attach(this.settings.buttonType, buttonAtts);
|
||||
if (buttonAtts.before) {
|
||||
let before = this.attachButton(buttonAtts.before);
|
||||
return h("div.double-button", [before, button]);
|
||||
}
|
||||
return button;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -359,22 +381,21 @@ export default createWidget("post-menu", {
|
|||
replaceButton(orderedButtons, "reply", "wiki-edit");
|
||||
}
|
||||
|
||||
orderedButtons
|
||||
.filter(x => x !== "like-count" && x !== "like")
|
||||
.forEach(i => {
|
||||
const button = this.attachButton(i, attrs);
|
||||
orderedButtons.forEach(i => {
|
||||
const button = this.attachButton(i, attrs);
|
||||
|
||||
if (button) {
|
||||
allButtons.push(button);
|
||||
if (button) {
|
||||
allButtons.push(button);
|
||||
|
||||
if (
|
||||
(attrs.yours && button.attrs.alwaysShowYours) ||
|
||||
hiddenButtons.indexOf(i) === -1
|
||||
) {
|
||||
visibleButtons.push(button);
|
||||
}
|
||||
if (
|
||||
(attrs.yours && button.attrs && button.attrs.alwaysShowYours) ||
|
||||
(attrs.reviewableId && i === "flag") ||
|
||||
hiddenButtons.indexOf(i) === -1
|
||||
) {
|
||||
visibleButtons.push(button);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.settings.collapseButtons) {
|
||||
visibleButtons = allButtons;
|
||||
|
@ -397,13 +418,6 @@ export default createWidget("post-menu", {
|
|||
visibleButtons.splice(visibleButtons.length - 1, 0, showMore);
|
||||
}
|
||||
|
||||
visibleButtons.unshift(
|
||||
h("div.like-button", [
|
||||
this.attachButton("like-count", attrs),
|
||||
this.attachButton("like", attrs)
|
||||
])
|
||||
);
|
||||
|
||||
Object.values(_extraButtons).forEach(builder => {
|
||||
if (builder) {
|
||||
const buttonAtts = builder(attrs, this.state, this.siteSettings);
|
||||
|
|
|
@ -711,21 +711,5 @@ export default createWidget("post", {
|
|||
bootbox.alert(I18n.t("post.few_likes_left"));
|
||||
kvs.set({ key: "lastWarnedLikes", value: new Date().getTime() });
|
||||
}
|
||||
},
|
||||
|
||||
undoPostAction(typeId) {
|
||||
const post = this.model;
|
||||
return post
|
||||
.get("actions_summary")
|
||||
.findBy("id", typeId)
|
||||
.undo(post);
|
||||
},
|
||||
|
||||
deferPostActionFlags(typeId) {
|
||||
const post = this.model;
|
||||
return post
|
||||
.get("actions_summary")
|
||||
.findBy("id", typeId)
|
||||
.deferFlags(post);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
.button-count.has-pending {
|
||||
span {
|
||||
background-color: $danger;
|
||||
color: $secondary;
|
||||
border-radius: 10px;
|
||||
padding: 0.25em 0.5em;
|
||||
display: inline-block;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder-avatar {
|
||||
display: inline-block;
|
||||
width: 45px;
|
||||
|
@ -243,6 +254,7 @@ blockquote {
|
|||
}
|
||||
|
||||
.post-action {
|
||||
color: $primary-medium;
|
||||
.undo-action,
|
||||
.act-action {
|
||||
margin-left: 5px;
|
||||
|
|
|
@ -46,19 +46,23 @@ section.post-menu-area {
|
|||
|
||||
nav.post-controls {
|
||||
padding: 0;
|
||||
.like-button {
|
||||
// Like button wrapper
|
||||
|
||||
// Some buttons can be doubled up, like likes or flags
|
||||
.double-button {
|
||||
display: inline-flex;
|
||||
color: $primary-low-mid;
|
||||
margin-right: 0.15em;
|
||||
&:hover {
|
||||
// Like button wrapper on hover
|
||||
button {
|
||||
background: $primary-low;
|
||||
color: $primary-medium;
|
||||
}
|
||||
}
|
||||
button {
|
||||
// It looks really confusing when one half a double button has an inner shadow on click.
|
||||
&:active {
|
||||
box-shadow: none;
|
||||
}
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
&.my-likes {
|
||||
|
@ -93,7 +97,7 @@ nav.post-controls {
|
|||
// Disabled like button
|
||||
cursor: not-allowed;
|
||||
}
|
||||
&.like-count {
|
||||
&.button-count {
|
||||
// Like count button
|
||||
&:not(.my-likes) {
|
||||
padding-right: 0;
|
||||
|
|
|
@ -49,16 +49,6 @@ class PostActionsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def defer_flags
|
||||
guardian.ensure_can_defer_flags!(@post)
|
||||
|
||||
if reviewable = @post.reviewable_flag
|
||||
reviewable.perform(current_user, :ignore)
|
||||
end
|
||||
|
||||
render json: { success: true }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_post_from_params
|
||||
|
|
|
@ -470,10 +470,6 @@ class Post < ActiveRecord::Base
|
|||
post_actions.where(post_action_type_id: PostActionType.flag_types_without_custom.values, deleted_at: nil).count != 0
|
||||
end
|
||||
|
||||
def active_flags
|
||||
post_actions.active.where(post_action_type_id: PostActionType.flag_types_without_custom.values)
|
||||
end
|
||||
|
||||
def reviewable_flag
|
||||
ReviewableFlaggedPost.pending.find_by(target: self)
|
||||
end
|
||||
|
|
|
@ -73,7 +73,10 @@ class PostSerializer < BasicPostSerializer
|
|||
:notice_args,
|
||||
:last_wiki_edit,
|
||||
:locked,
|
||||
:excerpt
|
||||
:excerpt,
|
||||
:reviewable_id,
|
||||
:reviewable_score_count,
|
||||
:reviewable_score_pending_count
|
||||
|
||||
def initialize(object, opts)
|
||||
super(object, opts)
|
||||
|
@ -251,14 +254,6 @@ class PostSerializer < BasicPostSerializer
|
|||
summary.delete(:can_act)
|
||||
end
|
||||
|
||||
# The following only applies if you're logged in
|
||||
if summary[:can_act] && scope.current_user.present?
|
||||
summary[:can_defer_flags] = true if scope.is_staff? &&
|
||||
PostActionType.flag_types_without_custom.values.include?(id) &&
|
||||
active_flags.present? && active_flags.has_key?(id) &&
|
||||
active_flags[id] > 0
|
||||
end
|
||||
|
||||
if actions.present? && actions.has_key?(id)
|
||||
summary[:acted] = true
|
||||
summary[:can_undo] = true if scope.can_delete?(actions[id])
|
||||
|
@ -416,7 +411,62 @@ class PostSerializer < BasicPostSerializer
|
|||
object.hidden
|
||||
end
|
||||
|
||||
private
|
||||
# If we have a topic view, it has bulk values for the reviewable content we can use
|
||||
def reviewable_id
|
||||
if @topic_view.present?
|
||||
for_post = @topic_view.reviewable_counts[object.id]
|
||||
return for_post ? for_post[:reviewable_id] : 0
|
||||
end
|
||||
|
||||
reviewable&.id
|
||||
end
|
||||
|
||||
def include_reviewable_id?
|
||||
can_review_topic?
|
||||
end
|
||||
|
||||
def reviewable_score_count
|
||||
if @topic_view.present?
|
||||
for_post = @topic_view.reviewable_counts[object.id]
|
||||
return for_post ? for_post[:total] : 0
|
||||
end
|
||||
|
||||
reviewable_scores.size
|
||||
end
|
||||
|
||||
def include_reviewable_score_count?
|
||||
can_review_topic?
|
||||
end
|
||||
|
||||
def reviewable_score_pending_count
|
||||
if @topic_view.present?
|
||||
for_post = @topic_view.reviewable_counts[object.id]
|
||||
return for_post ? for_post[:pending] : 0
|
||||
end
|
||||
|
||||
reviewable_scores.count { |rs| rs.pending? }
|
||||
end
|
||||
|
||||
def include_reviewable_score_pending_count?
|
||||
can_review_topic?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_review_topic?
|
||||
return @can_review_topic unless @can_review_topic.nil?
|
||||
@can_review_topic = @topic_view&.can_review_topic
|
||||
@can_review_topic ||= scope.can_review_topic?(object.topic)
|
||||
@can_review_topic
|
||||
end
|
||||
|
||||
def reviewable
|
||||
@reviewable ||= Reviewable.where(target: object).includes(:reviewable_scores).first
|
||||
end
|
||||
|
||||
def reviewable_scores
|
||||
reviewable&.reviewable_scores&.to_a || []
|
||||
end
|
||||
|
||||
def user_custom_fields_object
|
||||
(@topic_view&.user_custom_fields || @options[:user_custom_fields] || {})
|
||||
|
@ -432,10 +482,6 @@ class PostSerializer < BasicPostSerializer
|
|||
@post_actions ||= (@topic_view&.all_post_actions || {})[object.id]
|
||||
end
|
||||
|
||||
def active_flags
|
||||
@active_flags ||= (@topic_view&.all_active_flags || {})[object.id]
|
||||
end
|
||||
|
||||
def post_custom_fields
|
||||
@post_custom_fields ||= if @topic_view
|
||||
(@topic_view.post_custom_fields || {})[object.id] || {}
|
||||
|
|
|
@ -11,7 +11,8 @@ class TopicViewDetailsSerializer < ApplicationSerializer
|
|||
:can_create_post,
|
||||
:can_reply_as_new_topic,
|
||||
:can_flag_topic,
|
||||
:can_convert_topic]
|
||||
:can_convert_topic,
|
||||
:can_review_topic]
|
||||
end
|
||||
|
||||
attributes(
|
||||
|
@ -77,6 +78,10 @@ class TopicViewDetailsSerializer < ApplicationSerializer
|
|||
define_method(ca) { true }
|
||||
end
|
||||
|
||||
def include_can_review_topic?
|
||||
scope.can_review_topic?(object.topic)
|
||||
end
|
||||
|
||||
def include_can_move_posts?
|
||||
scope.can_move_posts?(object.topic)
|
||||
end
|
||||
|
|
|
@ -190,10 +190,9 @@ basic:
|
|||
post_menu:
|
||||
client: true
|
||||
type: list
|
||||
default: "like-count|like|share|flag|edit|bookmark|delete|admin|reply"
|
||||
default: "like|share|flag|edit|bookmark|delete|admin|reply"
|
||||
allow_any: false
|
||||
choices:
|
||||
- like-count
|
||||
- like
|
||||
- edit
|
||||
- flag
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
class RemoveLikeCountFromPostMenu < ActiveRecord::Migration[5.2]
|
||||
def up
|
||||
execute(<<~SQL)
|
||||
UPDATE site_settings
|
||||
SET value = REGEXP_REPLACE(REPLACE(REPLACE(value, 'like-count', ''), '||', '|'), '^\\|', '')
|
||||
WHERE name = 'post_menu'
|
||||
SQL
|
||||
end
|
||||
end
|
|
@ -79,10 +79,6 @@ module PostGuardian
|
|||
can_see_post?(post) && is_staff?
|
||||
end
|
||||
|
||||
def can_defer_flags?(post)
|
||||
can_see_post?(post) && is_staff? && post
|
||||
end
|
||||
|
||||
# Can we see who acted on a post in a particular way?
|
||||
def can_see_post_actors?(topic, post_action_type_id)
|
||||
return true if is_admin?
|
||||
|
|
|
@ -10,6 +10,16 @@ module TopicGuardian
|
|||
)
|
||||
end
|
||||
|
||||
def can_review_topic?(topic)
|
||||
return false if anonymous? || topic.nil?
|
||||
return true if is_staff?
|
||||
|
||||
SiteSetting.enable_category_group_review? &&
|
||||
topic.category.present? &&
|
||||
topic.category.reviewable_by_group_id.present? &&
|
||||
GroupUser.where(group_id: topic.category.reviewable_by_group_id, user_id: user.id).exists?
|
||||
end
|
||||
|
||||
def can_create_shared_draft?
|
||||
is_staff? && SiteSetting.shared_drafts_enabled?
|
||||
end
|
||||
|
|
|
@ -15,7 +15,8 @@ class TopicView
|
|||
:print,
|
||||
:message_bus_last_id,
|
||||
:queued_posts_enabled,
|
||||
:personal_message
|
||||
:personal_message,
|
||||
:can_review_topic
|
||||
)
|
||||
|
||||
attr_accessor(
|
||||
|
@ -100,6 +101,7 @@ class TopicView
|
|||
@draft_key = @topic.draft_key
|
||||
@draft_sequence = DraftSequence.current(@user, @draft_key)
|
||||
|
||||
@can_review_topic = @guardian.can_review_topic?(@topic)
|
||||
@queued_posts_enabled = NewPostManager.queue_enabled?
|
||||
@personal_message = @topic.private_message?
|
||||
end
|
||||
|
@ -410,16 +412,32 @@ class TopicView
|
|||
@all_post_actions ||= PostAction.counts_for(@posts, @user)
|
||||
end
|
||||
|
||||
def all_active_flags
|
||||
@all_active_flags ||= ReviewableFlaggedPost.counts_for(@posts)
|
||||
end
|
||||
|
||||
def links
|
||||
@links ||= TopicLink.topic_map(@guardian, @topic.id)
|
||||
end
|
||||
|
||||
def reviewable_counts
|
||||
if @reviewable_counts.blank?
|
||||
|
||||
# Create a hash with counts by post so we can quickly look up whether there is reviewable content.
|
||||
@reviewable_counts = {}
|
||||
Reviewable.
|
||||
where(target_type: 'Post', target_id: filtered_post_ids).
|
||||
includes(:reviewable_scores).each do |r|
|
||||
|
||||
for_post = (@reviewable_counts[r.target_id] ||= { total: 0, pending: 0, reviewable_id: r.id })
|
||||
r.reviewable_scores.each do |s|
|
||||
for_post[:total] += 1
|
||||
for_post[:pending] += 1 if s.pending?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@reviewable_counts
|
||||
end
|
||||
|
||||
def pending_posts
|
||||
ReviewableQueuedPost.pending.where(created_by: @user, topic: @topic).order(:created_at)
|
||||
@pending_posts ||= ReviewableQueuedPost.pending.where(created_by: @user, topic: @topic).order(:created_at)
|
||||
end
|
||||
|
||||
def actions_summary
|
||||
|
|
|
@ -249,29 +249,6 @@ describe Guardian do
|
|||
end
|
||||
end
|
||||
|
||||
describe "can_defer_flags" do
|
||||
let(:post) { Fabricate(:post) }
|
||||
let(:user) { post.user }
|
||||
let(:moderator) { Fabricate(:moderator) }
|
||||
|
||||
it "returns false when the user is nil" do
|
||||
expect(Guardian.new(nil).can_defer_flags?(post)).to be_falsey
|
||||
end
|
||||
|
||||
it "returns false when the post is nil" do
|
||||
expect(Guardian.new(moderator).can_defer_flags?(nil)).to be_falsey
|
||||
end
|
||||
|
||||
it "returns false when the user is not a moderator" do
|
||||
expect(Guardian.new(user).can_defer_flags?(post)).to be_falsey
|
||||
end
|
||||
|
||||
it "returns true when the user is a moderator" do
|
||||
expect(Guardian.new(moderator).can_defer_flags?(post)).to be_truthy
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'can_send_private_message' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:another_user) { Fabricate(:user) }
|
||||
|
@ -1672,6 +1649,28 @@ describe Guardian do
|
|||
end
|
||||
end
|
||||
|
||||
context "can_review_topic?" do
|
||||
it 'returns false with a nil object' do
|
||||
expect(Guardian.new(user).can_review_topic?(nil)).to eq(false)
|
||||
end
|
||||
|
||||
it 'returns true for a staff user' do
|
||||
expect(Guardian.new(moderator).can_review_topic?(topic)).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns false for a regular user' do
|
||||
expect(Guardian.new(user).can_review_topic?(topic)).to eq(false)
|
||||
end
|
||||
|
||||
it 'returns false for a regular user' do
|
||||
SiteSetting.enable_category_group_review = true
|
||||
group = Fabricate(:group)
|
||||
GroupUser.create!(group_id: group.id, user_id: user.id)
|
||||
topic.category.update!(reviewable_by_group_id: group.id)
|
||||
expect(Guardian.new(user).can_review_topic?(topic)).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'can_move_posts?' do
|
||||
|
||||
it 'returns false with a nil object' do
|
||||
|
|
|
@ -307,28 +307,6 @@ describe TopicView do
|
|||
end
|
||||
end
|
||||
|
||||
context '.all_active_flags' do
|
||||
it 'is blank at first' do
|
||||
expect(topic_view.all_active_flags).to be_blank
|
||||
end
|
||||
|
||||
it 'returns the active flags' do
|
||||
PostActionCreator.off_topic(moderator, p1)
|
||||
PostActionCreator.off_topic(evil_trout, p1)
|
||||
|
||||
expect(topic_view.all_active_flags[p1.id][PostActionType.types[:off_topic]]).to eq(2)
|
||||
end
|
||||
|
||||
it 'returns only the active flags' do
|
||||
reviewable = PostActionCreator.off_topic(moderator, p1).reviewable
|
||||
PostActionCreator.off_topic(evil_trout, p1)
|
||||
|
||||
reviewable.perform(moderator, :ignore)
|
||||
|
||||
expect(topic_view.all_active_flags[p1.id]).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context '.read?' do
|
||||
it 'tracks correctly' do
|
||||
# anon is assumed to have read everything
|
||||
|
|
|
@ -261,62 +261,4 @@ RSpec.describe PostActionsController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#defer_flags' do
|
||||
let(:flagged_post) { Fabricate(:post, user: Fabricate(:coding_horror)) }
|
||||
let!(:reviewable) do
|
||||
PostActionCreator.spam(Fabricate(:user), flagged_post).reviewable
|
||||
end
|
||||
|
||||
context "not logged in" do
|
||||
it "should not allow them to clear flags" do
|
||||
post "/post_actions/defer_flags.json", params: { id: flagged_post.id }
|
||||
expect(response.status).to eq(403)
|
||||
expect(reviewable.reload).not_to be_ignored
|
||||
end
|
||||
end
|
||||
|
||||
context 'logged in' do
|
||||
let!(:user) { sign_in(Fabricate(:moderator)) }
|
||||
|
||||
it "raises an error without a post_action_type_id" do
|
||||
post "/post_actions/defer_flags.json", params: { id: flagged_post.id }
|
||||
expect(response.status).to eq(400)
|
||||
expect(reviewable.reload).not_to be_ignored
|
||||
end
|
||||
|
||||
it "raises an error when the user doesn't have access" do
|
||||
sign_in(Fabricate(:user))
|
||||
|
||||
post "/post_actions/defer_flags.json", params: {
|
||||
id: flagged_post.id, post_action_type_id: PostActionType.types[:spam]
|
||||
}
|
||||
|
||||
expect(response).to be_forbidden
|
||||
expect(reviewable.reload).not_to be_ignored
|
||||
end
|
||||
|
||||
context "success" do
|
||||
it "performs the ignore" do
|
||||
post "/post_actions/defer_flags.json", params: {
|
||||
id: flagged_post.id, post_action_type_id: PostActionType.types[:spam]
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(reviewable.reload).to be_ignored
|
||||
end
|
||||
|
||||
it "works with a deleted post" do
|
||||
flagged_post.trash!(user)
|
||||
|
||||
post "/post_actions/defer_flags.json", params: {
|
||||
id: flagged_post.id, post_action_type_id: PostActionType.types[:spam]
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(reviewable.reload).to be_ignored
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,6 +44,18 @@ describe PostSerializer do
|
|||
end
|
||||
end
|
||||
|
||||
context "a post with reviewable content" do
|
||||
let!(:post) { Fabricate(:post, user: Fabricate(:user)) }
|
||||
let!(:reviewable) { PostActionCreator.spam(Fabricate(:user), post).reviewable }
|
||||
|
||||
it "includes the reviewable data" do
|
||||
json = PostSerializer.new(post, scope: Guardian.new(Fabricate(:moderator)), root: false).as_json
|
||||
expect(json[:reviewable_id]).to eq(reviewable.id)
|
||||
expect(json[:reviewable_score_count]).to eq(1)
|
||||
expect(json[:reviewable_score_pending_count]).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context "a post by a nuked user" do
|
||||
let!(:post) { Fabricate(:post, user: Fabricate(:user), deleted_at: Time.zone.now) }
|
||||
|
||||
|
|
|
@ -134,6 +134,27 @@ describe TopicViewSerializer do
|
|||
end
|
||||
end
|
||||
|
||||
context "with flags" do
|
||||
let!(:post) { Fabricate(:post, topic: topic) }
|
||||
let!(:other_post) { Fabricate(:post, topic: topic) }
|
||||
|
||||
it "will return reviewable counts on posts" do
|
||||
r = PostActionCreator.inappropriate(Fabricate(:user), post).reviewable
|
||||
r.perform(admin, :agree_and_keep)
|
||||
PostActionCreator.spam(Fabricate(:user), post)
|
||||
|
||||
json = serialize_topic(topic, admin)
|
||||
p0 = json[:post_stream][:posts][0]
|
||||
expect(p0[:id]).to eq(post.id)
|
||||
expect(p0[:reviewable_score_count]).to eq(2)
|
||||
expect(p0[:reviewable_score_pending_count]).to eq(1)
|
||||
|
||||
p1 = json[:post_stream][:posts][1]
|
||||
expect(p1[:reviewable_score_count]).to eq(0)
|
||||
expect(p1[:reviewable_score_pending_count]).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe "pending posts" do
|
||||
context "when the queue is enabled" do
|
||||
before do
|
||||
|
@ -185,6 +206,7 @@ describe TopicViewSerializer do
|
|||
expect(details[:notification_level]).to be_present
|
||||
expect(details[:can_move_posts]).to eq(true)
|
||||
expect(details[:can_flag_topic]).to eq(true)
|
||||
expect(details[:can_review_topic]).to eq(true)
|
||||
expect(details[:links][0][:clicks]).to eq(100)
|
||||
|
||||
participant = details[:participants].find { |p| p[:id] == user.id }
|
||||
|
|
|
@ -31,13 +31,13 @@ export default {
|
|||
raw:
|
||||
"Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?",
|
||||
actions_summary: [
|
||||
{ id: 2, count: 0, hidden: false, can_act: true, can_defer_flags: false },
|
||||
{ id: 3, count: 0, hidden: false, can_act: true, can_defer_flags: false },
|
||||
{ id: 4, count: 0, hidden: false, can_act: true, can_defer_flags: false },
|
||||
{ id: 5, count: 0, hidden: true, can_act: true, can_defer_flags: false },
|
||||
{ id: 6, count: 0, hidden: false, can_act: true, can_defer_flags: false },
|
||||
{ id: 7, count: 0, hidden: false, can_act: true, can_defer_flags: false },
|
||||
{ id: 8, count: 0, hidden: false, can_act: true, can_defer_flags: false }
|
||||
{ id: 2, count: 0, hidden: false, can_act: true },
|
||||
{ id: 3, count: 0, hidden: false, can_act: true },
|
||||
{ id: 4, count: 0, hidden: false, can_act: true },
|
||||
{ id: 5, count: 0, hidden: true, can_act: true },
|
||||
{ id: 6, count: 0, hidden: false, can_act: true },
|
||||
{ id: 7, count: 0, hidden: false, can_act: true },
|
||||
{ id: 8, count: 0, hidden: false, can_act: true }
|
||||
],
|
||||
moderator: false,
|
||||
admin: false,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,73 +2,6 @@ import { moduleForWidget, widgetTest } from "helpers/widget-test";
|
|||
|
||||
moduleForWidget("actions-summary");
|
||||
|
||||
widgetTest("listing actions", {
|
||||
template: '{{mount-widget widget="actions-summary" args=args}}',
|
||||
beforeEach() {
|
||||
this.set("args", {
|
||||
actionsSummary: [
|
||||
{ id: 1, action: "off_topic", description: "very off topic" },
|
||||
{ id: 2, action: "spam", description: "suspicious message" }
|
||||
]
|
||||
});
|
||||
},
|
||||
async test(assert) {
|
||||
assert.equal(find(".post-actions .post-action").length, 2);
|
||||
|
||||
await click(".post-action:eq(0) .action-link a");
|
||||
assert.equal(
|
||||
find(".post-action:eq(0) img.avatar").length,
|
||||
1,
|
||||
"clicking it shows the user"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest("undo", {
|
||||
template:
|
||||
'{{mount-widget widget="actions-summary" args=args undoPostAction=undoPostAction}}',
|
||||
beforeEach() {
|
||||
this.set("args", {
|
||||
actionsSummary: [
|
||||
{ action: "off_topic", description: "very off topic", canUndo: true }
|
||||
]
|
||||
});
|
||||
|
||||
this.set("undoPostAction", () => (this.undid = true));
|
||||
},
|
||||
async test(assert) {
|
||||
assert.equal(find(".post-actions .post-action").length, 1);
|
||||
|
||||
await click(".action-link.undo");
|
||||
assert.ok(this.undid, "it triggered the action");
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest("deferFlags", {
|
||||
template:
|
||||
'{{mount-widget widget="actions-summary" args=args deferPostActionFlags=(action "deferPostActionFlags")}}',
|
||||
beforeEach() {
|
||||
this.set("args", {
|
||||
actionsSummary: [
|
||||
{
|
||||
action: "off_topic",
|
||||
description: "very off topic",
|
||||
canIgnoreFlags: true,
|
||||
count: 1
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
this.on("deferPostActionFlags", () => (this.deferred = true));
|
||||
},
|
||||
async test(assert) {
|
||||
assert.equal(find(".post-actions .post-action").length, 1);
|
||||
|
||||
await click(".action-link.defer-flags");
|
||||
assert.ok(this.deferred, "it triggered the action");
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest("post deleted", {
|
||||
template: '{{mount-widget widget="actions-summary" args=args}}',
|
||||
beforeEach() {
|
||||
|
|
Loading…
Reference in New Issue
Block a user