DEV: Fix new Rubocop offenses

This commit is contained in:
Loïc Guitaut 2024-03-05 12:14:32 +01:00 committed by Loïc Guitaut
parent b50b118a79
commit 4c6ddcf08d
13 changed files with 418 additions and 383 deletions

View File

@ -1,11 +1,30 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (7.1.3.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.6)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
drb (2.2.1)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
json (2.7.1)
language_server-protocol (3.17.0.3)
minitest (5.22.2)
mutex_m (0.2.0)
parallel (1.24.0)
parser (3.3.0.4)
parser (3.3.0.5)
ast (~> 2.4.1)
racc
prettier_print (1.2.1)
@ -13,7 +32,7 @@ GEM
rainbow (3.1.1)
regexp_parser (2.9.0)
rexml (3.2.6)
rubocop (1.60.0)
rubocop (1.61.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
@ -24,22 +43,27 @@ GEM
rubocop-ast (>= 1.30.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
rubocop-ast (1.31.1)
parser (>= 3.3.0.4)
rubocop-capybara (2.20.0)
rubocop (~> 1.41)
rubocop-discourse (3.6.0)
rubocop-discourse (3.7.1)
activesupport (>= 6.1)
rubocop (>= 1.59.0)
rubocop-capybara (>= 2.0.0)
rubocop-factory_bot (>= 2.0.0)
rubocop-rspec (>= 2.25.0)
rubocop-factory_bot (2.25.1)
rubocop (~> 1.41)
rubocop-rspec (2.26.1)
rubocop-rspec (2.27.1)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
syntax_tree (6.2.0)
prettier_print (>= 1.2.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
PLATFORMS

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
class DiscourseSolved::AnswerController < ::ApplicationController
requires_plugin DiscourseSolved::PLUGIN_NAME
def accept
limit_accepts
post = Post.find(params[:id].to_i)
topic = post.topic
topic ||= Topic.with_deleted.find(post.topic_id) if guardian.is_staff?
guardian.ensure_can_accept_answer!(topic, post)
DiscourseSolved.accept_answer!(post, current_user, topic: topic)
render json: success_json
end
def unaccept
limit_accepts
post = Post.find(params[:id].to_i)
topic = post.topic
topic ||= Topic.with_deleted.find(post.topic_id) if guardian.is_staff?
guardian.ensure_can_accept_answer!(topic, post)
DiscourseSolved.unaccept_answer!(post, topic: topic)
render json: success_json
end
def limit_accepts
return if current_user.staff?
RateLimiter.new(nil, "accept-hr-#{current_user.id}", 20, 1.hour).performed!
RateLimiter.new(nil, "accept-min-#{current_user.id}", 4, 30.seconds).performed!
end
end

View File

@ -0,0 +1,85 @@
# frozen_string_literal: true
class DiscourseSolved::BeforeHeadClose
attr_reader :controller
def initialize(controller)
@controller = controller
end
def html
return "" if !controller.instance_of? TopicsController
topic_view = controller.instance_variable_get(:@topic_view)
topic = topic_view&.topic
return "" if !topic
# note, we have canonicals so we only do this for page 1 at the moment
# it can get confusing to have this on every page and it should make page 1
# a bit more prominent + cut down on pointless work
return "" if SiteSetting.solved_add_schema_markup == "never"
allowed =
controller.guardian.allow_accepted_answers?(topic.category_id, topic.tags.pluck(:name))
return "" if !allowed
first_post = topic_view.posts&.first
return "" if first_post&.post_number != 1
question_json = {
"@type" => "Question",
"name" => topic.title,
"text" => get_schema_text(first_post),
"upvoteCount" => first_post.like_count,
"answerCount" => 0,
"datePublished" => topic.created_at,
"author" => {
"@type" => "Person",
"name" => topic.user&.username,
"url" => topic.user&.full_url,
},
}
if accepted_answer =
Post.find_by(
id: topic.custom_fields[::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD],
)
question_json["answerCount"] = 1
question_json[:acceptedAnswer] = {
"@type" => "Answer",
"text" => get_schema_text(accepted_answer),
"upvoteCount" => accepted_answer.like_count,
"datePublished" => accepted_answer.created_at,
"url" => accepted_answer.full_url,
"author" => {
"@type" => "Person",
"name" => accepted_answer.user&.username,
"url" => accepted_answer.user&.full_url,
},
}
else
return "" if SiteSetting.solved_add_schema_markup == "answered only"
end
[
'<script type="application/ld+json">',
MultiJson
.dump(
"@context" => "http://schema.org",
"@type" => "QAPage",
"name" => topic&.title,
"mainEntity" => question_json,
)
.gsub("</", "<\\/")
.html_safe,
"</script>",
].join("")
end
private
def get_schema_text(post)
post.excerpt(nil, keep_onebox_body: true).presence ||
post.excerpt(nil, keep_onebox_body: true, keep_quotes: true)
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
module DiscourseSolved::CategoryExtension
extend ActiveSupport::Concern
prepended { after_save :reset_accepted_cache, if: -> { SiteSetting.solved_enabled? } }
private
def reset_accepted_cache
::DiscourseSolved::AcceptedAnswerCache.reset_accepted_answer_cache
end
end

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
module DiscourseSolved::PostSerializerExtension
extend ActiveSupport::Concern
private
def topic
topic_view&.topic || object.topic
end
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
module DiscourseSolved::TopicPostersSummaryExtension
extend ActiveSupport::Concern
def descriptions_by_id
if !defined?(@descriptions_by_id)
super(ids: old_user_ids)
if id = topic.accepted_answer_user_id
@descriptions_by_id[id] ||= []
@descriptions_by_id[id] << I18n.t(:accepted_answer)
end
end
super
end
def last_poster_is_topic_creator?
super || topic.accepted_answer_user_id == topic.last_post_user_id
end
def user_ids
if id = topic.accepted_answer_user_id
super.insert(1, id)
else
super
end
end
end

View File

@ -0,0 +1,54 @@
# frozen_string_literal: true
module DiscourseSolved::TopicViewSerializerExtension
extend ActiveSupport::Concern
prepended { attributes :accepted_answer }
def include_accepted_answer?
SiteSetting.solved_enabled? && accepted_answer_post_id
end
def accepted_answer
if info = accepted_answer_post_info
{ post_number: info[0], username: info[1], excerpt: info[2], name: info[3] }
end
end
private
def accepted_answer_post_info
post_info =
if post = object.posts.find { |p| p.post_number == accepted_answer_post_id }
[post.post_number, post.user.username, post.cooked, post.user.name]
else
Post
.where(id: accepted_answer_post_id, topic_id: object.topic.id)
.joins(:user)
.pluck("post_number", "username", "cooked", "name")
.first
end
if post_info
post_info[2] = if SiteSetting.solved_quote_length > 0
PrettyText.excerpt(post_info[2], SiteSetting.solved_quote_length, keep_emoji_images: true)
else
nil
end
post_info[3] = nil if !SiteSetting.enable_names || !SiteSetting.display_name_on_posts
post_info
end
end
def accepted_answer_post_id
id = object.topic.custom_fields[::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD]
# a bit messy but race conditions can give us an array here, avoid
begin
id && id.to_i
rescue StandardError
nil
end
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
module DiscourseSolved::UserSummaryExtension
extend ActiveSupport::Concern
def solved_count
UserAction.where(user: @user).where(action_type: UserAction::SOLVED).count
end
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
module DiscourseSolved::WebHookExtension
extend ActiveSupport::Concern
class_methods do
def enqueue_solved_hooks(event, post, payload = nil)
if active_web_hooks(event).exists? && post.present?
payload ||= WebHook.generate_payload(:post, post)
WebHook.enqueue_hooks(
:solved,
event,
id: post.id,
category_id: post.topic&.category_id,
tag_ids: post.topic&.tags&.pluck(:id),
payload: payload,
)
end
end
end
end

492
plugin.rb
View File

@ -15,74 +15,39 @@ if respond_to?(:register_svg_icon)
register_svg_icon "far fa-square"
end
PLUGIN_NAME = "discourse_solved"
register_asset "stylesheets/solutions.scss"
register_asset "stylesheets/mobile/solutions.scss", :mobile
after_initialize do
SeedFu.fixture_paths << Rails.root.join("plugins", "discourse-solved", "db", "fixtures").to_s
%w[
../app/lib/first_accepted_post_solution_validator.rb
../app/lib/accepted_answer_cache.rb
../app/lib/guardian_extensions.rb
../app/serializers/concerns/topic_answer_mixin.rb
].each { |path| load File.expand_path(path, __FILE__) }
skip_db = defined?(GlobalSetting.skip_db?) && GlobalSetting.skip_db?
reloadable_patch { |plugin| Guardian.prepend(DiscourseSolved::GuardianExtensions) }
# we got to do a one time upgrade
if !skip_db && defined?(UserAction::SOLVED)
unless Discourse.redis.get("solved_already_upgraded")
unless UserAction.where(action_type: UserAction::SOLVED).exists?
Rails.logger.info("Upgrading storage for solved")
sql = <<SQL
INSERT INTO user_actions(action_type,
user_id,
target_topic_id,
target_post_id,
acting_user_id,
created_at,
updated_at)
SELECT :solved,
p.user_id,
p.topic_id,
p.id,
t.user_id,
pc.created_at,
pc.updated_at
FROM
post_custom_fields pc
JOIN
posts p ON p.id = pc.post_id
JOIN
topics t ON t.id = p.topic_id
WHERE
pc.name = 'is_accepted_answer' AND
pc.value = 'true' AND
p.user_id IS NOT NULL
SQL
DB.exec(sql, solved: UserAction::SOLVED)
end
Discourse.redis.set("solved_already_upgraded", "true")
end
end
module ::DiscourseSolved
class Engine < ::Rails::Engine
engine_name PLUGIN_NAME
isolate_namespace DiscourseSolved
end
PLUGIN_NAME = "discourse-solved"
AUTO_CLOSE_TOPIC_TIMER_CUSTOM_FIELD = "solved_auto_close_topic_timer_id"
ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD = "accepted_answer_post_id"
ENABLE_ACCEPTED_ANSWERS_CUSTOM_FIELD = "enable_accepted_answers"
IS_ACCEPTED_ANSWER_CUSTOM_FIELD = "is_accepted_answer"
class Engine < ::Rails::Engine
engine_name PLUGIN_NAME
isolate_namespace DiscourseSolved
end
end
SeedFu.fixture_paths << Rails.root.join("plugins", "discourse-solved", "db", "fixtures").to_s
require_relative "app/controllers/answer_controller"
require_relative "app/lib/first_accepted_post_solution_validator"
require_relative "app/lib/accepted_answer_cache"
require_relative "app/lib/guardian_extensions"
require_relative "app/lib/before_head_close"
require_relative "app/lib/category_extension"
require_relative "app/lib/post_serializer_extension"
require_relative "app/lib/topic_posters_summary_extension"
require_relative "app/lib/topic_view_serializer_extension"
require_relative "app/lib/user_summary_extension"
require_relative "app/lib/web_hook_extension"
require_relative "app/serializers/concerns/topic_answer_mixin"
module ::DiscourseSolved
def self.accept_answer!(post, acting_user, topic: nil)
topic ||= post.topic
@ -214,45 +179,68 @@ SQL
DiscourseEvent.trigger(:unaccepted_solution, post)
end
end
def self.skip_db?
defined?(GlobalSetting.skip_db?) && GlobalSetting.skip_db?
end
end
require_dependency "application_controller"
class DiscourseSolved::AnswerController < ::ApplicationController
def accept
limit_accepts
post = Post.find(params[:id].to_i)
topic = post.topic
topic ||= Topic.with_deleted.find(post.topic_id) if guardian.is_staff?
guardian.ensure_can_accept_answer!(topic, post)
DiscourseSolved.accept_answer!(post, current_user, topic: topic)
render json: success_json
reloadable_patch do |plugin|
::Guardian.prepend(DiscourseSolved::GuardianExtensions)
::WebHook.prepend(DiscourseSolved::WebHookExtension)
::TopicViewSerializer.prepend(DiscourseSolved::TopicViewSerializerExtension)
::Category.prepend(DiscourseSolved::CategoryExtension)
::PostSerializer.prepend(DiscourseSolved::PostSerializerExtension)
::UserSummary.prepend(DiscourseSolved::UserSummaryExtension) if defined?(::UserAction::SOLVED)
if respond_to?(:register_topic_list_preload_user_ids)
::Topic.attr_accessor(:accepted_answer_user_id)
::TopicPostersSummary.alias_method(:old_user_ids, :user_ids)
::TopicPostersSummary.prepend(DiscourseSolved::TopicPostersSummaryExtension)
end
[
::TopicListItemSerializer,
::SearchTopicListItemSerializer,
::SuggestedTopicSerializer,
::UserSummarySerializer::TopicSerializer,
::ListableTopicSerializer,
].each { |klass| klass.include(TopicAnswerMixin) }
end
def unaccept
limit_accepts
# we got to do a one time upgrade
if !::DiscourseSolved.skip_db? && defined?(UserAction::SOLVED)
unless Discourse.redis.get("solved_already_upgraded")
unless UserAction.where(action_type: UserAction::SOLVED).exists?
Rails.logger.info("Upgrading storage for solved")
sql = <<~SQL
INSERT INTO user_actions(action_type,
user_id,
target_topic_id,
target_post_id,
acting_user_id,
created_at,
updated_at)
SELECT :solved,
p.user_id,
p.topic_id,
p.id,
t.user_id,
pc.created_at,
pc.updated_at
FROM
post_custom_fields pc
JOIN
posts p ON p.id = pc.post_id
JOIN
topics t ON t.id = p.topic_id
WHERE
pc.name = 'is_accepted_answer' AND
pc.value = 'true' AND
p.user_id IS NOT NULL
SQL
post = Post.find(params[:id].to_i)
topic = post.topic
topic ||= Topic.with_deleted.find(post.topic_id) if guardian.is_staff?
guardian.ensure_can_accept_answer!(topic, post)
DiscourseSolved.unaccept_answer!(post, topic: topic)
render json: success_json
end
def limit_accepts
return if current_user.staff?
RateLimiter.new(nil, "accept-hr-#{current_user.id}", 20, 1.hour).performed!
RateLimiter.new(nil, "accept-min-#{current_user.id}", 4, 30.seconds).performed!
DB.exec(sql, solved: UserAction::SOLVED)
end
Discourse.redis.set("solved_already_upgraded", "true")
end
end
@ -276,86 +264,12 @@ SQL
topic_view_post_custom_fields_allowlister { [::DiscourseSolved::IS_ACCEPTED_ANSWER_CUSTOM_FIELD] }
def get_schema_text(post)
post.excerpt(nil, keep_onebox_body: true).presence ||
post.excerpt(nil, keep_onebox_body: true, keep_quotes: true)
end
def before_head_close_meta(controller)
return "" if !controller.instance_of? TopicsController
topic_view = controller.instance_variable_get(:@topic_view)
topic = topic_view&.topic
return "" if !topic
# note, we have canonicals so we only do this for page 1 at the moment
# it can get confusing to have this on every page and it should make page 1
# a bit more prominent + cut down on pointless work
return "" if SiteSetting.solved_add_schema_markup == "never"
allowed =
controller.guardian.allow_accepted_answers?(topic.category_id, topic.tags.pluck(:name))
return "" if !allowed
first_post = topic_view.posts&.first
return "" if first_post&.post_number != 1
question_json = {
"@type" => "Question",
"name" => topic.title,
"text" => get_schema_text(first_post),
"upvoteCount" => first_post.like_count,
"answerCount" => 0,
"datePublished" => topic.created_at,
"author" => {
"@type" => "Person",
"name" => topic.user&.username,
"url" => topic.user&.full_url,
},
}
if accepted_answer =
Post.find_by(
id: topic.custom_fields[::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD],
)
question_json["answerCount"] = 1
question_json[:acceptedAnswer] = {
"@type" => "Answer",
"text" => get_schema_text(accepted_answer),
"upvoteCount" => accepted_answer.like_count,
"datePublished" => accepted_answer.created_at,
"url" => accepted_answer.full_url,
"author" => {
"@type" => "Person",
"name" => accepted_answer.user&.username,
"url" => accepted_answer.user&.full_url,
},
}
else
return "" if SiteSetting.solved_add_schema_markup == "answered only"
end
[
'<script type="application/ld+json">',
MultiJson
.dump(
"@context" => "http://schema.org",
"@type" => "QAPage",
"name" => topic&.title,
"mainEntity" => question_json,
)
.gsub("</", "<\\/")
.html_safe,
"</script>",
].join("")
end
register_html_builder("server:before-head-close-crawler") do |controller|
before_head_close_meta(controller)
DiscourseSolved::BeforeHeadClose.new(controller).html
end
register_html_builder("server:before-head-close") do |controller|
before_head_close_meta(controller)
DiscourseSolved::BeforeHeadClose.new(controller).html
end
if Report.respond_to?(:add_report)
@ -414,129 +328,22 @@ SQL
end
end
if defined?(UserAction::SOLVED)
require_dependency "user_summary"
class ::UserSummary
def solved_count
UserAction.where(user: @user).where(action_type: UserAction::SOLVED).count
end
end
require_dependency "user_summary_serializer"
class ::UserSummarySerializer
attributes :solved_count
def solved_count
object.solved_count
end
end
if defined?(::UserAction::SOLVED)
add_to_serializer(:user_summary, :solved_count) { object.solved_count }
end
class ::WebHook
def self.enqueue_solved_hooks(event, post, payload = nil)
if active_web_hooks(event).exists? && post.present?
payload ||= WebHook.generate_payload(:post, post)
WebHook.enqueue_hooks(
:solved,
event,
id: post.id,
category_id: post.topic&.category_id,
tag_ids: post.topic&.tags&.pluck(:id),
payload: payload,
)
end
end
add_to_serializer(:post, :can_accept_answer) do
scope.can_accept_answer?(topic, object) && object.post_number > 1 && !accepted_answer
end
require_dependency "topic_view_serializer"
class ::TopicViewSerializer
attributes :accepted_answer
def include_accepted_answer?
accepted_answer_post_id
end
def accepted_answer
if info = accepted_answer_post_info
{ post_number: info[0], username: info[1], excerpt: info[2], name: info[3] }
end
end
def accepted_answer_post_info
post_info =
if post = object.posts.find { |p| p.post_number == accepted_answer_post_id }
[post.post_number, post.user.username, post.cooked, post.user.name]
else
Post
.where(id: accepted_answer_post_id, topic_id: object.topic.id)
.joins(:user)
.pluck("post_number", "username", "cooked", "name")
.first
end
if post_info
post_info[2] = if SiteSetting.solved_quote_length > 0
PrettyText.excerpt(post_info[2], SiteSetting.solved_quote_length, keep_emoji_images: true)
else
nil
end
post_info[3] = nil if !SiteSetting.enable_names || !SiteSetting.display_name_on_posts
post_info
end
end
def accepted_answer_post_id
id = object.topic.custom_fields[::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD]
# a bit messy but race conditions can give us an array here, avoid
begin
id && id.to_i
rescue StandardError
nil
end
end
add_to_serializer(:post, :can_unaccept_answer) do
scope.can_accept_answer?(topic, object) && accepted_answer
end
class ::Category
after_save :reset_accepted_cache
protected
def reset_accepted_cache
::DiscourseSolved::AcceptedAnswerCache.reset_accepted_answer_cache
end
add_to_serializer(:post, :accepted_answer) do
post_custom_fields[::DiscourseSolved::IS_ACCEPTED_ANSWER_CUSTOM_FIELD] == "true"
end
require_dependency "post_serializer"
class ::PostSerializer
attributes :can_accept_answer, :can_unaccept_answer, :accepted_answer, :topic_accepted_answer
def can_accept_answer
scope.can_accept_answer?(topic, object) && object.post_number > 1 && !accepted_answer
end
def can_unaccept_answer
scope.can_accept_answer?(topic, object) && accepted_answer
end
def accepted_answer
post_custom_fields[::DiscourseSolved::IS_ACCEPTED_ANSWER_CUSTOM_FIELD] == "true"
end
def topic_accepted_answer
topic&.custom_fields&.[](::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD).present?
end
def topic
topic_view&.topic || object.topic
end
add_to_serializer(:post, :topic_accepted_answer) do
topic&.custom_fields&.[](::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD).present?
end
require_dependency "search"
#TODO Remove when plugin is 1.0
if Search.respond_to? :advanced_filter
Search.advanced_filter(/status:solved/) do |posts|
@ -581,8 +388,6 @@ SQL
end
if Discourse.has_needed_version?(Discourse::VERSION::STRING, "1.8.0.beta6")
require_dependency "topic_query"
TopicQuery.add_custom_filter(:solved) do |results, topic_query|
if topic_query.options[:solved] == "yes"
results =
@ -609,31 +414,6 @@ SQL
end
end
require_dependency "topic_list_item_serializer"
require_dependency "search_topic_list_item_serializer"
require_dependency "suggested_topic_serializer"
require_dependency "user_summary_serializer"
class ::TopicListItemSerializer
include TopicAnswerMixin
end
class ::SearchTopicListItemSerializer
include TopicAnswerMixin
end
class ::SuggestedTopicSerializer
include TopicAnswerMixin
end
class ::UserSummarySerializer::TopicSerializer
include TopicAnswerMixin
end
class ::ListableTopicSerializer
include TopicAnswerMixin
end
if TopicList.respond_to? :preloaded_custom_fields
TopicList.preloaded_custom_fields << ::DiscourseSolved::ACCEPTED_ANSWER_POST_ID_CUSTOM_FIELD
end
@ -756,10 +536,6 @@ SQL
end
if respond_to?(:register_topic_list_preload_user_ids)
class ::Topic
attr_accessor :accepted_answer_user_id
end
register_topic_list_preload_user_ids do |topics, user_ids, topic_list|
answer_post_ids =
TopicCustomField
@ -770,39 +546,6 @@ SQL
topics.each { |topic| topic.accepted_answer_user_id = answer_user_ids[topic.id] }
user_ids.concat(answer_user_ids.values)
end
module AddSolvedToTopicPostersSummary
def descriptions_by_id
if !defined?(@descriptions_by_id)
super(ids: old_user_ids)
if id = topic.accepted_answer_user_id
@descriptions_by_id[id] ||= []
@descriptions_by_id[id] << I18n.t(:accepted_answer)
end
end
super
end
def last_poster_is_topic_creator?
super || topic.accepted_answer_user_id == topic.last_post_user_id
end
def user_ids
if id = topic.accepted_answer_user_id
super.insert(1, id)
else
super
end
end
end
TopicPostersSummary.class_eval do
alias old_user_ids user_ids
prepend AddSolvedToTopicPostersSummary
end
end
if defined?(DiscourseAutomation)
@ -830,29 +573,6 @@ SQL
end
end
TRUST_LEVELS = [
{
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",
},
]
add_triggerable_to_scriptable(:first_accepted_solution, :send_pms)
DiscourseAutomation::Triggerable.add(:first_accepted_solution) do
@ -861,7 +581,33 @@ SQL
field :maximum_trust_level,
component: :choices,
extra: {
content: TRUST_LEVELS,
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

View File

@ -6,8 +6,8 @@ require "composer_messages_finder"
describe ComposerMessagesFinder do
describe ".check_topic_is_solved" do
fab!(:user) { Fabricate(:user) }
fab!(:topic) { Fabricate(:topic) }
fab!(:user)
fab!(:topic)
fab!(:post) { Fabricate(:post, topic: topic, user: Fabricate(:user)) }
before { SiteSetting.disable_solved_education_message = false }

View File

@ -41,7 +41,7 @@ describe PostRevisor do
fab!(:tag1) { Fabricate(:tag) }
fab!(:tag2) { Fabricate(:tag) }
fab!(:topic) { Fabricate(:topic) }
fab!(:topic)
let(:post) { Fabricate(:post, topic: topic) }
it "sets the refresh option after adding an allowed tag" do

View File

@ -340,7 +340,7 @@ RSpec.describe "Managing Posts solved status" do
end
context "with group moderators" do
fab!(:group_user) { Fabricate(:group_user) }
fab!(:group_user)
let(:user_gm) { group_user.user }
let(:group) { group_user.group }