Skip to content

Commit 6e12858

Browse files
authored
FIX: Accepting another answer does not commit (#360)
When an answer already exists, clicking "✅ Solution" on another post works, but does not commit. This commit fixes that and also adds a test, and a transaction around accepting a solution (deleting the topic timer, previous user action, etc).
1 parent 24d819a commit 6e12858

File tree

3 files changed

+92
-67
lines changed

3 files changed

+92
-67
lines changed

Diff for: app/models/discourse_solved/solved_topic.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class SolvedTopic < ActiveRecord::Base
77
belongs_to :topic, class_name: "Topic"
88
belongs_to :answer_post, class_name: "Post", foreign_key: "answer_post_id"
99
belongs_to :accepter, class_name: "User", foreign_key: "accepter_user_id"
10-
belongs_to :topic_timer
10+
belongs_to :topic_timer, dependent: :destroy
1111

1212
validates :topic_id, presence: true
1313
validates :answer_post_id, presence: true

Diff for: plugin.rb

+71-66
Original file line numberDiff line numberDiff line change
@@ -33,74 +33,80 @@ def self.accept_answer!(post, acting_user, topic: nil)
3333
DistributedMutex.synchronize("discourse_solved_toggle_answer_#{topic.id}") do
3434
solved = topic.solved
3535

36-
if previous_accepted_post_id = solved&.answer_post_id
37-
UserAction.where(
38-
action_type: UserAction::SOLVED,
39-
target_post_id: previous_accepted_post_id,
40-
).destroy_all
41-
else
42-
UserAction.log_action!(
43-
action_type: UserAction::SOLVED,
44-
user_id: post.user_id,
45-
acting_user_id: acting_user.id,
46-
target_post_id: post.id,
47-
target_topic_id: post.topic_id,
48-
)
49-
end
50-
51-
solved ||=
52-
DiscourseSolved::SolvedTopic.new(topic:, answer_post: post, accepter: acting_user)
53-
54-
notification_data = {
55-
message: "solved.accepted_notification",
56-
display_username: acting_user.username,
57-
topic_title: topic.title,
58-
title: "solved.notification.title",
59-
}.to_json
60-
61-
unless acting_user.id == post.user_id
62-
Notification.create!(
63-
notification_type: Notification.types[:custom],
64-
user_id: post.user_id,
65-
topic_id: post.topic_id,
66-
post_number: post.post_number,
67-
data: notification_data,
68-
)
69-
end
70-
71-
if SiteSetting.notify_on_staff_accept_solved && acting_user.id != topic.user_id
72-
Notification.create!(
73-
notification_type: Notification.types[:custom],
74-
user_id: topic.user_id,
75-
topic_id: post.topic_id,
76-
post_number: post.post_number,
77-
data: notification_data,
78-
)
79-
end
80-
81-
auto_close_hours = 0
82-
if topic&.category.present?
83-
auto_close_hours = topic.category.custom_fields["solved_topics_auto_close_hours"].to_i
84-
auto_close_hours = 175_200 if auto_close_hours > 175_200 # 20 years
85-
end
86-
87-
auto_close_hours = SiteSetting.solved_topics_auto_close_hours if auto_close_hours == 0
88-
89-
if (auto_close_hours > 0) && !topic.closed
90-
topic_timer =
91-
topic.set_or_create_timer(
92-
TopicTimer.types[:silent_close],
93-
nil,
94-
based_on_last_post: true,
95-
duration_minutes: auto_close_hours * 60,
36+
ActiveRecord::Base.transaction do
37+
if previous_accepted_post_id = solved&.answer_post_id
38+
UserAction.where(
39+
action_type: UserAction::SOLVED,
40+
target_post_id: previous_accepted_post_id,
41+
).destroy_all
42+
solved.destroy!
43+
else
44+
UserAction.log_action!(
45+
action_type: UserAction::SOLVED,
46+
user_id: post.user_id,
47+
acting_user_id: acting_user.id,
48+
target_post_id: post.id,
49+
target_topic_id: post.topic_id,
9650
)
97-
solved.topic_timer = topic_timer
98-
99-
MessageBus.publish("/topic/#{topic.id}", reload_topic: true)
51+
end
52+
53+
solved =
54+
DiscourseSolved::SolvedTopic.new(topic:, answer_post: post, accepter: acting_user)
55+
56+
unless acting_user.id == post.user_id
57+
Notification.create!(
58+
notification_type: Notification.types[:custom],
59+
user_id: post.user_id,
60+
topic_id: post.topic_id,
61+
post_number: post.post_number,
62+
data: {
63+
message: "solved.accepted_notification",
64+
display_username: acting_user.username,
65+
topic_title: topic.title,
66+
title: "solved.notification.title",
67+
}.to_json,
68+
)
69+
end
70+
71+
if SiteSetting.notify_on_staff_accept_solved && acting_user.id != topic.user_id
72+
Notification.create!(
73+
notification_type: Notification.types[:custom],
74+
user_id: topic.user_id,
75+
topic_id: post.topic_id,
76+
post_number: post.post_number,
77+
data: {
78+
message: "solved.accepted_notification",
79+
display_username: acting_user.username,
80+
topic_title: topic.title,
81+
title: "solved.notification.title",
82+
}.to_json,
83+
)
84+
end
85+
86+
auto_close_hours = 0
87+
if topic&.category.present?
88+
auto_close_hours = topic.category.custom_fields["solved_topics_auto_close_hours"].to_i
89+
auto_close_hours = 175_200 if auto_close_hours > 175_200 # 20 years
90+
end
91+
92+
auto_close_hours = SiteSetting.solved_topics_auto_close_hours if auto_close_hours == 0
93+
94+
if (auto_close_hours > 0) && !topic.closed
95+
topic_timer =
96+
topic.set_or_create_timer(
97+
TopicTimer.types[:silent_close],
98+
nil,
99+
based_on_last_post: true,
100+
duration_minutes: auto_close_hours * 60,
101+
)
102+
solved.topic_timer = topic_timer
103+
104+
MessageBus.publish("/topic/#{topic.id}", reload_topic: true)
105+
end
106+
107+
solved.save!
100108
end
101109

102-
solved.save!
103-
104110
if WebHook.active_web_hooks(:accepted_solution).exists?
105111
payload = WebHook.generate_payload(:post, post)
106112
WebHook.enqueue_solved_hooks(:accepted_solution, post, payload)
@@ -127,7 +133,6 @@ def self.unaccept_answer!(post, topic: nil)
127133
topic_id: post.topic_id,
128134
post_number: post.post_number,
129135
)&.destroy!
130-
solved.topic_timer.destroy! if solved.topic_timer
131136
solved.destroy!
132137
end
133138

Diff for: spec/integration/solved_spec.rb

+20
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,26 @@
540540
end
541541
end
542542

543+
describe "#accept_answer!" do
544+
it "marks the post as the accepted answer correctly" do
545+
user = Fabricate(:user, trust_level: 1)
546+
topic = Fabricate(:topic, user:)
547+
reply1 = Fabricate(:post, topic:, user:, post_number: 2)
548+
reply2 = Fabricate(:post, topic:, user:, post_number: 3)
549+
550+
DiscourseSolved.accept_answer!(reply1, user)
551+
topic.reload
552+
553+
expect(topic.solved.answer_post_id).to eq(reply1.id)
554+
expect(topic.solved.topic_timer).to eq(topic.public_topic_timer)
555+
556+
DiscourseSolved.accept_answer!(reply2, user)
557+
topic.reload
558+
559+
expect(topic.solved.answer_post_id).to eq(reply2.id)
560+
end
561+
end
562+
543563
describe "user actions stream modifier" do
544564
it "correctly list solutions" do
545565
t1 = Fabricate(:topic)

0 commit comments

Comments
 (0)