FEATURE: Create a rake task for destroying categories
Created a rake task for destroying multiple categories along with any subcategories and topics the belong to those categories. Also created a rake task for listing all of your categories. Refactored existing destroy rake tasks to use new logging method, that allows for puts output in the console but prevents it from showing in the specs.
This commit is contained in:
parent
98866ca043
commit
092eeb5ca3
|
@ -1,83 +1,110 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
## Because these methods are meant to be called from a rake task
|
|
||||||
# we are capturing all log output into a log array to return
|
|
||||||
# to the rake task rather than using `puts` statements.
|
|
||||||
class DestroyTask
|
class DestroyTask
|
||||||
def self.destroy_topics(category, parent_category = nil)
|
|
||||||
|
def initialize(io = $stdout)
|
||||||
|
@io = io
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy_topics(category, parent_category = nil, delete_system_topics = false)
|
||||||
c = Category.find_by_slug(category, parent_category)
|
c = Category.find_by_slug(category, parent_category)
|
||||||
log = []
|
|
||||||
descriptive_slug = parent_category ? "#{parent_category}/#{category}" : category
|
descriptive_slug = parent_category ? "#{parent_category}/#{category}" : category
|
||||||
return "A category with the slug: #{descriptive_slug} could not be found" if c.nil?
|
return @io.puts "A category with the slug: #{descriptive_slug} could not be found" if c.nil?
|
||||||
|
if delete_system_topics
|
||||||
|
topics = Topic.where(category_id: c.id, pinned_at: nil)
|
||||||
|
else
|
||||||
topics = Topic.where(category_id: c.id, pinned_at: nil).where.not(user_id: -1)
|
topics = Topic.where(category_id: c.id, pinned_at: nil).where.not(user_id: -1)
|
||||||
log << "There are #{topics.count} topics to delete in #{descriptive_slug} category"
|
end
|
||||||
|
@io.puts "There are #{topics.count} topics to delete in #{descriptive_slug} category"
|
||||||
topics.each do |topic|
|
topics.each do |topic|
|
||||||
log << "Deleting #{topic.slug}..."
|
@io.puts "Deleting #{topic.slug}..."
|
||||||
first_post = topic.ordered_posts.first
|
first_post = topic.ordered_posts.first
|
||||||
if first_post.nil?
|
if first_post.nil?
|
||||||
return log << "Topic.ordered_posts.first was nil"
|
return @io.puts "Topic.ordered_posts.first was nil"
|
||||||
end
|
end
|
||||||
system_user = User.find(-1)
|
system_user = User.find(-1)
|
||||||
log << PostDestroyer.new(system_user, first_post).destroy
|
@io.puts PostDestroyer.new(system_user, first_post).destroy
|
||||||
end
|
end
|
||||||
log
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.destroy_topics_all_categories
|
def destroy_topics_in_category(category_id, delete_system_topics = false)
|
||||||
|
c = Category.find(category_id)
|
||||||
|
return @io.puts "A category with the id: #{category_id} could not be found" if c.nil?
|
||||||
|
if delete_system_topics
|
||||||
|
topics = Topic.where(category_id: c.id, pinned_at: nil)
|
||||||
|
else
|
||||||
|
topics = Topic.where(category_id: c.id, pinned_at: nil).where.not(user_id: -1)
|
||||||
|
end
|
||||||
|
@io.puts "There are #{topics.count} topics to delete in #{c.slug} category"
|
||||||
|
topics.each do |topic|
|
||||||
|
first_post = topic.ordered_posts.first
|
||||||
|
return @io.puts "Topic.ordered_posts.first was nil for topic: #{topic.id}" if first_post.nil?
|
||||||
|
system_user = User.find(-1)
|
||||||
|
PostDestroyer.new(system_user, first_post).destroy
|
||||||
|
end
|
||||||
|
topics = Topic.where(category_id: c.id, pinned_at: nil)
|
||||||
|
@io.puts "There are #{topics.count} topics that could not be deleted in #{c.slug} category"
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy_topics_all_categories
|
||||||
categories = Category.all
|
categories = Category.all
|
||||||
log = []
|
|
||||||
categories.each do |c|
|
categories.each do |c|
|
||||||
log << destroy_topics(c.slug, c.parent_category&.slug)
|
@io.puts destroy_topics(c.slug, c.parent_category&.slug)
|
||||||
end
|
end
|
||||||
log
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.destroy_private_messages
|
def destroy_private_messages
|
||||||
pms = Topic.where(archetype: "private_message")
|
pms = Topic.where(archetype: "private_message")
|
||||||
current_user = User.find(-1) #system
|
current_user = User.find(-1) #system
|
||||||
log = []
|
|
||||||
pms.each do |pm|
|
pms.each do |pm|
|
||||||
log << "Destroying #{pm.slug} pm"
|
@io.puts "Destroying #{pm.slug} pm"
|
||||||
first_post = pm.ordered_posts.first
|
first_post = pm.ordered_posts.first
|
||||||
log << PostDestroyer.new(current_user, first_post).destroy
|
@io.puts PostDestroyer.new(current_user, first_post).destroy
|
||||||
end
|
end
|
||||||
log
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.destroy_groups
|
def destroy_category(category_id, destroy_system_topics = false)
|
||||||
|
c = Category.find_by_id(category_id)
|
||||||
|
return @io.puts "A category with the id: #{category_id} could not be found" if c.nil?
|
||||||
|
subcategories = Category.where(parent_category_id: c.id).pluck(:id)
|
||||||
|
@io.puts "There are #{subcategories.count} subcategories to delete" if subcategories
|
||||||
|
subcategories.each do |subcategory_id|
|
||||||
|
s = Category.find_by_id(subcategory_id)
|
||||||
|
category_topic_destroyer(s, destroy_system_topics)
|
||||||
|
end
|
||||||
|
category_topic_destroyer(c, destroy_system_topics)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy_groups
|
||||||
groups = Group.where(automatic: false)
|
groups = Group.where(automatic: false)
|
||||||
log = []
|
|
||||||
groups.each do |group|
|
groups.each do |group|
|
||||||
log << "destroying group: #{group.id}"
|
@io.puts "destroying group: #{group.id}"
|
||||||
log << group.destroy
|
@io.puts group.destroy
|
||||||
end
|
end
|
||||||
log
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.destroy_users
|
def destroy_users
|
||||||
log = []
|
|
||||||
users = User.where(admin: false, id: 1..Float::INFINITY)
|
users = User.where(admin: false, id: 1..Float::INFINITY)
|
||||||
log << "There are #{users.count} users to delete"
|
@io.puts "There are #{users.count} users to delete"
|
||||||
options = {}
|
options = {}
|
||||||
options[:delete_posts] = true
|
options[:delete_posts] = true
|
||||||
current_user = User.find(-1) #system
|
current_user = User.find(-1) #system
|
||||||
users.each do |user|
|
users.each do |user|
|
||||||
begin
|
begin
|
||||||
if UserDestroyer.new(current_user).destroy(user, options)
|
if UserDestroyer.new(current_user).destroy(user, options)
|
||||||
log << "#{user.username} deleted"
|
@io.puts "#{user.username} deleted"
|
||||||
else
|
else
|
||||||
log << "#{user.username} not deleted"
|
@io.puts "#{user.username} not deleted"
|
||||||
end
|
end
|
||||||
rescue UserDestroyer::PostsExistError
|
rescue UserDestroyer::PostsExistError
|
||||||
raise Discourse::InvalidAccess.new("User #{user.username} has #{user.post_count} posts, so can't be deleted.")
|
raise Discourse::InvalidAccess.new("User #{user.username} has #{user.post_count} posts, so can't be deleted.")
|
||||||
rescue NoMethodError
|
rescue NoMethodError
|
||||||
log << "#{user.username} could not be deleted"
|
@io.puts "#{user.username} could not be deleted"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
log
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.destroy_stats
|
def destroy_stats
|
||||||
ApplicationRequest.destroy_all
|
ApplicationRequest.destroy_all
|
||||||
IncomingLink.destroy_all
|
IncomingLink.destroy_all
|
||||||
UserVisit.destroy_all
|
UserVisit.destroy_all
|
||||||
|
@ -90,4 +117,13 @@ class DestroyTask
|
||||||
PostAction.unscoped.destroy_all
|
PostAction.unscoped.destroy_all
|
||||||
EmailLog.destroy_all
|
EmailLog.destroy_all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def category_topic_destroyer(category, destroy_system_topics = false)
|
||||||
|
destroy_topics_log = destroy_topics_in_category(category.id, destroy_system_topics)
|
||||||
|
@io.puts "Destroying #{category.slug} category"
|
||||||
|
category.destroy
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -36,3 +36,11 @@ end
|
||||||
def print_status(current, max)
|
def print_status(current, max)
|
||||||
print "\r%9d / %d (%5.1f%%)" % [current, max, ((current.to_f / max.to_f) * 100).round(1)]
|
print "\r%9d / %d (%5.1f%%)" % [current, max, ((current.to_f / max.to_f) * 100).round(1)]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc "Output a list of categories"
|
||||||
|
task "categories:list" => :environment do
|
||||||
|
categories = Category.pluck(:id, :slug, :parent_category_id)
|
||||||
|
categories.each do |c|
|
||||||
|
puts "id: #{c[0]}, slug: #{c[1]}, parent: #{c[2]}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -1,43 +1,60 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
## These tasks are destructive and are for clearing out all the
|
## These tasks are destructive and are for clearing out all the
|
||||||
# content and users from your site, but keeping your site settings,
|
# content and users from your site.
|
||||||
# theme, and category structure.
|
|
||||||
desc "Remove all topics in a category"
|
desc "Remove all topics in a category"
|
||||||
task "destroy:topics", [:category, :parent_category] => :environment do |t, args|
|
task "destroy:topics", [:category, :parent_category] => :environment do |t, args|
|
||||||
|
destroy_task = DestroyTask.new
|
||||||
category = args[:category]
|
category = args[:category]
|
||||||
parent_category = args[:parent_category]
|
parent_category = args[:parent_category]
|
||||||
descriptive_slug = parent_category ? "#{parent_category}/#{category}" : category
|
descriptive_slug = parent_category ? "#{parent_category}/#{category}" : category
|
||||||
puts "Going to delete all topics in the #{descriptive_slug} category"
|
puts "Going to delete all topics in the #{descriptive_slug} category"
|
||||||
puts log = DestroyTask.destroy_topics(category, parent_category)
|
destroy_task.destroy_topics(category, parent_category)
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Remove all topics in all categories"
|
desc "Remove all topics in all categories"
|
||||||
task "destroy:topics_all_categories" => :environment do
|
task "destroy:topics_all_categories" => :environment do
|
||||||
|
destroy_task = DestroyTask.new
|
||||||
puts "Going to delete all topics in all categories..."
|
puts "Going to delete all topics in all categories..."
|
||||||
puts log = DestroyTask.destroy_topics_all_categories
|
puts log = destroy_task.destroy_topics_all_categories
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Remove all private messages"
|
desc "Remove all private messages"
|
||||||
task "destroy:private_messages" => :environment do
|
task "destroy:private_messages" => :environment do
|
||||||
|
destroy_task = DestroyTask.new
|
||||||
puts "Going to delete all private messages..."
|
puts "Going to delete all private messages..."
|
||||||
puts log = DestroyTask.destroy_private_messages
|
puts log = destroy_task.destroy_private_messages
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Destroy all groups"
|
desc "Destroy all groups"
|
||||||
task "destroy:groups" => :environment do
|
task "destroy:groups" => :environment do
|
||||||
|
destroy_task = DestroyTask.new
|
||||||
puts "Going to delete all non-default groups..."
|
puts "Going to delete all non-default groups..."
|
||||||
puts log = DestroyTask.destroy_groups
|
puts log = destroy_task.destroy_groups
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Destroy all non-admin users"
|
desc "Destroy all non-admin users"
|
||||||
task "destroy:users" => :environment do
|
task "destroy:users" => :environment do
|
||||||
|
destroy_task = DestroyTask.new
|
||||||
puts "Going to delete all non-admin users..."
|
puts "Going to delete all non-admin users..."
|
||||||
puts log = DestroyTask.destroy_users
|
puts log = destroy_task.destroy_users
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Destroy site stats"
|
desc "Destroy site stats"
|
||||||
task "destroy:stats" => :environment do
|
task "destroy:stats" => :environment do
|
||||||
|
destroy_task = DestroyTask.new
|
||||||
puts "Going to delete all site stats..."
|
puts "Going to delete all site stats..."
|
||||||
DestroyTask.destroy_stats
|
destroy_task.destroy_stats
|
||||||
|
end
|
||||||
|
|
||||||
|
# Example: rake destroy:categories[28,29,44,85]
|
||||||
|
# Run rake categories:list for a list of category ids
|
||||||
|
desc "Destroy a comma separated list of category ids."
|
||||||
|
task "destroy:categories" => :environment do |t, args|
|
||||||
|
destroy_task = DestroyTask.new
|
||||||
|
categories = args.extras
|
||||||
|
puts "Going to delete these categories: #{categories}"
|
||||||
|
categories.each do |id|
|
||||||
|
destroy_task.destroy_category(id, true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,37 +11,68 @@ describe DestroyTask do
|
||||||
fab!(:c2) { Fabricate(:category) }
|
fab!(:c2) { Fabricate(:category) }
|
||||||
fab!(:t2) { Fabricate(:topic, category: c2) }
|
fab!(:t2) { Fabricate(:topic, category: c2) }
|
||||||
let!(:p2) { Fabricate(:post, topic: t2) }
|
let!(:p2) { Fabricate(:post, topic: t2) }
|
||||||
fab!(:sc) { Fabricate(:category, parent_category: c) }
|
fab!(:sc) { Fabricate(:category, parent_category: c2) }
|
||||||
fab!(:t3) { Fabricate(:topic, category: sc) }
|
fab!(:t3) { Fabricate(:topic, category: sc) }
|
||||||
let!(:p3) { Fabricate(:post, topic: t3) }
|
let!(:p3) { Fabricate(:post, topic: t3) }
|
||||||
|
|
||||||
it 'destroys all topics in a category' do
|
it 'destroys all topics in a category' do
|
||||||
expect { DestroyTask.destroy_topics(c.slug) }
|
destroy_task = DestroyTask.new(StringIO.new)
|
||||||
|
expect { destroy_task.destroy_topics(c.slug) }
|
||||||
.to change { Topic.where(category_id: c.id).count }.by (-1)
|
.to change { Topic.where(category_id: c.id).count }.by (-1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'destroys all topics in a sub category' do
|
it 'destroys all topics in a sub category' do
|
||||||
expect { DestroyTask.destroy_topics(sc.slug, c.slug) }
|
destroy_task = DestroyTask.new(StringIO.new)
|
||||||
|
expect { destroy_task.destroy_topics(sc.slug, c2.slug) }
|
||||||
.to change { Topic.where(category_id: sc.id).count }.by(-1)
|
.to change { Topic.where(category_id: sc.id).count }.by(-1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't destroy system topics" do
|
it "doesn't destroy system topics" do
|
||||||
DestroyTask.destroy_topics(c2.slug)
|
destroy_task = DestroyTask.new(StringIO.new)
|
||||||
|
destroy_task.destroy_topics(c2.slug)
|
||||||
expect(Topic.where(category_id: c2.id).count).to eq 1
|
expect(Topic.where(category_id: c2.id).count).to eq 1
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'destroys topics in all categories' do
|
it 'destroys topics in all categories' do
|
||||||
DestroyTask.destroy_topics_all_categories
|
destroy_task = DestroyTask.new(StringIO.new)
|
||||||
|
destroy_task.destroy_topics_all_categories
|
||||||
expect(Post.where(topic_id: [t.id, t2.id, t3.id]).count).to eq 0
|
expect(Post.where(topic_id: [t.id, t2.id, t3.id]).count).to eq 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'destroy categories' do
|
||||||
|
fab!(:c) { Fabricate(:category) }
|
||||||
|
fab!(:t) { Fabricate(:topic, category: c) }
|
||||||
|
let!(:p) { Fabricate(:post, topic: t) }
|
||||||
|
fab!(:c2) { Fabricate(:category) }
|
||||||
|
fab!(:t2) { Fabricate(:topic, category: c) }
|
||||||
|
let!(:p2) { Fabricate(:post, topic: t2) }
|
||||||
|
fab!(:sc) { Fabricate(:category, parent_category: c2) }
|
||||||
|
fab!(:t3) { Fabricate(:topic, category: sc) }
|
||||||
|
let!(:p3) { Fabricate(:post, topic: t3) }
|
||||||
|
|
||||||
|
it 'destroys specified category' do
|
||||||
|
destroy_task = DestroyTask.new(StringIO.new)
|
||||||
|
|
||||||
|
expect { destroy_task.destroy_category(c.id) }
|
||||||
|
.to change { Category.where(id: c.id).count }.by (-1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'destroys sub-categories when destroying parent category' do
|
||||||
|
destroy_task = DestroyTask.new(StringIO.new)
|
||||||
|
|
||||||
|
expect { destroy_task.destroy_category(c2.id) }
|
||||||
|
.to change { Category.where(id: sc.id).count }.by (-1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'private messages' do
|
describe 'private messages' do
|
||||||
let!(:pm) { Fabricate(:private_message_post) }
|
let!(:pm) { Fabricate(:private_message_post) }
|
||||||
let!(:pm2) { Fabricate(:private_message_post) }
|
let!(:pm2) { Fabricate(:private_message_post) }
|
||||||
|
|
||||||
it 'destroys all private messages' do
|
it 'destroys all private messages' do
|
||||||
DestroyTask.destroy_private_messages
|
destroy_task = DestroyTask.new(StringIO.new)
|
||||||
|
destroy_task.destroy_private_messages
|
||||||
expect(Topic.where(archetype: "private_message").count).to eq 0
|
expect(Topic.where(archetype: "private_message").count).to eq 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -51,13 +82,15 @@ describe DestroyTask do
|
||||||
let!(:g2) { Fabricate(:group) }
|
let!(:g2) { Fabricate(:group) }
|
||||||
|
|
||||||
it 'destroys all groups' do
|
it 'destroys all groups' do
|
||||||
DestroyTask.destroy_groups
|
destroy_task = DestroyTask.new(StringIO.new)
|
||||||
|
destroy_task.destroy_groups
|
||||||
expect(Group.where(automatic: false).count).to eq 0
|
expect(Group.where(automatic: false).count).to eq 0
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't destroy default groups" do
|
it "doesn't destroy default groups" do
|
||||||
|
destroy_task = DestroyTask.new(StringIO.new)
|
||||||
before_count = Group.count
|
before_count = Group.count
|
||||||
DestroyTask.destroy_groups
|
destroy_task.destroy_groups
|
||||||
expect(Group.count).to eq before_count - 2
|
expect(Group.count).to eq before_count - 2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -70,7 +103,8 @@ describe DestroyTask do
|
||||||
Fabricate(:user)
|
Fabricate(:user)
|
||||||
Fabricate(:admin)
|
Fabricate(:admin)
|
||||||
|
|
||||||
DestroyTask.destroy_users
|
destroy_task = DestroyTask.new(StringIO.new)
|
||||||
|
destroy_task.destroy_users
|
||||||
expect(User.where(admin: false).count).to eq 0
|
expect(User.where(admin: false).count).to eq 0
|
||||||
# admin does not get detroyed
|
# admin does not get detroyed
|
||||||
expect(User.count).to eq before_count + 1
|
expect(User.count).to eq before_count + 1
|
||||||
|
@ -79,7 +113,8 @@ describe DestroyTask do
|
||||||
|
|
||||||
describe 'stats' do
|
describe 'stats' do
|
||||||
it 'destroys all site stats' do
|
it 'destroys all site stats' do
|
||||||
DestroyTask.destroy_stats
|
destroy_task = DestroyTask.new(StringIO.new)
|
||||||
|
destroy_task.destroy_stats
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue