FEATURE: Allow group moderators to add/remove staff notes (#10252)

* FEATURE: Allow group moderators to add/remove staff notes
This commit is contained in:
jbrw 2020-07-20 15:53:47 -04:00 committed by GitHub
parent 44cfa25d7d
commit 7ab5658462
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 110 additions and 19 deletions

View File

@ -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 =

View File

@ -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",

View File

@ -332,7 +332,7 @@ registerButton(
);
registerButton("admin", attrs => {
if (!attrs.canManage && !attrs.canWiki) {
if (!attrs.canManage && !attrs.canWiki && !attrs.canEditStaffNotes) {
return;
}
return {

View File

@ -6,10 +6,12 @@
margin-left: 0;
}
.topic-post:first-child {
.staff {
.topic-post:first-child {
nav.post-controls .post-admin-menu {
bottom: -125px;
}
}
}
.topic-body {

View File

@ -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]

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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");
});

View File

@ -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");
});

View File

@ -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);
});