diff --git a/app/assets/javascripts/discourse/app/lib/transform-post.js b/app/assets/javascripts/discourse/app/lib/transform-post.js index 2f8087795f4..e2dd44b9676 100644 --- a/app/assets/javascripts/discourse/app/lib/transform-post.js +++ b/app/assets/javascripts/discourse/app/lib/transform-post.js @@ -121,6 +121,7 @@ export default function transformPost( currentUser && (currentUser.id === post.user_id || currentUser.staff); postAtts.canArchiveTopic = !!details.can_archive_topic; postAtts.canCloseTopic = !!details.can_close_topic; + postAtts.canEditStaffNotes = !!details.can_edit_staff_notes; postAtts.canReplyAsNewTopic = !!details.can_reply_as_new_topic; postAtts.canReviewTopic = !!details.can_review_topic; postAtts.canPublishPage = diff --git a/app/assets/javascripts/discourse/app/widgets/post-admin-menu.js b/app/assets/javascripts/discourse/app/widgets/post-admin-menu.js index ec329103b46..5dbad8b0050 100644 --- a/app/assets/javascripts/discourse/app/widgets/post-admin-menu.js +++ b/app/assets/javascripts/discourse/app/widgets/post-admin-menu.js @@ -52,7 +52,7 @@ export function buildManageButtons(attrs, currentUser, siteSettings) { contents.push(buttonAtts); } - if (currentUser.staff) { + if (attrs.canEditStaffNotes) { if (attrs.noticeType) { contents.push({ icon: "user-shield", diff --git a/app/assets/javascripts/discourse/app/widgets/post-menu.js b/app/assets/javascripts/discourse/app/widgets/post-menu.js index d014a67e60f..b985948d928 100644 --- a/app/assets/javascripts/discourse/app/widgets/post-menu.js +++ b/app/assets/javascripts/discourse/app/widgets/post-menu.js @@ -332,7 +332,7 @@ registerButton( ); registerButton("admin", attrs => { - if (!attrs.canManage && !attrs.canWiki) { + if (!attrs.canManage && !attrs.canWiki && !attrs.canEditStaffNotes) { return; } return { diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index 4a06c3b9bd5..dbb2c430c94 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -6,9 +6,11 @@ margin-left: 0; } -.topic-post:first-child { - nav.post-controls .post-admin-menu { - bottom: -125px; +.staff { + .topic-post:first-child { + nav.post-controls .post-admin-menu { + bottom: -125px; + } } } diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 63ef5bb7901..09cde3a54d9 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -474,9 +474,8 @@ class PostsController < ApplicationController end def notice - raise Discourse::NotFound unless guardian.is_staff? - post = find_post_from_params + raise Discourse::NotFound unless guardian.can_edit_staff_notes?(post.topic) if params[:notice].present? post.custom_fields[Post::NOTICE_TYPE] = Post.notices[:custom] diff --git a/app/serializers/topic_view_details_serializer.rb b/app/serializers/topic_view_details_serializer.rb index 8ea96113179..e9967225384 100644 --- a/app/serializers/topic_view_details_serializer.rb +++ b/app/serializers/topic_view_details_serializer.rb @@ -18,7 +18,8 @@ class TopicViewDetailsSerializer < ApplicationSerializer :can_edit_tags, :can_publish_page, :can_close_topic, - :can_archive_topic] + :can_archive_topic, + :can_edit_staff_notes] end attributes( @@ -136,13 +137,12 @@ class TopicViewDetailsSerializer < ApplicationSerializer !scope.can_edit?(object.topic) && scope.can_edit_tags?(object.topic) end - def include_can_close_topic? - scope.can_close_topic?(object.topic) - end - - def include_can_archive_topic? - scope.can_archive_topic?(object.topic) + def can_perform_action_available_to_group_moderators? + @can_perform_action_available_to_group_moderators ||= scope.can_perform_action_available_to_group_moderators?(object.topic) end + alias :include_can_close_topic? :can_perform_action_available_to_group_moderators? + alias :include_can_archive_topic? :can_perform_action_available_to_group_moderators? + alias :include_can_edit_staff_notes? :can_perform_action_available_to_group_moderators? def include_can_publish_page? scope.can_publish_page?(object.topic) diff --git a/lib/guardian/topic_guardian.rb b/lib/guardian/topic_guardian.rb index e1ff4108c31..5a0f9b54df6 100644 --- a/lib/guardian/topic_guardian.rb +++ b/lib/guardian/topic_guardian.rb @@ -216,5 +216,6 @@ module TopicGuardian end alias :can_archive_topic? :can_perform_action_available_to_group_moderators? alias :can_close_topic? :can_perform_action_available_to_group_moderators? + alias :can_edit_staff_notes? :can_perform_action_available_to_group_moderators? end diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index 3082c184a67..b200a244906 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -1779,6 +1779,28 @@ describe Guardian do end end + context "can_edit_staff_notes?" do + it 'returns false with a nil object' do + expect(Guardian.new(user).can_edit_staff_notes?(nil)).to eq(false) + end + + it 'returns true for a staff user' do + expect(Guardian.new(moderator).can_edit_staff_notes?(topic)).to eq(true) + end + + it 'returns false for a regular user' do + expect(Guardian.new(user).can_edit_staff_notes?(topic)).to eq(false) + end + + it 'returns true for a group member with reviewable status' do + SiteSetting.enable_category_group_moderation = 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_edit_staff_notes?(topic)).to eq(true) + end + end + context "can_create_topic?" do it 'returns true for staff user' do expect(Guardian.new(moderator).can_create_topic?(topic)).to eq(true) diff --git a/spec/requests/posts_controller_spec.rb b/spec/requests/posts_controller_spec.rb index 6ccc7380d81..65d11dd5736 100644 --- a/spec/requests/posts_controller_spec.rb +++ b/spec/requests/posts_controller_spec.rb @@ -1795,11 +1795,9 @@ describe PostsController do end describe "#notice" do - before do + it 'can create and remove notices as a moderator' do sign_in(moderator) - end - it 'can create and remove notices' do put "/posts/#{public_post.id}/notice.json", params: { notice: "Hello *world*!\n\nhttps://github.com/discourse/discourse" } expect(response.status).to eq(200) @@ -1815,6 +1813,52 @@ describe PostsController do expect(public_post.custom_fields[Post::NOTICE_TYPE]).to eq(nil) expect(public_post.custom_fields[Post::NOTICE_ARGS]).to eq(nil) end + + describe 'group moderators' do + fab!(:group_user) { Fabricate(:group_user) } + let(:user) { group_user.user } + let(:group) { group_user.group } + + before do + SiteSetting.enable_category_group_moderation = true + topic.category.update!(reviewable_by_group_id: group.id) + + sign_in(user) + end + + it 'can create and remove notices as a group moderator' do + put "/posts/#{public_post.id}/notice.json", params: { notice: "Hello *world*!\n\nhttps://github.com/discourse/discourse" } + + expect(response.status).to eq(200) + public_post.reload + expect(public_post.custom_fields[Post::NOTICE_TYPE]).to eq(Post.notices[:custom]) + expect(public_post.custom_fields[Post::NOTICE_ARGS]).to include('<p>Hello <em>world</em>!</p>') + expect(public_post.custom_fields[Post::NOTICE_ARGS]).not_to include('onebox') + + put "/posts/#{public_post.id}/notice.json", params: { notice: nil } + + expect(response.status).to eq(200) + public_post.reload + expect(public_post.custom_fields[Post::NOTICE_TYPE]).to eq(nil) + expect(public_post.custom_fields[Post::NOTICE_ARGS]).to eq(nil) + end + + it 'prevents a group moderator from altering notes outside of their category' do + moderatable_group = Fabricate(:group) + topic.category.update!(reviewable_by_group_id: moderatable_group.id) + + put "/posts/#{public_post.id}/notice.json", params: { notice: "Hello" } + + expect(response.status).to eq(404) + end + + it 'prevents a normal user from altering notes' do + group_user.destroy! + put "/posts/#{public_post.id}/notice.json", params: { notice: "Hello" } + + expect(response.status).to eq(404) + end + end end describe Plugin::Instance do diff --git a/test/javascripts/acceptance/post-admin-menu-test.js b/test/javascripts/acceptance/post-admin-menu-test.js new file mode 100644 index 00000000000..277fc9e3d98 --- /dev/null +++ b/test/javascripts/acceptance/post-admin-menu-test.js @@ -0,0 +1,21 @@ +import { acceptance } from "helpers/qunit-helpers"; + +acceptance("Post - Admin Menu Anonymous Users", { loggedIn: false }); + +QUnit.test("Enter as a anon user", async assert => { + await visit("/t/internationalization-localization/280"); + await click(".show-more-actions"); + + assert.ok(exists("#topic"), "The topic was rendered"); + assert.ok(!exists(".show-post-admin-menu"), "The wrench button was not rendered"); +}); + +acceptance("Post - Admin Menu", { loggedIn: true }); + +QUnit.test("Enter as a user with group moderator permissions", async assert => { + await visit("/t/topic-for-group-moderators/2480"); + await click(".show-more-actions"); + await click(".show-post-admin-menu"); + + assert.ok(exists(".add-notice"), "The add notice button was rendered"); +}); diff --git a/test/javascripts/acceptance/topic-admin-menu-test.js b/test/javascripts/acceptance/topic-admin-menu-test.js index 759f30ba44a..a00a89be629 100644 --- a/test/javascripts/acceptance/topic-admin-menu-test.js +++ b/test/javascripts/acceptance/topic-admin-menu-test.js @@ -19,10 +19,10 @@ QUnit.test("Enter as a user with group moderator permissions", async assert => { assert.ok(exists(".toggle-admin-menu"), "The admin menu button was rendered"); }); -QUnit.test("Enter as a user with group moderator permissions", async assert => { +QUnit.test("Enter as a user with moderator and admin permissions", async assert => { updateCurrentUser({ moderator: true, admin: true, trust_level: 4 }); - await visit("/t/topic-for-group-moderators/2480"); + await visit("/t/internationalization-localization/280"); assert.ok(exists("#topic"), "The topic was rendered"); assert.ok(exists(".toggle-admin-menu"), "The admin menu button was rendered"); }); diff --git a/test/javascripts/helpers/create-pretender.js b/test/javascripts/helpers/create-pretender.js index 050b00d109d..3376b06c4a2 100644 --- a/test/javascripts/helpers/create-pretender.js +++ b/test/javascripts/helpers/create-pretender.js @@ -259,6 +259,7 @@ export function applyDefaultHandlers(pretender) { const json = fixturesByUrl["/t/34/1.json"]; json.details.can_archive_topic = true; json.details.can_close_topic = true; + json.details.can_edit_staff_notes = true; return response(json); });