mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-23 06:30:50 +08:00
Allows users to opt out of solved topic notifications whether they are the topic creator, the user who posted the answer or just someone watching or tracking the topic. This change will be a follow up to #38724 and this PR will need updated to opt out of watched/tracked notifications once it is merged. Internal ref - /t/173087
194 lines
5.7 KiB
Ruby
Vendored
194 lines
5.7 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
class DiscourseSolved::AcceptAnswer
|
|
include Service::Base
|
|
|
|
params do
|
|
attribute :post_id, :integer
|
|
|
|
validates :post_id, presence: true
|
|
end
|
|
|
|
model :post
|
|
model :topic
|
|
policy :can_accept_answer
|
|
|
|
lock(:topic) do
|
|
transaction do
|
|
only_if(:previous_answer_exists) { step :revoke_previous_accepted_answer }
|
|
step :credit_post_author
|
|
model :solved, :mark_as_solved
|
|
only_if(:should_notify_post_author) { step :notify_post_author }
|
|
only_if(:should_notify_topic_owner) { step :notify_topic_owner }
|
|
model :topic_user_ids, optional: true
|
|
only_if(:has_topic_users?) do
|
|
model :screener
|
|
step :notify_tracking_and_watching_users
|
|
end
|
|
end
|
|
end
|
|
|
|
only_if(:accepted_solution_webhooks_active) { step :enqueue_web_hooks }
|
|
only_if(:topic_will_auto_close) { step :publish_topic_reload }
|
|
step :publish_solution
|
|
|
|
private
|
|
|
|
def fetch_post(params:)
|
|
Post.find_by(id: params.post_id)
|
|
end
|
|
|
|
def fetch_topic(post:, guardian:)
|
|
return Topic.with_deleted.find_by(id: post.topic_id) if guardian.is_staff?
|
|
post.topic
|
|
end
|
|
|
|
def can_accept_answer(guardian:, topic:, post:)
|
|
guardian.can_accept_answer?(topic, post)
|
|
end
|
|
|
|
def previous_answer_exists(topic:)
|
|
topic.solved&.answer_post_id.present?
|
|
end
|
|
|
|
def revoke_previous_accepted_answer(topic:)
|
|
UserAction.where(
|
|
action_type: UserAction::SOLVED,
|
|
target_post: topic.solved.answer_post,
|
|
).destroy_all
|
|
topic.solved.destroy!
|
|
end
|
|
|
|
def credit_post_author(post:, guardian:)
|
|
UserAction.log_action!(
|
|
action_type: UserAction::SOLVED,
|
|
user_id: post.user_id,
|
|
acting_user_id: guardian.user.id,
|
|
target_post_id: post.id,
|
|
target_topic_id: post.topic_id,
|
|
)
|
|
end
|
|
|
|
def mark_as_solved(post:, topic:, guardian:)
|
|
DiscourseSolved::SolvedTopic.create(topic:, answer_post: post, accepter: guardian.user)
|
|
end
|
|
|
|
def should_notify_post_author(post:, guardian:)
|
|
return if guardian.user.id == post.user_id || !User.exists?(post.user_id)
|
|
return if !UserOption.exists?(user_id: post.user_id, notify_on_solved: true)
|
|
screener = UserCommScreener.new(acting_user_id: guardian.user.id, target_user_ids: post.user_id)
|
|
!screener.ignoring_or_muting_actor?(post.user_id)
|
|
end
|
|
|
|
def notify_post_author(post:, topic:, guardian:)
|
|
Notification.create!(
|
|
notification_type: Notification.types[:custom],
|
|
user_id: post.user_id,
|
|
topic_id: post.topic_id,
|
|
post_number: post.post_number,
|
|
data: {
|
|
message: "solved.accepted_notification",
|
|
display_username: guardian.user.username,
|
|
topic_title: topic.title,
|
|
title: "solved.notification.title",
|
|
}.to_json,
|
|
)
|
|
end
|
|
|
|
def should_notify_topic_owner(topic:, guardian:)
|
|
return if !topic.category&.notify_on_staff_accept_solved?
|
|
return if guardian.user.id == topic.user_id || !User.exists?(topic.user_id)
|
|
return if !UserOption.exists?(user_id: topic.user_id, notify_on_solved: true)
|
|
screener =
|
|
UserCommScreener.new(acting_user_id: guardian.user.id, target_user_ids: topic.user_id)
|
|
!screener.ignoring_or_muting_actor?(topic.user_id)
|
|
end
|
|
|
|
def notify_topic_owner(post:, topic:, guardian:)
|
|
Notification.create!(
|
|
notification_type: Notification.types[:custom],
|
|
user_id: topic.user_id,
|
|
topic_id: post.topic_id,
|
|
post_number: post.post_number,
|
|
data: {
|
|
message: "solved.accepted_notification",
|
|
display_username: guardian.user.username,
|
|
topic_title: topic.title,
|
|
title: "solved.notification.title",
|
|
}.to_json,
|
|
)
|
|
end
|
|
|
|
def has_topic_users?(topic_user_ids:)
|
|
topic_user_ids.present?
|
|
end
|
|
|
|
def fetch_topic_user_ids(post:, topic:, guardian:)
|
|
already_notified_ids = [guardian.user.id, post.user_id, topic.user_id]
|
|
|
|
TopicUser
|
|
.where(topic:)
|
|
.where("notification_level >= ?", TopicUser.notification_levels[:tracking])
|
|
.where.not(user_id: already_notified_ids)
|
|
.joins(user: :user_option)
|
|
.where(user_options: { notify_on_solved: true })
|
|
.pluck(:user_id)
|
|
end
|
|
|
|
def fetch_screener(guardian:, topic_user_ids:)
|
|
UserCommScreener.new(acting_user_id: guardian.user.id, target_user_ids: topic_user_ids)
|
|
end
|
|
|
|
def notify_tracking_and_watching_users(post:, topic:, guardian:, topic_user_ids:, screener:)
|
|
notification_data = {
|
|
message: "solved.topic_solved_notification",
|
|
display_username: guardian.user.username,
|
|
topic_title: topic.title,
|
|
title: "solved.notification.topic_solved_title",
|
|
}.to_json
|
|
|
|
records =
|
|
topic_user_ids.filter_map do |user_id|
|
|
next if screener.ignoring_or_muting_actor?(user_id)
|
|
|
|
{
|
|
notification_type: Notification.types[:custom],
|
|
user_id: user_id,
|
|
topic_id: topic.id,
|
|
post_number: post.post_number,
|
|
data: notification_data,
|
|
}
|
|
end
|
|
|
|
Notification::Action::BulkCreate.call(records:)
|
|
end
|
|
|
|
def topic_will_auto_close(solved:)
|
|
solved.topic_timer.present?
|
|
end
|
|
|
|
def publish_topic_reload(topic:)
|
|
MessageBus.publish(
|
|
"/topic/#{topic.id}",
|
|
{ reload_topic: true },
|
|
topic.secure_audience_publish_messages,
|
|
)
|
|
end
|
|
|
|
def accepted_solution_webhooks_active
|
|
WebHook.active_web_hooks(:accepted_solution).exists?
|
|
end
|
|
|
|
def enqueue_web_hooks(post:)
|
|
WebHook.enqueue_solved_hooks(:accepted_solution, post, WebHook.generate_payload(:post, post))
|
|
end
|
|
|
|
def publish_solution(post:, topic:)
|
|
DiscourseEvent.trigger(:accepted_solution, post)
|
|
MessageBus.publish(
|
|
"/topic/#{topic.id}",
|
|
{ type: :accepted_solution, accepted_answer: topic.reload.accepted_answer_post_info },
|
|
topic.secure_audience_publish_messages,
|
|
)
|
|
end
|
|
end
|