diff --git a/app/assets/javascripts/discourse/lib/transform-post.js.es6 b/app/assets/javascripts/discourse/lib/transform-post.js.es6 index 1aa662a8534..eccfb196e6b 100644 --- a/app/assets/javascripts/discourse/lib/transform-post.js.es6 +++ b/app/assets/javascripts/discourse/lib/transform-post.js.es6 @@ -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}`) }; }); } diff --git a/app/assets/javascripts/discourse/models/action-summary.js.es6 b/app/assets/javascripts/discourse/models/action-summary.js.es6 index 683f340be4f..1071f7ddf9b 100644 --- a/app/assets/javascripts/discourse/models/action-summary.js.es6 +++ b/app/assets/javascripts/discourse/models/action-summary.js.es6 @@ -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)); } }); diff --git a/app/assets/javascripts/discourse/widgets/actions-summary.js.es6 b/app/assets/javascripts/discourse/widgets/actions-summary.js.es6 index d15a3d7ba79..56c32a64ead 100644 --- a/app/assets/javascripts/discourse/widgets/actions-summary.js.es6 +++ b/app/assets/javascripts/discourse/widgets/actions-summary.js.es6 @@ -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}} +
{{as.description}}
{{/each}} {{#if attrs.deleted_at}} diff --git a/app/assets/javascripts/discourse/widgets/post-menu.js.es6 b/app/assets/javascripts/discourse/widgets/post-menu.js.es6 index bab75ab4d1f..ebb94fcf9a7 100644 --- a/app/assets/javascripts/discourse/widgets/post-menu.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post-menu.js.es6 @@ -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); diff --git a/app/assets/javascripts/discourse/widgets/post.js.es6 b/app/assets/javascripts/discourse/widgets/post.js.es6 index daa19ec9465..258e061868d 100644 --- a/app/assets/javascripts/discourse/widgets/post.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post.js.es6 @@ -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); } }); diff --git a/app/assets/stylesheets/common/base/topic-post.scss b/app/assets/stylesheets/common/base/topic-post.scss index b67441ab27d..dc3f1625dd0 100644 --- a/app/assets/stylesheets/common/base/topic-post.scss +++ b/app/assets/stylesheets/common/base/topic-post.scss @@ -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; diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index 196d51e9e94..6e8e1c70bc3 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -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; diff --git a/app/controllers/post_actions_controller.rb b/app/controllers/post_actions_controller.rb index e401856584a..f36249dbe72 100644 --- a/app/controllers/post_actions_controller.rb +++ b/app/controllers/post_actions_controller.rb @@ -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 diff --git a/app/models/post.rb b/app/models/post.rb index f51146ca8c3..8b98d039f72 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -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 diff --git a/app/serializers/post_serializer.rb b/app/serializers/post_serializer.rb index 2e059ac3b11..9ec373fbf3c 100644 --- a/app/serializers/post_serializer.rb +++ b/app/serializers/post_serializer.rb @@ -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] || {} diff --git a/app/serializers/topic_view_details_serializer.rb b/app/serializers/topic_view_details_serializer.rb index 8ad7a74e9a9..fd4697a3827 100644 --- a/app/serializers/topic_view_details_serializer.rb +++ b/app/serializers/topic_view_details_serializer.rb @@ -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 diff --git a/config/site_settings.yml b/config/site_settings.yml index ced98fe3244..b2a07af9761 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -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 diff --git a/db/migrate/20190503180839_remove_like_count_from_post_menu.rb b/db/migrate/20190503180839_remove_like_count_from_post_menu.rb new file mode 100644 index 00000000000..84b0d12bdbd --- /dev/null +++ b/db/migrate/20190503180839_remove_like_count_from_post_menu.rb @@ -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 diff --git a/lib/guardian/post_guardian.rb b/lib/guardian/post_guardian.rb index 545a7d28c51..a0931226417 100644 --- a/lib/guardian/post_guardian.rb +++ b/lib/guardian/post_guardian.rb @@ -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? diff --git a/lib/guardian/topic_guardian.rb b/lib/guardian/topic_guardian.rb index 8f3fea02d92..d6ed4159095 100644 --- a/lib/guardian/topic_guardian.rb +++ b/lib/guardian/topic_guardian.rb @@ -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 diff --git a/lib/topic_view.rb b/lib/topic_view.rb index 15807ef5cfa..5079a9f05a7 100644 --- a/lib/topic_view.rb +++ b/lib/topic_view.rb @@ -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 diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index eaf6d74f0b4..d5f7d7f1497 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -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 diff --git a/spec/components/topic_view_spec.rb b/spec/components/topic_view_spec.rb index 1d8635a8489..0b03d5361ff 100644 --- a/spec/components/topic_view_spec.rb +++ b/spec/components/topic_view_spec.rb @@ -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 diff --git a/spec/requests/post_actions_controller_spec.rb b/spec/requests/post_actions_controller_spec.rb index 8a514dd3de1..c187592c63e 100644 --- a/spec/requests/post_actions_controller_spec.rb +++ b/spec/requests/post_actions_controller_spec.rb @@ -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 diff --git a/spec/serializers/post_serializer_spec.rb b/spec/serializers/post_serializer_spec.rb index f0c76e8e03e..86176970f16 100644 --- a/spec/serializers/post_serializer_spec.rb +++ b/spec/serializers/post_serializer_spec.rb @@ -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) } diff --git a/spec/serializers/topic_view_serializer_spec.rb b/spec/serializers/topic_view_serializer_spec.rb index 436d9cb4c65..97def1a62f8 100644 --- a/spec/serializers/topic_view_serializer_spec.rb +++ b/spec/serializers/topic_view_serializer_spec.rb @@ -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 } diff --git a/test/javascripts/fixtures/post.js.es6 b/test/javascripts/fixtures/post.js.es6 index 736f4376d23..4cbed4acaea 100644 --- a/test/javascripts/fixtures/post.js.es6 +++ b/test/javascripts/fixtures/post.js.es6 @@ -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, diff --git a/test/javascripts/fixtures/topic.js.es6 b/test/javascripts/fixtures/topic.js.es6 index 0ced6085b41..3ac3b3352bf 100644 --- a/test/javascripts/fixtures/topic.js.es6 +++ b/test/javascripts/fixtures/topic.js.es6 @@ -88,50 +88,43 @@ export default { id: 2, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -191,50 +184,43 @@ export default { id: 2, count: 4, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -304,50 +290,43 @@ export default { id: 2, count: 4, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: true, @@ -407,50 +386,43 @@ export default { id: 2, count: 7, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -512,50 +484,43 @@ export default { id: 2, count: 2, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -627,50 +592,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: true, @@ -730,50 +688,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -839,50 +790,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: true, @@ -933,50 +877,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -1032,50 +969,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -1131,50 +1061,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: true, @@ -1225,50 +1148,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -1319,50 +1235,43 @@ export default { id: 2, count: 3, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -1422,50 +1331,43 @@ export default { id: 2, count: 3, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -1516,50 +1418,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -1610,50 +1505,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -1704,50 +1592,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -1803,50 +1684,43 @@ export default { id: 2, count: 7, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: true, @@ -1902,50 +1776,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, @@ -2005,50 +1872,43 @@ export default { id: 2, count: 1, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 3, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 4, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 5, count: 0, hidden: true, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 6, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 7, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true }, { id: 8, count: 0, hidden: false, - can_act: true, - can_defer_flags: false + can_act: true } ], moderator: false, diff --git a/test/javascripts/widgets/actions-summary-test.js.es6 b/test/javascripts/widgets/actions-summary-test.js.es6 index 9dc926ab46f..69776458df3 100644 --- a/test/javascripts/widgets/actions-summary-test.js.es6 +++ b/test/javascripts/widgets/actions-summary-test.js.es6 @@ -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() {