discourse/lib/tasks/topics.rake
Jarek Radosz a54e3208cb
DEV: Hand-pick Rails/WhereNot autofixes (#35117)
We can't enable `Rails/WhereNot` lint/autofix, because it would break
code that uses mini_sql instead of AR (which rubocop, and tbh also we,
can't easily differentiate)

Those are safe because they either:
* are executed in AR model scope definitions
* are clearly chained starting from a AR model
* are less-clearly chained, but still can be traced to a AR model/scope

---------

Co-authored-by: Loïc Guitaut <loic@discourse.org>
2025-10-03 13:29:22 +02:00

173 lines
4.5 KiB
Ruby

# frozen_string_literal: true
require "rake_helpers"
def close_old_topics(category)
topics = Topic.where(closed: false, category_id: category.id)
if category.auto_close_based_on_last_post
topics = topics.where("last_posted_at < ?", category.auto_close_hours.hours.ago)
else
topics = topics.where("created_at < ?", category.auto_close_hours.hours.ago)
end
topics_closed = 0
total = topics.count
if total == 0
print " all old topics are closed"
return
end
topics.find_each do |topic|
topic.update_status("closed", true, Discourse.system_user, { silent: true })
RakeHelpers.print_status_with_label(" closing old topics: ", topics_closed += 1, total)
end
end
def apply_auto_close(category)
topics =
Topic.where(closed: false, category_id: category.id).where(<<-SQL, TopicTimer.types[:close])
NOT EXISTS (
SELECT 1
FROM topic_timers
WHERE topic_timers.timerable_id = topics.id
AND topic_timers.status_type = ?
AND topic_timers.deleted_at IS NULL
)
SQL
topics_closed = 0
total = topics.count
if total == 0
print " all topics have auto-close applied"
return
end
topics.find_each do |topic|
topic.inherit_auto_close_from_category
RakeHelpers.print_status_with_label(
" applying auto-close to topics: ",
topics_closed += 1,
total,
)
end
end
task "topics:apply_autoclose" => :environment do
categories = Category.where("auto_close_hours > 0")
categories.find_each do |category|
puts "", "Applying auto-close to category '#{category.name}' ..."
close_old_topics(category)
puts ""
apply_auto_close(category)
puts ""
end
puts "", "Done"
end
task "topics:watch_all_replied_topics" => :environment do
puts "Setting all topics to Watching on which a user has posted at least once..."
topics = Topic.where.not(archetype: Archetype.private_message)
total = topics.count
count = 0
topics.find_each do |t|
t
.topic_users
.where(posted: true)
.find_each do |tp|
tp.update!(
notification_level: TopicUser.notification_levels[:watching],
notifications_reason_id: TopicUser.notification_reasons[:created_post],
)
end
RakeHelpers.print_status(count += 1, total)
end
puts "", "Done"
end
task "topics:update_fancy_titles" => :environment do
if !SiteSetting.title_fancy_entities?
puts "fancy topic titles are disabled"
next
end
DB.exec("UPDATE topics SET fancy_title = NULL")
total = Topic.count
count = 0
Topic.find_each do |topic|
topic.fancy_title
RakeHelpers.print_status(count += 1, total)
end
puts "", "Done"
end
desc "Remap topic titles - replace a specific string with another"
task "topics:remap_titles", %i[old_string new_string] => [:environment] do |_, args|
require "highline/import"
if args[:old_string].blank? || args[:new_string].blank?
puts "ERROR: Missing required arguments"
puts "Usage: rake topics:remap_titles['old string','new string']"
exit 1
end
old_string = args[:old_string]
new_string = args[:new_string]
# add a confirmation to ensure user wants to replace old with new here
confirm_replace =
ask(
"Are you sure you want to replace all occurrences of '#{old_string}' with '#{new_string}' in topic titles? (Y/n)",
)
if confirm_replace.blank? || confirm_replace.downcase == "y"
puts "Remapping topic titles..."
puts "Replacing '#{old_string}' with '#{new_string}'"
# Filter topics that contain the old_string (using LIKE for case-sensitive matching)
matching_topics =
Topic
.where("title LIKE ?", "%#{old_string}%")
.where("deleted_at IS NULL")
.where(archetype: "regular")
.where.not(title: nil)
total_matches = matching_topics.count
puts "Found #{total_matches} topics containing '#{old_string}'"
if total_matches == 0
puts "No matching topics found. Exiting."
exit 0
end
count = 0
matching_topics.find_each do |topic|
old_title = topic.title
new_title = topic.title.gsub(old_string, new_string)
# Only update if there's an actual change
if old_title != new_title
puts "#{topic.id}: #{old_title}#{new_title}"
# Update the title directly with SQL
Topic.where(id: topic.id).update_all(title: new_title, fancy_title: new_title)
count += 1
end
end
puts "Remapped #{count} topic titles."
else
puts "Aborting..."
exit 0
end
end