From 96a2893284ed5488bbeb93463e40ed5b53084dd0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=94=A6=E5=BF=83?=
<41134017+Lhcfl@users.noreply.github.com>
Date: Thu, 1 Jun 2023 11:32:05 +0800
Subject: [PATCH] FEATURE: Allow expanding hidden posts for groups in
SiteSetting.can_see_hidden_post (#21853)
Allow expanding hidden posts for groups in SiteSetting.can_see_hidden_post
---
.../discourse/app/lib/transform-post.js | 1 +
.../javascripts/discourse/app/widgets/post.js | 5 +-
.../components/widgets/post-test.js | 13 ++++-
app/serializers/post_serializer.rb | 5 ++
app/serializers/web_hook_post_serializer.rb | 1 +
config/locales/server.en.yml | 1 +
config/site_settings.yml | 6 +++
lib/guardian/post_guardian.rb | 6 ++-
spec/lib/guardian/post_guardian_spec.rb | 51 ++++++++++++++-----
spec/requests/api/posts_spec.rb | 3 ++
.../schemas/json/post_replies_response.json | 4 ++
.../api/schemas/json/post_show_response.json | 3 ++
.../schemas/json/post_update_response.json | 3 ++
.../schemas/json/topic_create_response.json | 3 ++
.../api/schemas/json/topic_show_response.json | 3 ++
spec/serializers/post_serializer_spec.rb | 7 +++
16 files changed, 97 insertions(+), 18 deletions(-)
diff --git a/app/assets/javascripts/discourse/app/lib/transform-post.js b/app/assets/javascripts/discourse/app/lib/transform-post.js
index 89ec81ecbf5..689cfd0daeb 100644
--- a/app/assets/javascripts/discourse/app/lib/transform-post.js
+++ b/app/assets/javascripts/discourse/app/lib/transform-post.js
@@ -57,6 +57,7 @@ export function transformBasicPost(post) {
canPermanentlyDelete: false,
showFlagDelete: false,
canRecover: post.can_recover,
+ canSeeHiddenPost: post.can_see_hidden_post,
canEdit: post.can_edit,
canFlag: !post.get("topic.deleted") && !isEmpty(post.get("flagsAvailable")),
canReviewTopic: false,
diff --git a/app/assets/javascripts/discourse/app/widgets/post.js b/app/assets/javascripts/discourse/app/widgets/post.js
index 1aa59ae5885..ff04f2fbda7 100644
--- a/app/assets/javascripts/discourse/app/widgets/post.js
+++ b/app/assets/javascripts/discourse/app/widgets/post.js
@@ -497,10 +497,7 @@ createWidget("post-contents", {
result = result.concat(applyDecorators(this, "after-cooked", attrs, state));
- if (
- attrs.cooked_hidden &&
- (this.currentUser?.isLeader || attrs.user_id === this.currentUser?.id)
- ) {
+ if (attrs.cooked_hidden && attrs.canSeeHiddenPost) {
result.push(this.attach("expand-hidden", attrs));
}
diff --git a/app/assets/javascripts/discourse/tests/integration/components/widgets/post-test.js b/app/assets/javascripts/discourse/tests/integration/components/widgets/post-test.js
index 6f7ca2c89a5..27be61e0c6c 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/widgets/post-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/widgets/post-test.js
@@ -495,7 +495,7 @@ module("Integration | Component | Widget | post", function (hooks) {
});
test("cooked content hidden", async function (assert) {
- this.set("args", { cooked_hidden: true });
+ this.set("args", { cooked_hidden: true, canSeeHiddenPost: true });
this.set("expandHidden", () => (this.unhidden = true));
await render(hbs`
@@ -506,6 +506,17 @@ module("Integration | Component | Widget | post", function (hooks) {
assert.ok(this.unhidden, "triggers the action");
});
+ test(`cooked content hidden - can't view hidden post`, async function (assert) {
+ this.set("args", { cooked_hidden: true, canSeeHiddenPost: false });
+ this.set("expandHidden", () => (this.unhidden = true));
+
+ await render(hbs`
+
+ `);
+
+ assert.ok(!exists(".topic-body .expand-hidden"), "button is not displayed");
+ });
+
test("expand first post", async function (assert) {
const store = getOwner(this).lookup("service:store");
this.set("args", { expandablePost: true });
diff --git a/app/serializers/post_serializer.rb b/app/serializers/post_serializer.rb
index a35ea4b8ca4..e2a849d8704 100644
--- a/app/serializers/post_serializer.rb
+++ b/app/serializers/post_serializer.rb
@@ -43,6 +43,7 @@ class PostSerializer < BasicPostSerializer
:can_delete,
:can_permanently_delete,
:can_recover,
+ :can_see_hidden_post,
:can_wiki,
:link_counts,
:read,
@@ -180,6 +181,10 @@ class PostSerializer < BasicPostSerializer
scope.can_recover_post?(object)
end
+ def can_see_hidden_post
+ scope.can_see_hidden_post?(object)
+ end
+
def can_wiki
scope.can_wiki?(object)
end
diff --git a/app/serializers/web_hook_post_serializer.rb b/app/serializers/web_hook_post_serializer.rb
index f804e00833e..c4b737703d2 100644
--- a/app/serializers/web_hook_post_serializer.rb
+++ b/app/serializers/web_hook_post_serializer.rb
@@ -20,6 +20,7 @@ class WebHookPostSerializer < PostSerializer
can_edit
can_delete
can_recover
+ can_see_hidden_post
can_wiki
actions_summary
can_view_edit_history
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 0a801ca3d0d..0ff51291927 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -1700,6 +1700,7 @@ en:
enable_badges: "Enable the badge system"
max_favorite_badges: "Maximum number of badges that user can select"
whispers_allowed_groups: "Allow private communication within topics for members of specified groups."
+ hidden_post_visible_groups: "Allow members of these groups to view hidden posts. Staff users can always view hidden posts."
allow_index_in_robots_txt: "Specify in robots.txt that this site is allowed to be indexed by web search engines. In exceptional cases you can permanently override robots.txt."
blocked_email_domains: "A pipe-delimited list of email domains that users are not allowed to register accounts with. Subdomains are automatically handled for the specified domains. Wildcard symbols * and ? are not supported. Example: mailinator.com|trashmail.net"
diff --git a/config/site_settings.yml b/config/site_settings.yml
index 5cc215c0e27..ebe289ba9e1 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -336,6 +336,12 @@ basic:
default: ""
allow_any: false
refresh: true
+ hidden_post_visible_groups:
+ type: group_list
+ list_type: compact
+ default: "14"
+ allow_any: false
+ refresh: true
enable_bookmarks_with_reminders:
client: true
default: true
diff --git a/lib/guardian/post_guardian.rb b/lib/guardian/post_guardian.rb
index a72b4befb45..6b485569571 100644
--- a/lib/guardian/post_guardian.rb
+++ b/lib/guardian/post_guardian.rb
@@ -284,8 +284,12 @@ module PostGuardian
end
def can_see_hidden_post?(post)
+ if SiteSetting.hidden_post_visible_groups_map.include?(Group::AUTO_GROUPS[:everyone])
+ return true
+ end
return false if anonymous?
- post.user_id == @user.id || @user.has_trust_level_or_staff?(TrustLevel[4])
+ return true if is_staff?
+ post.user_id == @user.id || @user.in_any_groups?(SiteSetting.hidden_post_visible_groups_map)
end
def can_view_edit_history?(post)
diff --git a/spec/lib/guardian/post_guardian_spec.rb b/spec/lib/guardian/post_guardian_spec.rb
index 713886a6f3b..c67579bd328 100644
--- a/spec/lib/guardian/post_guardian_spec.rb
+++ b/spec/lib/guardian/post_guardian_spec.rb
@@ -1,32 +1,59 @@
# frozen_string_literal: true
RSpec.describe PostGuardian do
+ fab!(:groupless_user) { Fabricate(:user) }
fab!(:user) { Fabricate(:user) }
fab!(:anon) { Fabricate(:anonymous) }
fab!(:admin) { Fabricate(:admin) }
- fab!(:tl3_user) { Fabricate(:trust_level_3) }
- fab!(:tl4_user) { Fabricate(:trust_level_4) }
fab!(:moderator) { Fabricate(:moderator) }
+ fab!(:group) { Fabricate(:group) }
+ fab!(:group_user) { Fabricate(:group_user, group: group, user: user) }
fab!(:category) { Fabricate(:category) }
fab!(:topic) { Fabricate(:topic, category: category) }
fab!(:hidden_post) { Fabricate(:post, topic: topic, hidden: true) }
describe "#can_see_hidden_post?" do
- it "returns false for anonymous users" do
- expect(Guardian.new(anon).can_see_hidden_post?(hidden_post)).to eq(false)
+ context "when the hidden_post_visible_groups contains everyone" do
+ before { SiteSetting.hidden_post_visible_groups = "#{Group::AUTO_GROUPS[:everyone]}" }
+
+ it "returns true for everyone" do
+ expect(Guardian.new(anon).can_see_hidden_post?(hidden_post)).to eq(true)
+ expect(Guardian.new(user).can_see_hidden_post?(hidden_post)).to eq(true)
+ expect(Guardian.new(admin).can_see_hidden_post?(hidden_post)).to eq(true)
+ expect(Guardian.new(moderator).can_see_hidden_post?(hidden_post)).to eq(true)
+ end
end
- it "returns false for TL3 users" do
- expect(Guardian.new(tl3_user).can_see_hidden_post?(hidden_post)).to eq(false)
+ context "when the post is a created by the user" do
+ fab!(:hidden_post) { Fabricate(:post, topic: topic, hidden: true, user: user) }
+
+ before { SiteSetting.hidden_post_visible_groups = "" }
+
+ it "returns true for the author" do
+ SiteSetting.hidden_post_visible_groups = ""
+ expect(Guardian.new(user).can_see_hidden_post?(hidden_post)).to eq(true)
+ end
end
- it "returns true for TL4 users" do
- expect(Guardian.new(tl4_user).can_see_hidden_post?(hidden_post)).to eq(true)
- end
+ context "when the post is a created by another user" do
+ before { SiteSetting.hidden_post_visible_groups = "14|#{group.id}" }
- it "returns true for staff users" do
- expect(Guardian.new(moderator).can_see_hidden_post?(hidden_post)).to eq(true)
- expect(Guardian.new(admin).can_see_hidden_post?(hidden_post)).to eq(true)
+ it "returns true for staff users" do
+ expect(Guardian.new(admin).can_see_hidden_post?(hidden_post)).to eq(true)
+ expect(Guardian.new(moderator).can_see_hidden_post?(hidden_post)).to eq(true)
+ end
+
+ it "returns false for anonymous users" do
+ expect(Guardian.new(anon).can_see_hidden_post?(hidden_post)).to eq(false)
+ end
+
+ it "returns true if the user is in hidden_post_visible_groups" do
+ expect(Guardian.new(user).can_see_hidden_post?(hidden_post)).to eq(true)
+ end
+
+ it "returns false if the user is not in hidden_post_visible_groups" do
+ expect(Guardian.new(groupless_user).can_see_hidden_post?(hidden_post)).to eq(false)
+ end
end
end
end
diff --git a/spec/requests/api/posts_spec.rb b/spec/requests/api/posts_spec.rb
index d5a2e5dd303..399079c25c3 100644
--- a/spec/requests/api/posts_spec.rb
+++ b/spec/requests/api/posts_spec.rb
@@ -131,6 +131,9 @@ RSpec.describe "posts" do
can_recover: {
type: :boolean,
},
+ can_see_hidden_post: {
+ type: :boolean,
+ },
can_wiki: {
type: :boolean,
},
diff --git a/spec/requests/api/schemas/json/post_replies_response.json b/spec/requests/api/schemas/json/post_replies_response.json
index 2527616f79a..5e762e15891 100644
--- a/spec/requests/api/schemas/json/post_replies_response.json
+++ b/spec/requests/api/schemas/json/post_replies_response.json
@@ -119,6 +119,9 @@
"can_recover": {
"type": "boolean"
},
+ "can_see_hidden_post": {
+ "type": "boolean"
+ },
"can_wiki": {
"type": "boolean"
},
@@ -252,6 +255,7 @@
"can_edit",
"can_delete",
"can_recover",
+ "can_see_hidden_post",
"can_wiki",
"user_title",
"reply_to_user",
diff --git a/spec/requests/api/schemas/json/post_show_response.json b/spec/requests/api/schemas/json/post_show_response.json
index 67a1dcb21e8..2010d8f6519 100644
--- a/spec/requests/api/schemas/json/post_show_response.json
+++ b/spec/requests/api/schemas/json/post_show_response.json
@@ -106,6 +106,9 @@
"can_recover": {
"type": "boolean"
},
+ "can_see_hidden_post": {
+ "type": "boolean"
+ },
"can_wiki": {
"type": "boolean"
},
diff --git a/spec/requests/api/schemas/json/post_update_response.json b/spec/requests/api/schemas/json/post_update_response.json
index 7c21daa521e..87ca79819d9 100644
--- a/spec/requests/api/schemas/json/post_update_response.json
+++ b/spec/requests/api/schemas/json/post_update_response.json
@@ -110,6 +110,9 @@
"can_recover": {
"type": "boolean"
},
+ "can_see_hidden_post": {
+ "type": "boolean"
+ },
"can_wiki": {
"type": "boolean"
},
diff --git a/spec/requests/api/schemas/json/topic_create_response.json b/spec/requests/api/schemas/json/topic_create_response.json
index e3bb4d5932b..02038d367b9 100644
--- a/spec/requests/api/schemas/json/topic_create_response.json
+++ b/spec/requests/api/schemas/json/topic_create_response.json
@@ -121,6 +121,9 @@
"can_recover": {
"type": "boolean"
},
+ "can_see_hidden_post": {
+ "type": "boolean"
+ },
"can_wiki": {
"type": "boolean"
},
diff --git a/spec/requests/api/schemas/json/topic_show_response.json b/spec/requests/api/schemas/json/topic_show_response.json
index d26043cf153..40e5bd5d16c 100644
--- a/spec/requests/api/schemas/json/topic_show_response.json
+++ b/spec/requests/api/schemas/json/topic_show_response.json
@@ -117,6 +117,9 @@
"can_recover": {
"type": "boolean"
},
+ "can_see_hidden_post": {
+ "type": "boolean"
+ },
"can_wiki": {
"type": "boolean"
},
diff --git a/spec/serializers/post_serializer_spec.rb b/spec/serializers/post_serializer_spec.rb
index b4eaa9ad34f..39d7cbe6c75 100644
--- a/spec/serializers/post_serializer_spec.rb
+++ b/spec/serializers/post_serializer_spec.rb
@@ -141,6 +141,13 @@ RSpec.describe PostSerializer do
)
end
+ it "includes if the user can see it" do
+ expect(serialized_post_for_user(Fabricate(:moderator))[:can_see_hidden_post]).to eq(true)
+ expect(serialized_post_for_user(Fabricate(:admin))[:can_see_hidden_post]).to eq(true)
+ expect(serialized_post_for_user(user)[:can_see_hidden_post]).to eq(true)
+ expect(serialized_post_for_user(Fabricate(:user))[:can_see_hidden_post]).to eq(false)
+ end
+
it "shows the raw post only if authorized to see it" do
expect(serialized_post_for_user(nil)[:raw]).to eq(nil)
expect(serialized_post_for_user(Fabricate(:user))[:raw]).to eq(nil)