mirror of
https://github.com/discourse/discourse.git
synced 2026-08-01 09:08:08 -05:00
FEATURE: allow marking solutions in group messages (#37635)
PM support for the solved plugin was intentionally removed in https://github.com/discourse/discourse-solved/pull/334, but multiple communities rely on solved in group messages for private support workflows (e.g. using Discourse as an internal ticket system). Add a new `allow_solved_in_groups` site setting (group list, default empty) that enables the solved UI for group messages belonging to the configured groups. Regular 1-on-1 PMs remain excluded. The implementation adds an early return in `allow_accepted_answers?` for private messages that checks whether the topic's allowed groups intersect with the configured setting. The category/tag logic is extracted into `solved_enabled_for_category?` so callers without a topic object (e.g. the category-change diff in plugin.rb) still work. All existing PM exclusions (reports, badges, filters, directory columns) are intentionally left unchanged. https://meta.discourse.org/t/370496
This commit is contained in:
+2
-2
@@ -16,8 +16,8 @@ module DiscourseSolved
|
||||
|
||||
def can_have_answer
|
||||
return true if SiteSetting.allow_solved_on_all_topics
|
||||
return false if object.closed || object.archived
|
||||
scope.allow_accepted_answers?(object.category_id, object.tags.map(&:name))
|
||||
return false if !object.private_message? && (object.closed || object.archived)
|
||||
scope.allow_accepted_answers?(object)
|
||||
end
|
||||
|
||||
def include_can_have_answer?
|
||||
|
||||
@@ -4,6 +4,7 @@ en:
|
||||
site_settings:
|
||||
solved_enabled: "Enable solved plugin, allow users to select solutions for topics"
|
||||
allow_solved_on_all_topics: "Allow users to select solutions on all topics (when unchecked, solutions can be enabled per category or tag)"
|
||||
allow_solved_in_groups: "Allow marking solutions in group messages for these groups."
|
||||
accept_all_solutions_trust_level: "Minimum trust level required to accept solutions on any topic (even when not OP)"
|
||||
accept_all_solutions_allowed_groups: "Groups that are allowed to accept solutions on any topic (even when not OP). Admins and moderators are always allowed."
|
||||
empty_box_on_unsolved: "Display an empty box next to unsolved topics"
|
||||
|
||||
@@ -8,6 +8,11 @@ discourse_solved:
|
||||
allow_solved_on_all_topics:
|
||||
default: false
|
||||
client: true
|
||||
allow_solved_in_groups:
|
||||
type: group_list
|
||||
default: ""
|
||||
allow_any: false
|
||||
refresh: true
|
||||
accept_all_solutions_trust_level:
|
||||
default: 4
|
||||
client: true
|
||||
|
||||
@@ -19,8 +19,7 @@ class DiscourseSolved::BeforeHeadClose
|
||||
|
||||
return "" if SiteSetting.solved_add_schema_markup == "never"
|
||||
|
||||
allowed =
|
||||
controller.guardian.allow_accepted_answers?(topic.category_id, topic.tags.pluck(:name))
|
||||
allowed = controller.guardian.allow_accepted_answers?(topic)
|
||||
return "" if !allowed
|
||||
|
||||
first_post = topic_view.posts&.first
|
||||
|
||||
@@ -2,35 +2,47 @@
|
||||
|
||||
module DiscourseSolved
|
||||
module GuardianExtensions
|
||||
def allow_accepted_answers?(category_id, tag_names = [])
|
||||
def allow_accepted_answers?(topic)
|
||||
return true if SiteSetting.allow_solved_on_all_topics
|
||||
|
||||
if topic.private_message?
|
||||
return(
|
||||
SiteSetting.allow_solved_in_groups_map.present? &&
|
||||
topic.allowed_groups.exists?(id: SiteSetting.allow_solved_in_groups_map)
|
||||
)
|
||||
end
|
||||
|
||||
solved_enabled_for_category?(topic.category_id, topic.tags.map(&:name))
|
||||
end
|
||||
|
||||
def solved_enabled_for_category?(category_id, tag_names = [])
|
||||
return true if SiteSetting.allow_solved_on_all_topics
|
||||
|
||||
if SiteSetting.enable_solved_tags.present? && tag_names.present?
|
||||
allowed_tags = SiteSetting.enable_solved_tags.split("|")
|
||||
is_allowed = (tag_names & allowed_tags).present?
|
||||
|
||||
return true if is_allowed
|
||||
return true if (tag_names & SiteSetting.enable_solved_tags.split("|")).present?
|
||||
end
|
||||
|
||||
return false if category_id.blank?
|
||||
|
||||
if !DiscourseSolved::AcceptedAnswerCache.allowed
|
||||
DiscourseSolved::AcceptedAnswerCache.reset_accepted_answer_cache
|
||||
end
|
||||
|
||||
DiscourseSolved::AcceptedAnswerCache.allowed.include?(category_id)
|
||||
end
|
||||
|
||||
def can_accept_answer?(topic, post)
|
||||
return false if !authenticated?
|
||||
if !topic || topic.private_message? || !post || post.post_number <= 1 || post.whisper?
|
||||
return false
|
||||
end
|
||||
return false if !allow_accepted_answers?(topic.category_id, topic.tags.map(&:name))
|
||||
return false if !topic || !post || post.post_number <= 1 || post.whisper?
|
||||
return false if !allow_accepted_answers?(topic)
|
||||
|
||||
return true if is_staff?
|
||||
|
||||
if current_user.in_any_groups?(SiteSetting.accept_all_solutions_allowed_groups_map)
|
||||
return true
|
||||
end
|
||||
return true if is_category_group_moderator?(topic.category)
|
||||
|
||||
return true if !topic.private_message? && is_category_group_moderator?(topic.category)
|
||||
|
||||
topic.user_id == current_user.id && !topic.closed && SiteSetting.accept_solutions_topic_author
|
||||
end
|
||||
|
||||
@@ -300,8 +300,8 @@ after_initialize do
|
||||
category_id_changes = topic_changes.diff["category_id"].to_a
|
||||
tag_changes = topic_changes.diff["tags"].to_a
|
||||
|
||||
old_allowed = Guardian.new.allow_accepted_answers?(category_id_changes[0], tag_changes[0])
|
||||
new_allowed = Guardian.new.allow_accepted_answers?(category_id_changes[1], tag_changes[1])
|
||||
old_allowed = Guardian.new.solved_enabled_for_category?(category_id_changes[0], tag_changes[0])
|
||||
new_allowed = Guardian.new.solved_enabled_for_category?(category_id_changes[1], tag_changes[1])
|
||||
|
||||
if old_allowed != new_allowed
|
||||
options[:refresh_stream] = true
|
||||
|
||||
@@ -24,9 +24,51 @@ describe DiscourseSolved::GuardianExtensions do
|
||||
expect(guardian.can_accept_answer?(topic, post)).to eq(false)
|
||||
end
|
||||
|
||||
it "returns false for private messages" do
|
||||
topic.update!(user:, category_id: nil, archetype: Archetype.private_message)
|
||||
expect(guardian.can_accept_answer?(topic, post)).to eq(false)
|
||||
it "returns false for regular private messages" do
|
||||
pm = Fabricate(:private_message_topic, user: user)
|
||||
pm_post = Fabricate(:post, topic: pm, user: other_user)
|
||||
expect(guardian.can_accept_answer?(pm, pm_post)).to eq(false)
|
||||
end
|
||||
|
||||
context "with group messages" do
|
||||
fab!(:group)
|
||||
fab!(:pm_topic) do
|
||||
Fabricate(:group_private_message_topic, user: user, recipient_group: group)
|
||||
end
|
||||
fab!(:pm_op) { Fabricate(:post, topic: pm_topic, user: user) }
|
||||
fab!(:pm_post) { Fabricate(:post, topic: pm_topic, user: other_user) }
|
||||
|
||||
before { SiteSetting.allow_solved_on_all_topics = false }
|
||||
|
||||
it "returns false when allow_solved_in_groups is empty" do
|
||||
SiteSetting.allow_solved_in_groups = ""
|
||||
expect(guardian.can_accept_answer?(pm_topic, pm_post)).to eq(false)
|
||||
end
|
||||
|
||||
it "returns false when the group is not in allow_solved_in_groups" do
|
||||
other_group = Fabricate(:group)
|
||||
SiteSetting.allow_solved_in_groups = other_group.id.to_s
|
||||
expect(guardian.can_accept_answer?(pm_topic, pm_post)).to eq(false)
|
||||
end
|
||||
|
||||
it "returns true for topic author when the group is in allow_solved_in_groups" do
|
||||
SiteSetting.allow_solved_in_groups = group.id.to_s
|
||||
SiteSetting.accept_solutions_topic_author = true
|
||||
expect(guardian.can_accept_answer?(pm_topic, pm_post)).to eq(true)
|
||||
end
|
||||
|
||||
it "returns true for staff when the group is in allow_solved_in_groups" do
|
||||
SiteSetting.allow_solved_in_groups = group.id.to_s
|
||||
admin = Fabricate(:admin, refresh_auto_groups: true)
|
||||
expect(Guardian.new(admin).can_accept_answer?(pm_topic, pm_post)).to eq(true)
|
||||
end
|
||||
|
||||
it "returns false for non-author non-staff users" do
|
||||
SiteSetting.allow_solved_in_groups = group.id.to_s
|
||||
non_author = Fabricate(:user, refresh_auto_groups: true)
|
||||
SiteSetting.accept_all_solutions_allowed_groups = Fabricate(:group).id
|
||||
expect(Guardian.new(non_author).can_accept_answer?(pm_topic, pm_post)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
it "returns false if accepted answers are not allowed" do
|
||||
|
||||
Reference in New Issue
Block a user