-
- <%= tag.name -%>
-
+ <% if SiteSetting.tagging_enabled %>
+ <% @tags = @topic_view.topic.tags %>
+ <% if @tags.present? %>
+
+ <% end %>
+ <% end %>
+
+ <%= server_plugin_outlet "topic_header" %>
+
+ <%- if include_crawler_content? %>
+
+ <% @topic_view.posts.each do |post| %>
+
+ <% if (u = post.user) %>
+
+
+ <%= u.username %>
+ <%= "(#{u.name})" if (SiteSetting.display_name_on_posts && SiteSetting.enable_names? && !u.name.blank?) %>
+ <%
+ post_custom_fields = @topic_view.post_custom_fields[post.id] || {}
+ who_username = post_custom_fields["action_code_who"] || ""
+ if post.action_code
+ %>
+ <%= t("js.action_codes.#{post.action_code}", when: "", who: who_username).html_safe %>
+ <% end %>
+
+
+
+ <% if post.updated_at > post.created_at %>
+
+
+ <% else %>
+
+ <% end %>
+ #<%= post.post_number %>
+
+
+ <%= post.hidden ? t('flagging.user_must_edit').html_safe : post.cooked.html_safe %>
+
+
+
+
+
+
+
+ <%= post.like_count > 0 ? t('post.has_likes', count: post.like_count) : '' %>
+
+
+
+
+
+
+
+ <% if @topic_view.link_counts[post.id] && @topic_view.link_counts[post.id].length > 0 %>
+
+ <% @topic_view.link_counts[post.id].each_with_index do |link, i| %>
+ <% if link[:reflection] && link[:title].present? %>
+
+ <% end %>
+ <% end %>
+
+ <% end %>
+
<% end %>
<% end %>
-<% end %>
-
-<%= server_plugin_outlet "topic_header" %>
-
-<%- if include_crawler_content? %>
-
-<% @topic_view.posts.each do |post| %>
-
- <% if (u = post.user) %>
-
-
- <%= u.username %>
- <%= "(#{u.name})" if (SiteSetting.display_name_on_posts && SiteSetting.enable_names? && !u.name.blank?) %>
- <%
- post_custom_fields = @topic_view.post_custom_fields[post.id] || {}
- who_username = post_custom_fields["action_code_who"] || ""
- if post.action_code
- %>
- <%= t("js.action_codes.#{post.action_code}", when: "", who: who_username).html_safe %>
- <% end %>
-
-
-
- <% if post.updated_at > post.created_at %>
-
-
- <% else %>
-
- <% end %>
- #<%= post.post_number %>
-
-
-
- <%= post.hidden ? t('flagging.user_must_edit').html_safe : post.cooked.html_safe %>
-
-
-
-
-
-
-
- <%= post.like_count > 0 ? t('post.has_likes', count: post.like_count) : '' %>
-
-
-
-
-
-
-
- <% if @topic_view.link_counts[post.id] && @topic_view.link_counts[post.id].length > 0 %>
-
- <% @topic_view.link_counts[post.id].each_with_index do |link, i| %>
- <% if link[:reflection] && link[:title].present? %>
-
- <% end %>
- <% end %>
-
- <% end %>
-
- <% end %>
-
-<% end %>
-
-<% if @topic_view.prev_page || @topic_view.next_page %>
-
- <% if @topic_view.prev_page %>
- <%= link_to t(:prev_page), @topic_view.prev_page_path, rel: 'prev', itemprop: 'url' %>
- <% end %>
- <% if @topic_view.next_page %>
- <%= link_to t(:next_page), @topic_view.next_page_path, rel: 'next', itemprop: 'url' %>
- <% end %>
-
-<% end %>
-
-<% end %>
-
-<% content_for :head do %>
- <%= auto_discovery_link_tag(@topic_view, {action: :feed, slug: @topic_view.topic.slug, topic_id: @topic_view.topic.id}, title: t('rss_posts_in_topic', topic: @topic_view.title), type: 'application/rss+xml') %>
- <%= raw crawlable_meta_data(title: @topic_view.title, description: @topic_view.summary(strip_images: true), image: @topic_view.image_url, read_time: @topic_view.read_time, like_count: @topic_view.like_count, ignore_canonical: true, published_time: @topic_view.published_time) %>
<% if @topic_view.prev_page || @topic_view.next_page %>
- <% if @topic_view.prev_page %>
-
+
+ <% if @topic_view.prev_page %>
+ <%= link_to t(:prev_page), @topic_view.prev_page_path, rel: 'prev', itemprop: 'url' %>
+ <% end %>
+ <% if @topic_view.next_page %>
+ <%= link_to t(:next_page), @topic_view.next_page_path, rel: 'next', itemprop: 'url' %>
+ <% end %>
+
+ <% end %>
+
+ <% end %>
+
+ <% content_for :head do %>
+ <%= auto_discovery_link_tag(@topic_view, {action: :feed, slug: @topic_view.topic.slug, topic_id: @topic_view.topic.id}, title: t('rss_posts_in_topic', topic: @topic_view.title), type: 'application/rss+xml') %>
+ <%= raw crawlable_meta_data(title: @topic_view.title, description: @topic_view.summary(strip_images: true), image: @topic_view.image_url, read_time: @topic_view.read_time, like_count: @topic_view.like_count, ignore_canonical: true, published_time: @topic_view.published_time) %>
+
+ <% if @topic_view.prev_page || @topic_view.next_page %>
+ <% if @topic_view.prev_page %>
+
+ <% end %>
+ <% if @topic_view.next_page %>
+
+ <% end %>
<% end %>
- <% if @topic_view.next_page %>
-
+ <% end %>
+
+ <% content_for(:title) { @title || "#{gsub_emoji_to_unicode(@topic_view.page_title)} - #{SiteSetting.title}" } %>
+
+ <% if @topic_view.print %>
+ <% content_for :after_body do %>
+ <%= preload_script('print-page') %>
<% end %>
<% end %>
<% end %>
-
-<% content_for(:title) { @title || "#{gsub_emoji_to_unicode(@topic_view.page_title)} - #{SiteSetting.title}" } %>
-
-<% if @topic_view.print %>
- <% content_for :after_body do %>
- <%= preload_script('print-page') %>
- <% end %>
-<% end %>
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index de60aa56f95..c5e2a11a309 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -1967,6 +1967,9 @@ en:
toggle_information: "toggle topic details"
read_more_in_category: "Want to read more? Browse other topics in {{catLink}} or {{latestLink}}."
read_more: "Want to read more? {{catLink}} or {{latestLink}}."
+ group_request: "You need to request membership to the `{{name}}` group to see this topic"
+ group_join: "You need join the `{{name}}` group to see this topic"
+ group_request_sent: "Your group membership request has been sent. You will be informed when it's accepted."
# keys ending with _MF use message format, see https://meta.discourse.org/t/message-format-support-for-localization/7035 for details
read_more_MF: "There {
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index c3ba80e7d78..4941b627bc4 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -381,6 +381,7 @@ en:
request_membership_pm:
title: "Membership Request for @%{group_name}"
handle: "handle membership request"
+ view_hidden_topic_request_reason: "I would like to join the group '%{group_name}', so I may access [this topic](%{topic_url})"
education:
until_posts:
diff --git a/lib/guardian/topic_guardian.rb b/lib/guardian/topic_guardian.rb
index 97f2425ad02..e171d32d90f 100644
--- a/lib/guardian/topic_guardian.rb
+++ b/lib/guardian/topic_guardian.rb
@@ -159,6 +159,10 @@ module TopicGuardian
can_see_topic?(topic, false)
end
+ def can_get_access_to_topic?(topic)
+ topic&.access_topic_via_group.present? && authenticated?
+ end
+
def filter_allowed_categories(records)
unless is_admin?
allowed_ids = allowed_category_ids
diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb
index cdb8b4ac7e9..26dda3fb468 100644
--- a/spec/models/topic_spec.rb
+++ b/spec/models/topic_spec.rb
@@ -2414,4 +2414,31 @@ describe Topic do
expect { topic.reset_bumped_at }.not_to change { topic.bumped_at }
end
end
+
+ describe "#access_topic_via_group" do
+ let(:open_group) { Fabricate(:group, public_admission: true) }
+ let(:request_group) do
+ Fabricate(:group).tap do |g|
+ g.add_owner(user)
+ g.allow_membership_requests = true
+ g.save!
+ end
+ end
+ let(:category) { Fabricate(:category) }
+ let(:topic) { Fabricate(:topic, category: category) }
+
+ it "returns a group that is open or accepts membership requests and has access to the topic" do
+ expect(topic.access_topic_via_group).to eq(nil)
+
+ category.set_permissions(request_group => :full)
+ category.save!
+
+ expect(topic.access_topic_via_group).to eq(request_group)
+
+ category.set_permissions(request_group => :full, open_group => :full)
+ category.save!
+
+ expect(topic.access_topic_via_group).to eq(open_group)
+ end
+ end
end
diff --git a/spec/requests/topics_controller_spec.rb b/spec/requests/topics_controller_spec.rb
index 758bda7ce83..ad718e80f30 100644
--- a/spec/requests/topics_controller_spec.rb
+++ b/spec/requests/topics_controller_spec.rb
@@ -1279,6 +1279,7 @@ RSpec.describe TopicsController do
context 'permission errors' do
fab!(:allowed_user) { Fabricate(:user) }
let(:allowed_group) { Fabricate(:group) }
+ let(:accessible_group) { Fabricate(:group, public_admission: true) }
let(:secure_category) do
c = Fabricate(:category)
c.permissions = [[allowed_group, :full]]
@@ -1287,6 +1288,12 @@ RSpec.describe TopicsController do
allowed_user.save
c
end
+ let(:accessible_category) do
+ Fabricate(:category).tap do |c|
+ c.set_permissions(accessible_group => :full)
+ c.save!
+ end
+ end
let(:normal_topic) { Fabricate(:topic) }
let(:secure_topic) { Fabricate(:topic, category: secure_category) }
let(:private_topic) { Fabricate(:private_message_topic, user: allowed_user) }
@@ -1294,6 +1301,7 @@ RSpec.describe TopicsController do
let(:deleted_secure_topic) { Fabricate(:topic, category: secure_category, deleted_at: 1.day.ago) }
let(:deleted_private_topic) { Fabricate(:private_message_topic, user: allowed_user, deleted_at: 1.day.ago) }
let(:nonexist_topic_id) { Topic.last.id + 10000 }
+ let(:secure_accessible_topic) { Fabricate(:topic, category: accessible_category) }
shared_examples "various scenarios" do |expected|
expected.each do |key, value|
@@ -1314,7 +1322,8 @@ RSpec.describe TopicsController do
deleted_topic: 410,
deleted_secure_topic: 403,
deleted_private_topic: 403,
- nonexist: 404
+ nonexist: 404,
+ secure_accessible_topic: 403
}
include_examples "various scenarios", expected
end
@@ -1330,7 +1339,8 @@ RSpec.describe TopicsController do
deleted_topic: 302,
deleted_secure_topic: 302,
deleted_private_topic: 302,
- nonexist: 302
+ nonexist: 302,
+ secure_accessible_topic: 302
}
include_examples "various scenarios", expected
end
@@ -1347,7 +1357,8 @@ RSpec.describe TopicsController do
deleted_topic: 410,
deleted_secure_topic: 403,
deleted_private_topic: 403,
- nonexist: 404
+ nonexist: 404,
+ secure_accessible_topic: 200
}
include_examples "various scenarios", expected
end
@@ -1364,7 +1375,8 @@ RSpec.describe TopicsController do
deleted_topic: 410,
deleted_secure_topic: 410,
deleted_private_topic: 410,
- nonexist: 404
+ nonexist: 404,
+ secure_accessible_topic: 200
}
include_examples "various scenarios", expected
end
@@ -1381,7 +1393,8 @@ RSpec.describe TopicsController do
deleted_topic: 200,
deleted_secure_topic: 403,
deleted_private_topic: 403,
- nonexist: 404
+ nonexist: 404,
+ secure_accessible_topic: 200
}
include_examples "various scenarios", expected
end
@@ -1398,7 +1411,8 @@ RSpec.describe TopicsController do
deleted_topic: 200,
deleted_secure_topic: 200,
deleted_private_topic: 200,
- nonexist: 404
+ nonexist: 404,
+ secure_accessible_topic: 200
}
include_examples "various scenarios", expected
end