Below you will find a collection of bulk operations that can be initiated from the command line. You will need SSH access, so if you are a hosted customer, you will need to contact the Discourse team about running these commands. This topic is a wiki, so feel free to add or improve anything relevant.
Before working with the console it is extremely important that you have a recent backup. Mistakes can always happen!
First thing to do is enter your site’s container:
cd /var/discourse
./launcher enter app
Table of contents
- Change Topic Status (close, archive, unlist)
- Move subset of topics
- Delete subset of users
- Suspend a set of users based on criteria
- Export/Import all site settings
- Export/Import categories
- Set permissions for multiple categories
- Tag all topics based on a keyword
- Tag all topics within a category
- Move all topics with a specific tag to a single category
- Move all topics from one category to another
- Grant a badge to all group members
- Ensure all users are at their automatic trust level
- Destructive rake tasks
Additional Guides:
- How do I set tag tracking level defaults historically
- How do I set category tracking level defaults historically
- Change ownership of all posts by a specific user
- Replace a string in all posts
- Edit a user setting for all Discourse users
- How to modify trust level for all users
- Apply auto-close to existing topics
Change topic status ↥
Before running the following commands, run rails c
to enter the console.
-
Unlist all topics within a category (excludes post action)
You can replace
visible
withclosed
orarchived
and adjust the true/false values as neededcat_id = Category.find_by_slug('admins').id Topic.where(category_id: cat_id, visible: true).update_all(visible: false)
-
Unlist all topics within a category (includes post action)
cat_id = Category.find_by_slug("admins").id Topic.where(category_id: cat_id, visible: true).find_each do |topic| topic.update_status('visible', false, Discourse.system_user) end
-
Close all topics created before a specified date (includes post action)
Topic.where(closed: false).where("created_at < '2015-01-01'").find_each do |topic| topic.update_status('closed', true, Discourse.system_user) end
Moving topics ↥
Move a collection of topics from one category to another
rails c
topic_ids = [12,16,29]
cat_to = Category.find_by_slug('faq')
Topic.where(id: topic_ids).update_all(category_id: cat_to.id)
Category.update_stats
Delete subset of users ↥
Delete users that have never posted and have not visited since a specified date
rails c
User.joins(:user_stat).where("user_stats.post_count = 0 AND previous_visit_at <= '2016-05-20'::timestamp").destroy_all
Suspend a set of users based on criteria↥
Set who will be logged as suspending the users
rails c
logger = StaffActionLogger.new(User.find_by(username_lower: "tshenry"))
Create a suspension timeframe and reason
suspend_till = DateTime.new(2057,12,31)
reason = 'Completed Course'
In this example, our user criteria will be group membership.
target_group = Group.find_by_name("summer_students")
users = User.joins(:group_users).where(group_users: {group_id: target_group.id})
Suspend each user based on the values established above:
users.each do |u|
u.suspended_till = suspend_till
u.suspended_at = DateTime.now
u.save!
logger.log_user_suspend(u,reason)
end
Export/Import all site settings ↥
To simply print out all of the settings that have been changed on your site, run:
rake site_settings:export
If you want to export the settings to a file:
rake site_settings:export > saved_settings.yml
If you want to import settings from a file:
rake site_settings:import < saved_settings.yml
Export/Import categories ↥
There are two options for exporting and one method to handle importing.
Export a set of complete categories
First get a list of your category IDs:
rails c
Category.all.pluck("id", "name")
exit
Then space-separate the category IDs in the export rake task. For example:
rake export:categories["12 6"]
Export your site’s category structure
This is essentially copying the “skeleton” of your Discourse site. It includes every category along with any groups associated with existing category permissions. It does not include topics:
rake export:category_structure
If you want the category structure along with any groups associated with the category permissions and any members of those groups:
rake export:category_structure[true]
Importing a category file
Only import to a fresh Discourse site, or one that does not have existing categories with the same name.
Use the exported file’s name like the example below:
rake import:file["category-export-2019-05-16-052430.json"]
Set permissions for multiple categories ↥
Note that this will remove any existing access restrictions you have set up for the categories involved. Make sure to include all of the relevant permissions.
-
Get a list of categories along with their IDs
rails c Category.all.pluck("name", "id")
-
Create an array with the category IDs you wish to target.
category_ids = [6,7,8,10]
-
Change the permissions. The
set_permissions
function can utilize the following parameters::full
,:create_post
,:readonly
-
A single permission. For example, making a set of categories staff only:
Category.where(id: category_ids).find_each do |category| category.set_permissions(:staff => :full) category.save end
-
Multiple permissions. For example, making a set of categories read-only for normal users:
Category.where(id: category_ids).find_each do |category| category.set_permissions(:everyone => :readonly, :staff => :full) category.save end
-
User group permissions. For example giving one group full permissions and another group read-only for a set of categories:
artists_group = Group.find_by_name("artists") buyers_group = Group.find_by_name("buyers") Category.where(id: category_ids).find_each do |category| category.set_permissions(artists_group.id => :full, buyers_group.id => :readonly) category.save end
-
Bulk Tag All Topics Based on a Keyword ↥
The following script will allow you to tag topics based on the presence of a keyword in the topic title or its posts. Start by creating an array of keywords:
rails c
keywords = ['apples','oranges']
Next we need to define a method:
def tag_by_keyword(word, tag_name)
tag = Tag.find_by_name(tag_name) || Tag.create(name: tag_name)
keyword_topics = Topic.joins(:posts).where("topics.title ~* :keyword or posts.raw ~* :keyword", keyword: "\\y#{word}\\y").distinct
keyword_topics.each do |topic|
if topic.tags.exclude?(tag)
topic.tags << tag
end
end
end
And finally run each keyword through the method. The following with tag each relevant topic with a tag called “fruit”:
keywords.each { |word| tag_by_keyword(word, 'fruit') }
Bulk Tag All Topics Within a Category ↥
Template: rake tags:bulk_tag_category["<tag>|<tag>",<category_id>]
This would be particularly useful when trying to convert a category to a tag
rails c
Category.find_by_slug('support').id
exit
rake tags:bulk_tag_category["support",6]
Move all topics with a specific tag to a single category ↥
When trying to restructure your Discourse site, you may find that you want to move a collection of topics without triggering any notifications. One way to do this is to create a temporary tag, apply the tag to appropriate topics, move the topics to a specific category using the code below, then finally delete the temporary tag.
Get the tag and destination category.
rails c
tag = Tag.find_by_name("tutorial")
cat_to = Category.find_by_slug('guides')
Move the tagged topics to the destination category.
Topic.joins(:topic_tags).where("topic_tags.tag_id = ?", tag.id).update_all(category_id: cat_to.id)
Update the topic counts of the affected categories.
Category.update_stats
Move all topics from one category to another ↥
Find the category IDs using the rails console:
rails c
Category.find_by_slug('support').id
Category.find_by_slug('faq').id
exit
The first value is the starting category ID and the second is the destination category ID.
rake categories:move_topics[15,6]
Grant a badge to all group members ↥
Grant a badge to all users that belong to a specific group. The first value is the group ID and the second is the badge ID.
rails c
Group.find_by_name("event_participants").id
Badge.find_by_name("event_badge").id
exit
rake groups:grant_badge[42,102]
Ensure all users are at their automatic trust level ↥
Say you set the default trust level for new or invited users to a value that isn’t working out quite the way you expected (such as TL4). Now you want to change it so your users are at the trust level they would be automatically, given their current stats. The following commands will ensure all users are at the trust level they should be according to Understanding Discourse Trust Levels. Note: users with locked trust-levels will not be affected.
Make sure all users are set to the correct trust level:
rails c
User.all.find_each do |user|
Promotion.recalculate(user)
end
Refresh the group stats to reflect the changes:
Group.ensure_consistency!
Destructive rake tasks ↥
Delete entire categories
The following will allow you to destroy multiple categories, along with any subcategories and topics that belong to those categories.
Print out a list of category IDs
rake categories:list
Destroy a set of categories based on their ID
rake destroy:categories[10,11,12,18,30]
Delete all topics in a category
Remove all private messages
rake destroy:private_messages
Destroy all groups
rake destroy:groups
Destroy all non-admin users
rake destroy:users
Destroy site stats
rake destroy:stats
Anonymize all users except staff
rake users:anonymize_all