FEATURE: add new custom status filters (#286)
* FEATURE: add new custom status filters Those can be used in /filter route. Depends on https://github.com/discourse/discourse/pull/26770 Internal ref. /t/127278 * DEV: pin plugin for Discourse 3.3.0.beta2-dev
This commit is contained in:
parent
a18ce6d712
commit
2c96c5b67c
|
@ -1,3 +1,4 @@
|
||||||
|
< 3.3.0.beta2-dev: a18ce6d712fafed286bcc99543dd173110c6dfb8
|
||||||
< 3.3.0.beta1-dev: 526a44644a7b3f0c2a3ba4fc16e72f364e9fce6d
|
< 3.3.0.beta1-dev: 526a44644a7b3f0c2a3ba4fc16e72f364e9fce6d
|
||||||
< 3.2.0.beta2-dev: 9fbf43e2f077e86f0a1ff769af6036d4e78bfff1
|
< 3.2.0.beta2-dev: 9fbf43e2f077e86f0a1ff769af6036d4e78bfff1
|
||||||
3.1.999: b5d487d6a5bfe2571d936eec5911d02a5f3fcc32
|
3.1.999: b5d487d6a5bfe2571d936eec5911d02a5f3fcc32
|
||||||
|
|
447
plugin.rb
447
plugin.rb
|
@ -9,11 +9,9 @@
|
||||||
|
|
||||||
enabled_site_setting :solved_enabled
|
enabled_site_setting :solved_enabled
|
||||||
|
|
||||||
if respond_to?(:register_svg_icon)
|
register_svg_icon "far fa-check-square"
|
||||||
register_svg_icon "far fa-check-square"
|
register_svg_icon "check-square"
|
||||||
register_svg_icon "check-square"
|
register_svg_icon "far fa-square"
|
||||||
register_svg_icon "far fa-square"
|
|
||||||
end
|
|
||||||
|
|
||||||
register_asset "stylesheets/solutions.scss"
|
register_asset "stylesheets/solutions.scss"
|
||||||
register_asset "stylesheets/mobile/solutions.scss", :mobile
|
register_asset "stylesheets/mobile/solutions.scss", :mobile
|
||||||
|
@ -59,24 +57,20 @@ after_initialize do
|
||||||
p2.custom_fields.delete(IS_ACCEPTED_ANSWER_CUSTOM_FIELD)
|
p2.custom_fields.delete(IS_ACCEPTED_ANSWER_CUSTOM_FIELD)
|
||||||
p2.save!
|
p2.save!
|
||||||
|
|
||||||
if defined?(UserAction::SOLVED)
|
UserAction.where(action_type: UserAction::SOLVED, target_post_id: p2.id).destroy_all
|
||||||
UserAction.where(action_type: UserAction::SOLVED, target_post_id: p2.id).destroy_all
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
post.custom_fields[IS_ACCEPTED_ANSWER_CUSTOM_FIELD] = "true"
|
post.custom_fields[IS_ACCEPTED_ANSWER_CUSTOM_FIELD] = "true"
|
||||||
topic.custom_fields[ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD] = post.id
|
topic.custom_fields[ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD] = post.id
|
||||||
|
|
||||||
if defined?(UserAction::SOLVED)
|
UserAction.log_action!(
|
||||||
UserAction.log_action!(
|
action_type: UserAction::SOLVED,
|
||||||
action_type: UserAction::SOLVED,
|
user_id: post.user_id,
|
||||||
user_id: post.user_id,
|
acting_user_id: acting_user.id,
|
||||||
acting_user_id: acting_user.id,
|
target_post_id: post.id,
|
||||||
target_post_id: post.id,
|
target_topic_id: post.topic_id,
|
||||||
target_topic_id: post.topic_id,
|
)
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
notification_data = {
|
notification_data = {
|
||||||
message: "solved.accepted_notification",
|
message: "solved.accepted_notification",
|
||||||
|
@ -156,9 +150,7 @@ after_initialize do
|
||||||
post.save!
|
post.save!
|
||||||
|
|
||||||
# TODO remove_action! does not allow for this type of interface
|
# TODO remove_action! does not allow for this type of interface
|
||||||
if defined?(UserAction::SOLVED)
|
UserAction.where(action_type: UserAction::SOLVED, target_post_id: post.id).destroy_all
|
||||||
UserAction.where(action_type: UserAction::SOLVED, target_post_id: post.id).destroy_all
|
|
||||||
end
|
|
||||||
|
|
||||||
# yank notification
|
# yank notification
|
||||||
notification =
|
notification =
|
||||||
|
@ -191,12 +183,10 @@ after_initialize do
|
||||||
::TopicViewSerializer.prepend(DiscourseSolved::TopicViewSerializerExtension)
|
::TopicViewSerializer.prepend(DiscourseSolved::TopicViewSerializerExtension)
|
||||||
::Category.prepend(DiscourseSolved::CategoryExtension)
|
::Category.prepend(DiscourseSolved::CategoryExtension)
|
||||||
::PostSerializer.prepend(DiscourseSolved::PostSerializerExtension)
|
::PostSerializer.prepend(DiscourseSolved::PostSerializerExtension)
|
||||||
::UserSummary.prepend(DiscourseSolved::UserSummaryExtension) if defined?(::UserAction::SOLVED)
|
::UserSummary.prepend(DiscourseSolved::UserSummaryExtension)
|
||||||
if respond_to?(:register_topic_list_preload_user_ids)
|
::Topic.attr_accessor(:accepted_answer_user_id)
|
||||||
::Topic.attr_accessor(:accepted_answer_user_id)
|
::TopicPostersSummary.alias_method(:old_user_ids, :user_ids)
|
||||||
::TopicPostersSummary.alias_method(:old_user_ids, :user_ids)
|
::TopicPostersSummary.prepend(DiscourseSolved::TopicPostersSummaryExtension)
|
||||||
::TopicPostersSummary.prepend(DiscourseSolved::TopicPostersSummaryExtension)
|
|
||||||
end
|
|
||||||
[
|
[
|
||||||
::TopicListItemSerializer,
|
::TopicListItemSerializer,
|
||||||
::SearchTopicListItemSerializer,
|
::SearchTopicListItemSerializer,
|
||||||
|
@ -207,7 +197,7 @@ after_initialize do
|
||||||
end
|
end
|
||||||
|
|
||||||
# we got to do a one time upgrade
|
# we got to do a one time upgrade
|
||||||
if !::DiscourseSolved.skip_db? && defined?(UserAction::SOLVED)
|
if !::DiscourseSolved.skip_db?
|
||||||
unless Discourse.redis.get("solved_already_upgraded")
|
unless Discourse.redis.get("solved_already_upgraded")
|
||||||
unless UserAction.where(action_type: UserAction::SOLVED).exists?
|
unless UserAction.where(action_type: UserAction::SOLVED).exists?
|
||||||
Rails.logger.info("Upgrading storage for solved")
|
Rails.logger.info("Upgrading storage for solved")
|
||||||
|
@ -272,65 +262,60 @@ after_initialize do
|
||||||
DiscourseSolved::BeforeHeadClose.new(controller).html
|
DiscourseSolved::BeforeHeadClose.new(controller).html
|
||||||
end
|
end
|
||||||
|
|
||||||
if Report.respond_to?(:add_report)
|
Report.add_report("accepted_solutions") do |report|
|
||||||
Report.add_report("accepted_solutions") do |report|
|
report.data = []
|
||||||
report.data = []
|
|
||||||
|
|
||||||
accepted_solutions =
|
accepted_solutions =
|
||||||
TopicCustomField.where(name: ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD)
|
TopicCustomField.where(name: ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD)
|
||||||
|
|
||||||
category_id, include_subcategories = report.add_category_filter
|
category_id, include_subcategories = report.add_category_filter
|
||||||
if category_id
|
if category_id
|
||||||
if include_subcategories
|
if include_subcategories
|
||||||
accepted_solutions =
|
accepted_solutions =
|
||||||
accepted_solutions.joins(:topic).where(
|
accepted_solutions.joins(:topic).where(
|
||||||
"topics.category_id IN (?)",
|
"topics.category_id IN (?)",
|
||||||
Category.subcategory_ids(category_id),
|
Category.subcategory_ids(category_id),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
accepted_solutions =
|
accepted_solutions =
|
||||||
accepted_solutions.joins(:topic).where("topics.category_id = ?", category_id)
|
accepted_solutions.joins(:topic).where("topics.category_id = ?", category_id)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
accepted_solutions
|
|
||||||
.where("topic_custom_fields.created_at >= ?", report.start_date)
|
|
||||||
.where("topic_custom_fields.created_at <= ?", report.end_date)
|
|
||||||
.group("DATE(topic_custom_fields.created_at)")
|
|
||||||
.order("DATE(topic_custom_fields.created_at)")
|
|
||||||
.count
|
|
||||||
.each { |date, count| report.data << { x: date, y: count } }
|
|
||||||
report.total = accepted_solutions.count
|
|
||||||
report.prev30Days =
|
|
||||||
accepted_solutions
|
|
||||||
.where("topic_custom_fields.created_at >= ?", report.start_date - 30.days)
|
|
||||||
.where("topic_custom_fields.created_at <= ?", report.start_date)
|
|
||||||
.count
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
accepted_solutions
|
||||||
|
.where("topic_custom_fields.created_at >= ?", report.start_date)
|
||||||
|
.where("topic_custom_fields.created_at <= ?", report.end_date)
|
||||||
|
.group("DATE(topic_custom_fields.created_at)")
|
||||||
|
.order("DATE(topic_custom_fields.created_at)")
|
||||||
|
.count
|
||||||
|
.each { |date, count| report.data << { x: date, y: count } }
|
||||||
|
report.total = accepted_solutions.count
|
||||||
|
report.prev30Days =
|
||||||
|
accepted_solutions
|
||||||
|
.where("topic_custom_fields.created_at >= ?", report.start_date - 30.days)
|
||||||
|
.where("topic_custom_fields.created_at <= ?", report.start_date)
|
||||||
|
.count
|
||||||
end
|
end
|
||||||
|
|
||||||
if respond_to?(:register_modifier)
|
register_modifier(:search_rank_sort_priorities) do |priorities, _search|
|
||||||
register_modifier(:search_rank_sort_priorities) do |priorities, search|
|
if SiteSetting.prioritize_solved_topics_in_search
|
||||||
if SiteSetting.prioritize_solved_topics_in_search
|
condition = <<~SQL
|
||||||
condition = <<~SQL
|
EXISTS (
|
||||||
EXISTS
|
SELECT 1
|
||||||
(
|
FROM topic_custom_fields
|
||||||
SELECT 1 FROM topic_custom_fields f
|
WHERE topic_id = topics.id
|
||||||
WHERE topics.id = f.topic_id
|
AND name = '#{::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD}'
|
||||||
AND f.name = '#{::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD}'
|
AND value IS NOT NULL
|
||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
priorities.push([condition, 1.1])
|
priorities.push([condition, 1.1])
|
||||||
else
|
else
|
||||||
priorities
|
priorities
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if defined?(::UserAction::SOLVED)
|
add_to_serializer(:user_summary, :solved_count) { object.solved_count }
|
||||||
add_to_serializer(:user_summary, :solved_count) { object.solved_count }
|
|
||||||
end
|
|
||||||
add_to_serializer(:post, :can_accept_answer) do
|
add_to_serializer(:post, :can_accept_answer) do
|
||||||
scope.can_accept_answer?(topic, object) && object.post_number > 1 && !accepted_answer
|
scope.can_accept_answer?(topic, object) && object.post_number > 1 && !accepted_answer
|
||||||
end
|
end
|
||||||
|
@ -344,107 +329,91 @@ after_initialize do
|
||||||
topic&.custom_fields&.[](::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD).present?
|
topic&.custom_fields&.[](::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD).present?
|
||||||
end
|
end
|
||||||
|
|
||||||
#TODO Remove when plugin is 1.0
|
solved_callback = ->(scope) do
|
||||||
if Search.respond_to? :advanced_filter
|
sql = <<~SQL
|
||||||
Search.advanced_filter(/status:solved/) do |posts|
|
topics.id IN (
|
||||||
posts.where(
|
SELECT topic_id
|
||||||
"topics.id IN (
|
FROM topic_custom_fields
|
||||||
SELECT tc.topic_id
|
WHERE name = '#{::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD}'
|
||||||
FROM topic_custom_fields tc
|
AND value IS NOT NULL
|
||||||
WHERE tc.name = '#{::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD}' AND
|
|
||||||
tc.value IS NOT NULL
|
|
||||||
)",
|
|
||||||
)
|
)
|
||||||
end
|
SQL
|
||||||
|
|
||||||
Search.advanced_filter(/status:unsolved/) do |posts|
|
scope.where(sql)
|
||||||
if SiteSetting.allow_solved_on_all_topics
|
|
||||||
posts.where(
|
|
||||||
"topics.id NOT IN (
|
|
||||||
SELECT tc.topic_id
|
|
||||||
FROM topic_custom_fields tc
|
|
||||||
WHERE tc.name = '#{::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD}' AND
|
|
||||||
tc.value IS NOT NULL
|
|
||||||
)",
|
|
||||||
)
|
|
||||||
else
|
|
||||||
tag_ids = Tag.where(name: SiteSetting.enable_solved_tags.split("|")).pluck(:id)
|
|
||||||
|
|
||||||
posts.where(
|
|
||||||
"topics.id NOT IN (
|
|
||||||
SELECT tc.topic_id
|
|
||||||
FROM topic_custom_fields tc
|
|
||||||
WHERE tc.name = '#{::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD}' AND
|
|
||||||
tc.value IS NOT NULL
|
|
||||||
) AND (topics.id IN (
|
|
||||||
SELECT top.id
|
|
||||||
FROM topics top
|
|
||||||
INNER JOIN category_custom_fields cc
|
|
||||||
ON top.category_id = cc.category_id
|
|
||||||
WHERE cc.name = '#{::DiscourseSolved::ENABLE_ACCEPTED_ANSWERS_CUSTOM_FIELD}' AND
|
|
||||||
cc.value = 'true'
|
|
||||||
) OR topics.id IN (
|
|
||||||
SELECT top.id
|
|
||||||
FROM topics top
|
|
||||||
INNER JOIN topic_tags tt
|
|
||||||
ON top.id = tt.topic_id
|
|
||||||
WHERE tt.tag_id IN (?)
|
|
||||||
))",
|
|
||||||
tag_ids,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if Discourse.has_needed_version?(Discourse::VERSION::STRING, "1.8.0.beta6")
|
unsolved_callback = ->(scope) do
|
||||||
TopicQuery.add_custom_filter(:solved) do |results, topic_query|
|
scope = scope.where <<~SQL
|
||||||
if topic_query.options[:solved] == "yes"
|
topics.id NOT IN (
|
||||||
results =
|
SELECT topic_id
|
||||||
results.where(
|
FROM topic_custom_fields
|
||||||
"topics.id IN (
|
WHERE name = '#{::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD}'
|
||||||
SELECT tc.topic_id
|
AND value IS NOT NULL
|
||||||
FROM topic_custom_fields tc
|
)
|
||||||
WHERE tc.name = '#{::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD}' AND
|
SQL
|
||||||
tc.value IS NOT NULL
|
|
||||||
)",
|
if !SiteSetting.allow_solved_on_all_topics
|
||||||
)
|
tag_ids = Tag.where(name: SiteSetting.enable_solved_tags.split("|")).pluck(:id)
|
||||||
elsif topic_query.options[:solved] == "no"
|
|
||||||
results =
|
scope = scope.where <<~SQL, tag_ids
|
||||||
results.where(
|
topics.id IN (
|
||||||
"topics.id NOT IN (
|
SELECT t.id
|
||||||
SELECT tc.topic_id
|
FROM topics t
|
||||||
FROM topic_custom_fields tc
|
JOIN category_custom_fields cc
|
||||||
WHERE tc.name = '#{::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD}' AND
|
ON t.category_id = cc.category_id
|
||||||
tc.value IS NOT NULL
|
AND cc.name = '#{::DiscourseSolved::ENABLE_ACCEPTED_ANSWERS_CUSTOM_FIELD}'
|
||||||
)",
|
AND cc.value = 'true'
|
||||||
)
|
)
|
||||||
end
|
OR
|
||||||
|
topics.id IN (
|
||||||
|
SELECT topic_id
|
||||||
|
FROM topic_tags
|
||||||
|
WHERE tag_id IN (?)
|
||||||
|
)
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
scope
|
||||||
|
end
|
||||||
|
|
||||||
|
register_custom_filter_by_status("solved", &solved_callback)
|
||||||
|
register_custom_filter_by_status("unsolved", &unsolved_callback)
|
||||||
|
|
||||||
|
register_search_advanced_filter(/status:solved/, &solved_callback)
|
||||||
|
register_search_advanced_filter(/status:unsolved/, &unsolved_callback)
|
||||||
|
|
||||||
|
TopicQuery.add_custom_filter(:solved) do |results, topic_query|
|
||||||
|
if topic_query.options[:solved] == "yes"
|
||||||
|
solved_callback.call(results)
|
||||||
|
elsif topic_query.options[:solved] == "no"
|
||||||
|
unsolved_callback.call(results)
|
||||||
|
else
|
||||||
results
|
results
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if TopicList.respond_to? :preloaded_custom_fields
|
TopicList.preloaded_custom_fields << ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD
|
||||||
TopicList.preloaded_custom_fields << ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD
|
Site.preloaded_category_custom_fields << ::DiscourseSolved::ENABLE_ACCEPTED_ANSWERS_CUSTOM_FIELD
|
||||||
end
|
Search.preloaded_topic_custom_fields << ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD
|
||||||
if Site.respond_to? :preloaded_category_custom_fields
|
CategoryList.preloaded_topic_custom_fields << ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD
|
||||||
Site.preloaded_category_custom_fields << ::DiscourseSolved::ENABLE_ACCEPTED_ANSWERS_CUSTOM_FIELD
|
|
||||||
end
|
|
||||||
if Search.respond_to? :preloaded_topic_custom_fields
|
|
||||||
Search.preloaded_topic_custom_fields << ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD
|
|
||||||
end
|
|
||||||
|
|
||||||
if CategoryList.respond_to?(:preloaded_topic_custom_fields)
|
on(:filter_auto_bump_topics) do |_category, filters|
|
||||||
CategoryList.preloaded_topic_custom_fields << ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD
|
filters.push(
|
||||||
end
|
->(r) do
|
||||||
|
sql = <<~SQL
|
||||||
|
NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM topic_custom_fields
|
||||||
|
WHERE topic_id = topics.id
|
||||||
|
AND name = '#{::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD}'
|
||||||
|
AND value IS NOT NULL
|
||||||
|
)
|
||||||
|
SQL
|
||||||
|
|
||||||
on(:filter_auto_bump_topics) { |_category, filters| filters.push(->(r) { r.where(<<~SQL) }) }
|
r.where(sql)
|
||||||
NOT EXISTS(
|
end,
|
||||||
SELECT 1 FROM topic_custom_fields
|
)
|
||||||
WHERE topic_id = topics.id
|
end
|
||||||
AND name = '#{::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD}'
|
|
||||||
AND value IS NOT NULL
|
|
||||||
)
|
|
||||||
SQL
|
|
||||||
|
|
||||||
on(:before_post_publish_changes) do |post_changes, topic_changes, options|
|
on(:before_post_publish_changes) do |post_changes, topic_changes, options|
|
||||||
category_id_changes = topic_changes.diff["category_id"].to_a
|
category_id_changes = topic_changes.diff["category_id"].to_a
|
||||||
|
@ -521,7 +490,7 @@ after_initialize do
|
||||||
AND di.period_type = :period_type
|
AND di.period_type = :period_type
|
||||||
AND di.solutions <> x.solutions
|
AND di.solutions <> x.solutions
|
||||||
"
|
"
|
||||||
add_directory_column("solutions", query: query) if respond_to?(:add_directory_column)
|
add_directory_column("solutions", query: query)
|
||||||
|
|
||||||
add_to_class(:composer_messages_finder, :check_topic_is_solved) do
|
add_to_class(:composer_messages_finder, :check_topic_is_solved) do
|
||||||
return if !SiteSetting.solved_enabled || SiteSetting.disable_solved_education_message
|
return if !SiteSetting.solved_enabled || SiteSetting.disable_solved_education_message
|
||||||
|
@ -538,88 +507,82 @@ after_initialize do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
if defined?(UserAction::SOLVED)
|
add_to_serializer(:user_card, :accepted_answers) do
|
||||||
add_to_serializer(:user_card, :accepted_answers) do
|
UserAction.where(user_id: object.id).where(action_type: UserAction::SOLVED).count
|
||||||
UserAction.where(user_id: object.id).where(action_type: UserAction::SOLVED).count
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if respond_to?(:register_topic_list_preload_user_ids)
|
register_topic_list_preload_user_ids do |topics, user_ids, topic_list|
|
||||||
register_topic_list_preload_user_ids do |topics, user_ids, topic_list|
|
answer_post_ids =
|
||||||
answer_post_ids =
|
TopicCustomField
|
||||||
TopicCustomField
|
.select("value::INTEGER")
|
||||||
.select("value::INTEGER")
|
.where(name: ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD)
|
||||||
.where(name: ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD)
|
.where(topic_id: topics.map(&:id))
|
||||||
.where(topic_id: topics.map(&:id))
|
answer_user_ids = Post.where(id: answer_post_ids).pluck(:topic_id, :user_id).to_h
|
||||||
answer_user_ids = Post.where(id: answer_post_ids).pluck(:topic_id, :user_id).to_h
|
topics.each { |topic| topic.accepted_answer_user_id = answer_user_ids[topic.id] }
|
||||||
topics.each { |topic| topic.accepted_answer_user_id = answer_user_ids[topic.id] }
|
user_ids.concat(answer_user_ids.values)
|
||||||
user_ids.concat(answer_user_ids.values)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if defined?(DiscourseAutomation)
|
if defined?(DiscourseAutomation)
|
||||||
if respond_to?(:add_triggerable_to_scriptable)
|
on(:accepted_solution) do |post|
|
||||||
on(:accepted_solution) do |post|
|
# testing directly automation is prone to issues
|
||||||
# testing directly automation is prone to issues
|
# we prefer to abstract logic in service object and test this
|
||||||
# we prefer to abstract logic in service object and test this
|
next if Rails.env.test?
|
||||||
next if Rails.env.test?
|
|
||||||
|
|
||||||
name = "first_accepted_solution"
|
name = "first_accepted_solution"
|
||||||
DiscourseAutomation::Automation
|
DiscourseAutomation::Automation
|
||||||
.where(trigger: name, enabled: true)
|
.where(trigger: name, enabled: true)
|
||||||
.find_each do |automation|
|
.find_each do |automation|
|
||||||
maximum_trust_level = automation.trigger_field("maximum_trust_level")&.dig("value")
|
maximum_trust_level = automation.trigger_field("maximum_trust_level")&.dig("value")
|
||||||
if FirstAcceptedPostSolutionValidator.check(post, trust_level: maximum_trust_level)
|
if FirstAcceptedPostSolutionValidator.check(post, trust_level: maximum_trust_level)
|
||||||
automation.trigger!(
|
automation.trigger!(
|
||||||
"kind" => name,
|
"kind" => name,
|
||||||
"accepted_post_id" => post.id,
|
"accepted_post_id" => post.id,
|
||||||
"usernames" => [post.user.username],
|
"usernames" => [post.user.username],
|
||||||
"placeholders" => {
|
"placeholders" => {
|
||||||
"post_url" => Discourse.base_url + post.url,
|
"post_url" => Discourse.base_url + post.url,
|
||||||
},
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
add_triggerable_to_scriptable(:first_accepted_solution, :send_pms)
|
|
||||||
|
|
||||||
DiscourseAutomation::Triggerable.add(:first_accepted_solution) do
|
|
||||||
placeholder :post_url
|
|
||||||
|
|
||||||
field :maximum_trust_level,
|
|
||||||
component: :choices,
|
|
||||||
extra: {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name:
|
|
||||||
"discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name:
|
|
||||||
"discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl2",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name:
|
|
||||||
"discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl3",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name:
|
|
||||||
"discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl4",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "any",
|
|
||||||
name:
|
|
||||||
"discourse_automation.triggerables.first_accepted_solution.max_trust_level.any",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
required: true
|
)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
add_triggerable_to_scriptable(:first_accepted_solution, :send_pms)
|
||||||
|
|
||||||
|
DiscourseAutomation::Triggerable.add(:first_accepted_solution) do
|
||||||
|
placeholder :post_url
|
||||||
|
|
||||||
|
field :maximum_trust_level,
|
||||||
|
component: :choices,
|
||||||
|
extra: {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name:
|
||||||
|
"discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name:
|
||||||
|
"discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name:
|
||||||
|
"discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name:
|
||||||
|
"discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "any",
|
||||||
|
name:
|
||||||
|
"discourse_automation.triggerables.first_accepted_solution.max_trust_level.any",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
required: true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,68 @@ RSpec.describe "Managing Posts solved status" do
|
||||||
|
|
||||||
before { SiteSetting.allow_solved_on_all_topics = true }
|
before { SiteSetting.allow_solved_on_all_topics = true }
|
||||||
|
|
||||||
|
describe "customer filters" do
|
||||||
|
before do
|
||||||
|
SiteSetting.allow_solved_on_all_topics = false
|
||||||
|
SiteSetting.enable_solved_tags = solvable_tag.name
|
||||||
|
end
|
||||||
|
|
||||||
|
fab!(:solvable_category) do
|
||||||
|
category = Fabricate(:category)
|
||||||
|
|
||||||
|
CategoryCustomField.create(
|
||||||
|
category_id: category.id,
|
||||||
|
name: ::DiscourseSolved::ENABLE_ACCEPTED_ANSWERS_CUSTOM_FIELD,
|
||||||
|
value: "true",
|
||||||
|
)
|
||||||
|
|
||||||
|
category
|
||||||
|
end
|
||||||
|
|
||||||
|
fab!(:solvable_tag) { Fabricate(:tag) }
|
||||||
|
|
||||||
|
fab!(:solved_in_category) do
|
||||||
|
Fabricate(
|
||||||
|
:custom_topic,
|
||||||
|
category: solvable_category,
|
||||||
|
custom_topic_name: ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD,
|
||||||
|
value: "42",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
fab!(:solved_in_tag) do
|
||||||
|
Fabricate(
|
||||||
|
:custom_topic,
|
||||||
|
tags: [solvable_tag],
|
||||||
|
custom_topic_name: ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD,
|
||||||
|
value: "42",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
fab!(:unsolved_in_category) { Fabricate(:topic, category: solvable_category) }
|
||||||
|
fab!(:unsolved_in_tag) { Fabricate(:topic, tags: [solvable_tag]) }
|
||||||
|
|
||||||
|
fab!(:unsolved_topic) { Fabricate(:topic) }
|
||||||
|
|
||||||
|
it "can filter by solved status" do
|
||||||
|
expect(
|
||||||
|
TopicsFilter
|
||||||
|
.new(guardian: Guardian.new)
|
||||||
|
.filter_from_query_string("status:solved")
|
||||||
|
.pluck(:id),
|
||||||
|
).to contain_exactly(solved_in_category.id, solved_in_tag.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can filter by unsolved status" do
|
||||||
|
expect(
|
||||||
|
TopicsFilter
|
||||||
|
.new(guardian: Guardian.new)
|
||||||
|
.filter_from_query_string("status:unsolved")
|
||||||
|
.pluck(:id),
|
||||||
|
).to contain_exactly(unsolved_in_category.id, unsolved_in_tag.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "search" do
|
describe "search" do
|
||||||
before { SearchIndexer.enable }
|
before { SearchIndexer.enable }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue