Skip to content

Commit e8087f2

Browse files
authored
DEV: Update Category Experts & auto tags when topic is edited (#214)
* DEV: Update Category Experts & auto tags when topic is edited * Combine query * lint * fix spec * fix query
1 parent d794597 commit e8087f2

File tree

3 files changed

+253
-0
lines changed

3 files changed

+253
-0
lines changed

lib/category_experts/post_handler.rb

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,75 @@ def correct_topic_custom_fields_after_addition(new_post: false)
122122
add_auto_tag
123123
end
124124

125+
def handle_topic_category_change(old_category_id, new_category_id)
126+
old_category = Category.find_by(id: old_category_id)
127+
new_category = Category.find_by(id: new_category_id)
128+
129+
# Get all posts in the topic that have expert status
130+
expert_posts =
131+
Post
132+
.includes(:user)
133+
.joins(:_custom_fields)
134+
.where(
135+
topic_id: topic.id,
136+
post_custom_fields: {
137+
name: CategoryExperts::POST_APPROVED_GROUP_NAME,
138+
},
139+
)
140+
.where.not(post_custom_fields: { value: nil })
141+
142+
# Re-evaluate each expert post
143+
expert_posts.each do |expert_post|
144+
post_author = expert_post.user
145+
next if !post_author
146+
147+
# Check if the post author is an expert in the new category
148+
author_expert_group_ids = post_author.expert_group_ids_for_category(new_category)
149+
150+
if author_expert_group_ids.empty?
151+
# Author is no longer an expert - remove expert status
152+
old_group_name = expert_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]
153+
expert_post.custom_fields.delete(CategoryExperts::POST_APPROVED_GROUP_NAME)
154+
expert_post.custom_fields.delete(CategoryExperts::POST_PENDING_EXPERT_APPROVAL)
155+
expert_post.save!
156+
157+
# Update topic custom fields to reflect the removal
158+
CategoryExperts::PostHandler.new(
159+
post: expert_post,
160+
topic: topic,
161+
).correct_topic_custom_fields_after_removal(group_name: old_group_name)
162+
else
163+
# Author is still an expert in the new category - update the group name
164+
new_expert_group = Group.find_by(id: author_expert_group_ids.first)
165+
old_group_name = expert_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]
166+
167+
if new_expert_group && new_expert_group.name != old_group_name
168+
# Update post with new group name first
169+
expert_post.custom_fields[
170+
CategoryExperts::POST_APPROVED_GROUP_NAME
171+
] = new_expert_group.name
172+
expert_post.save!
173+
174+
# Remove old group from topic custom fields (now that post is updated)
175+
CategoryExperts::PostHandler.new(
176+
post: expert_post,
177+
topic: topic,
178+
).correct_topic_custom_fields_after_removal(group_name: old_group_name)
179+
180+
# Add new group to topic custom fields
181+
CategoryExperts::PostHandler.new(
182+
post: expert_post,
183+
topic: topic,
184+
user: post_author,
185+
).correct_topic_custom_fields_after_addition
186+
end
187+
end
188+
end
189+
190+
# Handle auto-tag changes
191+
handle_auto_tag_change(old_category, new_category)
192+
end
193+
125194
private
126195

127196
def ensure_poster_is_category_expert
@@ -171,5 +240,29 @@ def auto_tag_for_category
171240
@auto_tag_for_category =
172241
@topic.category.custom_fields[CategoryExperts::CATEGORY_EXPERT_AUTO_TAG]
173242
end
243+
244+
def handle_auto_tag_change(old_category, new_category)
245+
return if !SiteSetting.tagging_enabled
246+
247+
old_auto_tag = old_category&.custom_fields&.[](CategoryExperts::CATEGORY_EXPERT_AUTO_TAG)
248+
new_auto_tag = new_category&.custom_fields&.[](CategoryExperts::CATEGORY_EXPERT_AUTO_TAG)
249+
250+
existing_tag_names = topic.tags.map(&:name)
251+
252+
# Determine what tags to add/remove
253+
tags_to_remove = old_auto_tag.present? ? [old_auto_tag] : []
254+
tags_to_add = new_auto_tag.present? ? [new_auto_tag] : []
255+
256+
# Only revise if there's actually a change needed
257+
if (tags_to_remove.any? && existing_tag_names.include?(old_auto_tag)) ||
258+
(tags_to_add.any? && !existing_tag_names.include?(new_auto_tag))
259+
new_tag_names = (existing_tag_names - tags_to_remove + tags_to_add).uniq
260+
261+
PostRevisor.new(topic.ordered_posts.first).revise!(
262+
Discourse.system_user,
263+
{ tags: new_tag_names },
264+
)
265+
end
266+
end
174267
end
175268
end

plugin.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,20 @@ class Engine < ::Rails::Engine
354354
)
355355
end
356356

357+
on(:post_edited) do |post, topic_changed, revisor|
358+
# Only handle when it's the first post (topic) and the category changed
359+
next if !post.is_first_post?
360+
next if !topic_changed
361+
next if !revisor.topic_diff.has_key?("category_id")
362+
363+
old_category_id, new_category_id = revisor.topic_diff["category_id"]
364+
365+
CategoryExperts::PostHandler.new(topic: post.topic).handle_topic_category_change(
366+
old_category_id,
367+
new_category_id,
368+
)
369+
end
370+
357371
add_to_class(:group, :category_expert_category_ids) do
358372
category_ids = []
359373

spec/plugin_spec.rb

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,5 +217,151 @@
217217
end
218218
end
219219
end
220+
221+
describe "on 'post_edited' with category change" do
222+
fab!(:category_b, :category)
223+
fab!(:group_b, :group)
224+
225+
before do
226+
category_b.custom_fields[CategoryExperts::CATEGORY_EXPERT_GROUP_IDS] = "#{group_b.id}"
227+
category_b.save!
228+
end
229+
230+
context "when topic is moved from category with experts to category without experts" do
231+
it "clears all expert custom fields from posts and topic" do
232+
expect(
233+
original_topic.custom_fields[CategoryExperts::TOPIC_EXPERT_POST_GROUP_NAMES],
234+
).to eq(group.name)
235+
expect(original_topic.custom_fields[CategoryExperts::TOPIC_FIRST_EXPERT_POST_ID]).to eq(
236+
second_post.post_number,
237+
)
238+
expect(second_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]).to eq(
239+
group.name,
240+
)
241+
242+
category_without_experts = Fabricate(:category)
243+
244+
PostRevisor.new(original_topic.first_post).revise!(
245+
admin,
246+
category_id: category_without_experts.id,
247+
)
248+
249+
original_topic.reload
250+
second_post.reload
251+
252+
expect(
253+
original_topic.custom_fields[CategoryExperts::TOPIC_EXPERT_POST_GROUP_NAMES],
254+
).to be_blank
255+
expect(
256+
original_topic.custom_fields[CategoryExperts::TOPIC_FIRST_EXPERT_POST_ID],
257+
).to be_blank
258+
expect(second_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]).to be_blank
259+
end
260+
end
261+
262+
context "when topic is moved from one category with experts to another with different experts" do
263+
it "updates expert custom fields appropriately when user is not expert in new category" do
264+
expect(
265+
original_topic.custom_fields[CategoryExperts::TOPIC_EXPERT_POST_GROUP_NAMES],
266+
).to eq(group.name)
267+
expect(second_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]).to eq(
268+
group.name,
269+
)
270+
271+
PostRevisor.new(original_topic.first_post).revise!(admin, category_id: category_b.id)
272+
273+
original_topic.reload
274+
second_post.reload
275+
276+
expect(
277+
original_topic.custom_fields[CategoryExperts::TOPIC_EXPERT_POST_GROUP_NAMES],
278+
).to be_blank
279+
expect(second_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]).to be_blank
280+
end
281+
282+
it "updates expert group name when user is expert in both categories" do
283+
group_b.add(expert)
284+
285+
expect(
286+
original_topic.custom_fields[CategoryExperts::TOPIC_EXPERT_POST_GROUP_NAMES],
287+
).to eq(group.name)
288+
expect(second_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]).to eq(
289+
group.name,
290+
)
291+
292+
PostRevisor.new(original_topic.first_post).revise!(admin, category_id: category_b.id)
293+
294+
original_topic.reload
295+
second_post.reload
296+
297+
expect(
298+
original_topic.custom_fields[CategoryExperts::TOPIC_EXPERT_POST_GROUP_NAMES],
299+
).to eq(group_b.name)
300+
expect(second_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]).to eq(
301+
group_b.name,
302+
)
303+
end
304+
305+
context "with multiple expert posts from different users" do
306+
fab!(:expert_2) { Fabricate(:user, refresh_auto_groups: true) }
307+
fab!(:third_post) { Fabricate(:post, topic: original_topic, user: expert_2) }
308+
309+
before do
310+
group.add(expert_2)
311+
CategoryExperts::PostHandler.new(post: third_post, user: expert_2).mark_post_as_approved
312+
end
313+
314+
it "only keeps expert status for users who are experts in new category" do
315+
group_b.add(expert)
316+
317+
expect(
318+
original_topic.custom_fields[CategoryExperts::TOPIC_EXPERT_POST_GROUP_NAMES],
319+
).to eq(group.name)
320+
expect(second_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]).to eq(
321+
group.name,
322+
)
323+
expect(third_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]).to eq(
324+
group.name,
325+
)
326+
327+
PostRevisor.new(original_topic.first_post).revise!(admin, category_id: category_b.id)
328+
329+
original_topic.reload
330+
second_post.reload
331+
third_post.reload
332+
333+
expect(
334+
original_topic.custom_fields[CategoryExperts::TOPIC_EXPERT_POST_GROUP_NAMES],
335+
).to eq(group_b.name)
336+
expect(second_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]).to eq(
337+
group_b.name,
338+
)
339+
expect(third_post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]).to be_blank
340+
end
341+
end
342+
end
343+
344+
context "when topic has auto-tag" do
345+
fab!(:auto_tag_b, :tag)
346+
347+
before do
348+
SiteSetting.tagging_enabled = true
349+
category_b.custom_fields[CategoryExperts::CATEGORY_EXPERT_AUTO_TAG] = auto_tag_b.name
350+
category_b.save!
351+
end
352+
353+
it "removes old auto-tag and adds new auto-tag when category changes" do
354+
expect(original_topic.tags.map(&:name)).to include(auto_tag.name)
355+
expect(original_topic.tags.map(&:name)).not_to include(auto_tag_b.name)
356+
357+
PostRevisor.new(original_topic.first_post).revise!(admin, category_id: category_b.id)
358+
359+
original_topic.reload
360+
361+
expect(original_topic.tags.map(&:name)).not_to include(auto_tag.name)
362+
expect(original_topic.tags.map(&:name)).to include(auto_tag_b.name)
363+
end
364+
end
365+
end
220366
end
221367
end

0 commit comments

Comments
 (0)