diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ee3e7ac12a2..7a81c069845 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1964,7 +1964,7 @@ en: label: "New Topic" shared_draft: label: "Shared Draft" - desc: "Draft a topic that will only be visible to staff" + desc: "Draft a topic that will only be visible to allowed users" toggle_topic_bump: label: "Toggle topic bump" desc: "Reply without changing latest reply date" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 1321f005b0b..877d4f37fcd 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -2252,6 +2252,7 @@ en: city_for_disputes: "City for Disputes" shared_drafts_category: "Enable the Shared Drafts feature by designating a category for topic drafts. Topics in this category will be suppressed from topic lists for staff users." + shared_drafts_min_trust_level: "Allow users to see and edit Shared Drafts." push_notifications_prompt: "Display user consent prompt." push_notifications_icon: "The badge icon that appears in the notification corner. A 96×96 monochromatic PNG with transparency is recommended." diff --git a/config/site_settings.yml b/config/site_settings.yml index 4bea11f1b53..fa35652b885 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -772,6 +772,9 @@ posting: shared_drafts_category: type: category default: "" + shared_drafts_min_trust_level: + default: "staff" + enum: "TrustLevelAndStaffSetting" post_edit_time_limit: default: 1440 max: 10080 diff --git a/lib/guardian/post_guardian.rb b/lib/guardian/post_guardian.rb index e6e90410139..4bbd2ac3e82 100644 --- a/lib/guardian/post_guardian.rb +++ b/lib/guardian/post_guardian.rb @@ -139,6 +139,14 @@ module PostGuardian return false end + return true if ( + can_see_post?(post) && + can_create_post?(post.topic) && + post.topic.category_id == SiteSetting.shared_drafts_category.to_i && + can_see_category?(post.topic.category) && + can_create_shared_draft? + ) + if post.wiki && (@user.trust_level >= SiteSetting.min_trust_to_edit_wiki_post.to_i) return can_create_post?(post.topic) end diff --git a/lib/guardian/topic_guardian.rb b/lib/guardian/topic_guardian.rb index 92e4c6702a1..74a02958504 100644 --- a/lib/guardian/topic_guardian.rb +++ b/lib/guardian/topic_guardian.rb @@ -22,7 +22,12 @@ module TopicGuardian alias :can_moderate_topic? :can_review_topic? def can_create_shared_draft? - is_staff? && SiteSetting.shared_drafts_enabled? + return false unless SiteSetting.shared_drafts_enabled? + + return is_admin? if SiteSetting.shared_drafts_min_trust_level.to_s == 'admin' + return is_staff? if SiteSetting.shared_drafts_min_trust_level.to_s == 'staff' + + @user.has_trust_level?(SiteSetting.shared_drafts_min_trust_level.to_i) end def can_create_whisper? @@ -34,7 +39,7 @@ module TopicGuardian end def can_publish_topic?(topic, category) - is_staff? && can_see?(topic) && can_create_topic?(category) + can_create_shared_draft? && can_see?(topic) && can_create_topic_on_category?(category) end # Creating Methods @@ -91,6 +96,16 @@ module TopicGuardian return false if !can_create_topic_on_category?(topic.category) end + # Editing a shared draft. + return true if ( + !topic.archived && + !topic.private_message? && + topic.category_id == SiteSetting.shared_drafts_category.to_i && + can_see_category?(topic.category) && + can_create_shared_draft? && + can_create_post?(topic) + ) + # TL4 users can edit archived topics, but can not edit private messages return true if ( SiteSetting.trusted_users_can_edit_others? && diff --git a/spec/components/guardian/topic_guardian_spec.rb b/spec/components/guardian/topic_guardian_spec.rb new file mode 100644 index 00000000000..60bc247291d --- /dev/null +++ b/spec/components/guardian/topic_guardian_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe TopicGuardian do + fab!(:admin) { Fabricate(:admin) } + fab!(:tl3_user) { Fabricate(:leader) } + fab!(:moderator) { Fabricate(:moderator) } + fab!(:category) { Fabricate(:category) } + + describe '#can_create_shared_draft?' do + it 'when shared_drafts are disabled' do + SiteSetting.shared_drafts_min_trust_level = 'admin' + + expect(Guardian.new(admin).can_create_shared_draft?).to eq(false) + end + + it 'when user is a moderator and access is set to admin' do + SiteSetting.shared_drafts_category = category.id + SiteSetting.shared_drafts_min_trust_level = 'admin' + + expect(Guardian.new(moderator).can_create_shared_draft?).to eq(false) + end + + it 'when user is a moderator and access is set to staff' do + SiteSetting.shared_drafts_category = category.id + SiteSetting.shared_drafts_min_trust_level = 'staff' + + expect(Guardian.new(moderator).can_create_shared_draft?).to eq(true) + end + + it 'when user is TL3 and access is set to TL2' do + SiteSetting.shared_drafts_category = category.id + SiteSetting.shared_drafts_min_trust_level = '2' + + expect(Guardian.new(tl3_user).can_create_shared_draft?).to eq(true) + end + end + + describe '#can_edit_topic?' do + context 'when the topic is a shared draft' do + let(:tl2_user) { Fabricate(:user, trust_level: TrustLevel[2]) } + + before do + SiteSetting.shared_drafts_category = category.id + SiteSetting.shared_drafts_min_trust_level = '2' + end + + it 'returns false if the topic is a PM' do + pm_with_draft = Fabricate(:private_message_topic, category: category) + Fabricate(:shared_draft, topic: pm_with_draft) + + expect(Guardian.new(tl2_user).can_edit_topic?(pm_with_draft)).to eq(false) + end + + it 'returns false if the topic is archived' do + archived_topic = Fabricate(:topic, archived: true, category: category) + Fabricate(:shared_draft, topic: archived_topic) + + expect(Guardian.new(tl2_user).can_edit_topic?(archived_topic)).to eq(false) + end + + it 'returns true if a shared draft exists' do + topic = Fabricate(:topic, category: category) + Fabricate(:shared_draft, topic: topic) + + expect(Guardian.new(tl2_user).can_edit_topic?(topic)).to eq(true) + end + + it 'returns false if the user has a lower trust level' do + tl1_user = Fabricate(:user, trust_level: TrustLevel[1]) + topic = Fabricate(:topic, category: category) + Fabricate(:shared_draft, topic: topic) + + expect(Guardian.new(tl1_user).can_edit_topic?(topic)).to eq(false) + end + + it 'returns true if the shared_draft is from a different category' do + topic = Fabricate(:topic, category: Fabricate(:category)) + Fabricate(:shared_draft, topic: topic) + + expect(Guardian.new(tl2_user).can_edit_topic?(topic)).to eq(false) + end + end + end +end diff --git a/spec/components/guardian/user_guardian_spec.rb b/spec/components/guardian/user_guardian_spec.rb index fc516cf70d6..9d289d32160 100644 --- a/spec/components/guardian/user_guardian_spec.rb +++ b/spec/components/guardian/user_guardian_spec.rb @@ -456,4 +456,31 @@ describe UserGuardian do end end + describe '#can_edit_post?' do + fab!(:category) { Fabricate(:category) } + + let(:topic) { Fabricate(:topic, category: category) } + let(:post_with_draft) { Fabricate(:post, topic: topic) } + + before do + SiteSetting.shared_drafts_category = category.id + SiteSetting.shared_drafts_min_trust_level = '2' + Fabricate(:shared_draft, topic: topic) + end + + it 'returns true if a shared draft exists' do + expect(Guardian.new(trust_level_2).can_edit_post?(post_with_draft)).to eq(true) + end + + it 'returns false if the user has a lower trust level' do + expect(Guardian.new(trust_level_1).can_edit_post?(post_with_draft)).to eq(false) + end + + it 'returns false if the draft is from a different category' do + topic.update!(category: Fabricate(:category)) + + expect(Guardian.new(trust_level_2).can_edit_post?(post_with_draft)).to eq(false) + end + + end end