forked from communiteq/discourse-private-topics
-
Notifications
You must be signed in to change notification settings - Fork 0
/
plugin.rb
196 lines (163 loc) · 7.55 KB
/
plugin.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# name: discourse-private-topics
# about: Communiteq private topics plugin
# version: 1.5
# authors: [email protected]
# url: https://github.com/communiteq/discourse-private-topics
enabled_site_setting :private_topics_enabled
module ::DiscoursePrivateTopics
# gets a list of user ids we should always show topics for
def DiscoursePrivateTopics.get_unfiltered_user_ids(user)
user_ids = [ Discourse.system_user.id ]
user_ids.append user.id if user && !user.anonymous?
group_ids = SiteSetting.private_topics_permitted_groups.split("|").map(&:to_i)
user_ids = user_ids + Group.where(id: group_ids).joins(:users).pluck('users.id')
user_ids.uniq
end
# gets a list of category ids we should not show topics for (unless the user is unfiltered)
def DiscoursePrivateTopics.get_filtered_category_ids(user)
return [] unless SiteSetting.private_topics_enabled
# first get all the categories with private topics enabled
cat_ids = CategoryCustomField.where(name: 'private_topics_enabled').where(value: 'true').pluck(:category_id).to_a
# we need to initialize the hash in case there are categories without whitelisted groups, or if we're anonymous user
cat_group_map = cat_ids.map { |i| [i, []] }.to_h
# remove the categories that have a whitelisted group we're a member of
if user
# get the groups that are excluded from filtering for each category
excluded_map = CategoryCustomField.
where(category_id: cat_ids).
where(name: 'private_topics_allowed_groups').
each_with_object({}) do |record, h|
h[record.category_id] = record.value.split(',').map(&:to_i)
end
cat_group_map.merge! (excluded_map)
# compare them to the groups we're member of
# so we end up with a list of category ids that we cannot see other peoples topics in
user_group_ids = user.groups.pluck(:id).to_a
cat_group_map = cat_group_map.reject { |k, v| (v & user_group_ids).any? }
end
filtered_category_ids = cat_group_map.keys
end
end
after_initialize do
# hide topics from search results
module PrivateTopicsPatchSearch
def execute(readonly_mode: @readonly_mode)
super
if SiteSetting.private_topics_enabled && !(SiteSetting.private_topics_admin_sees_all & @guardian&.user&.admin?)
cat_ids = DiscoursePrivateTopics.get_filtered_category_ids(@guardian.user)
unless cat_ids.empty?
user_ids = DiscoursePrivateTopics.get_unfiltered_user_ids(@guardian.user)
@results.posts.delete_if do |post|
next false if user_ids.include? post&.user&.id
post&.topic&.category&.id && cat_ids.include?(post.topic.category&.id)
end
end
end
@results
end
end
# hide topics on from post stream and raw
module ::TopicGuardian
alias_method :org_can_see_topic?, :can_see_topic?
def can_see_topic?(topic, hide_deleted = true)
allowed = org_can_see_topic?(topic, hide_deleted)
return false unless allowed # false stays false
if SiteSetting.private_topics_enabled && !(SiteSetting.private_topics_admin_sees_all & @user&.admin?)
return true unless topic&.category # skip for PM's
user_ids = DiscoursePrivateTopics.get_unfiltered_user_ids(@user)
return true if user_ids.include?(topic&.user&.id) # topic authors and permitted users are always good
cat_ids = DiscoursePrivateTopics.get_filtered_category_ids(@user)
return true if cat_ids.empty?
return false if cat_ids.include?(topic.category&.id)
end
true
end
end
# hide topics from user profile -> activity
class ::UserAction
module PrivateTopicsApplyCommonFilters
def apply_common_filters(builder, user_id, guardian, ignore_private_messages=false)
if SiteSetting.private_topics_enabled && !(SiteSetting.private_topics_admin_sees_all & guardian&.user&.admin?)
cat_ids = DiscoursePrivateTopics.get_filtered_category_ids(guardian.user).join(",")
unless cat_ids.empty?
user_ids = DiscoursePrivateTopics.get_unfiltered_user_ids(guardian.user).join(",")
builder.where("(t.category_id NOT IN (#{cat_ids}) OR p.user_id IN (#{user_ids}))")
end
end
super(builder, user_id, guardian, ignore_private_messages)
end
end
singleton_class.prepend PrivateTopicsApplyCommonFilters
end
# hide topics from user profile -> summary
module PrivateTopicsPatchUserSummary
def filtered_category_ids
@cat_ids ||= DiscoursePrivateTopics.get_filtered_category_ids(@guardian&.user).join(",")
end
def unfiltered_user_ids
@user_ids ||= DiscoursePrivateTopics.get_unfiltered_user_ids(@guardian&.user).join(",")
end
def topics
if SiteSetting.private_topics_enabled && !(SiteSetting.private_topics_admin_sees_all & @guardian&.user&.admin?) && !filtered_category_ids.empty?
return super.where("(topics.category_id NOT IN (#{filtered_category_ids}) OR topics.user_id IN (#{unfiltered_user_ids}))")
end
super
end
def replies
if SiteSetting.private_topics_enabled && !(SiteSetting.private_topics_admin_sees_all & @guardian&.user&.admin?) && !filtered_category_ids.empty?
return super.where("(topics.category_id NOT IN (#{filtered_category_ids}) OR topics.user_id IN (#{unfiltered_user_ids}))")
end
super
end
def links
if SiteSetting.private_topics_enabled && !(SiteSetting.private_topics_admin_sees_all & @guardian&.user&.admin?) && !filtered_category_ids.empty?
return super.where("(topics.category_id NOT IN (#{filtered_category_ids}) OR topics.user_id IN (#{unfiltered_user_ids}))")
end
super
end
end
# don't send follow plugin notifications for the entire category (regardless of whether a user can see)
module PrivateTopicsFollowNotificationHandler
def handle
return if post&.topic&.category&.id && DiscoursePrivateTopics.get_filtered_category_ids.include?(post.topic.category.id)
super
end
end
Site.preloaded_category_custom_fields << 'private_topics_enabled'
Site.preloaded_category_custom_fields << 'private_topics_allowed_groups'
# this removes the categories from the "recent topics" shown on the 404 page
# called from ApplicationController.build_not_found_page
# this is cached without a user so just pass nil and exclude every private category
class ::Topic
def self.recent(max = 10)
cat_ids = DiscoursePrivateTopics.get_filtered_category_ids(nil).join(",")
if cat_ids.empty?
Topic.listable_topics.visible.secured.order("created_at desc").limit(max)
else
Topic.listable_topics.visible.secured.where("category_id NOT IN (#{cat_ids})").order("created_at desc").limit(max)
end
end
end
class ::Search
prepend PrivateTopicsPatchSearch
end
class ::UserSummary
prepend PrivateTopicsPatchUserSummary
end
if defined?(Follow::NotificationHandler)
class ::Follow::NotificationHandler
prepend PrivateTopicsFollowNotificationHandler
end
end
# hide topics from topic lists
TopicQuery.add_custom_filter(:private_topics) do |result, query|
if SiteSetting.private_topics_enabled && ! (SiteSetting.private_topics_admin_sees_all && query&.guardian&.user&.admin?)
cat_ids = DiscoursePrivateTopics.get_filtered_category_ids(query&.guardian&.user).join(",")
unless cat_ids.empty?
user_ids = DiscoursePrivateTopics.get_unfiltered_user_ids(query&.guardian&.user).join(",")
result = result.where("(topics.category_id NOT IN (#{cat_ids}) OR topics.user_id IN (#{user_ids}))")
end
end
result
end
end