Bulk convert topics from a Category to Group PMs

I’ve since then (clearly took a while!) worked out two ways to achieve this. You can do it via the UI, which works well for small Categories (and small groups). It takes a bit of care to avoid firing off unhelpful notifications while you do so, but is very doable.

Alternatively, you can use the Rails Console (assuming you are self-hosted), which is faster and much more powerful.

From the UI

  1. Remove the other group members from the Group (but keep a list of usernames / emails so you can re-add them easily). This will avoid them being notified.
  2. Cycle through each topic in the Category, converting each one into a PM using the admin spanner menu
    • I find this easiest to do by opening each topic in its own tab, and then moving across them one by one
  3. Add the Group to each of the Topics (now PMs)
  4. Add the Group members back into the group
  5. Tidy up / delete all the ‘small posts’ in each Group Message as desired

In the Rails Console

Access the console by entering the container and then calling up the console:

./launcher enter app
  rails c

Set the Category and Group, and then copy the script below into the console:

category_id = =CategoryID= 
group_name = '=GroupName='

group = Group.find_by('lower(name) = ?', group_name.downcase)
about_id = Category.find(category_id).topic_id

topic_ids = Topic.where(category_id: category_id).where.not(id: about_id).pluck(:id)

Topic.where(id: topic_ids).find_each do |topic|
  topic.archetype = Archetype.private_message
  topic.category_id = nil
  topic.subtype = 'user_to_user'
  topic.save!

  TopicAllowedGroup.find_or_create_by!(topic_id: topic.id, group_id: group.id)

  Post.where(topic_id: topic.id).pluck(:user_id).uniq.each do |uid|
    TopicAllowedUser.find_or_create_by!(topic_id: topic.id, user_id: uid)
  end
end

This doesn’t notify the users at all, and preserves the users participating in the posts nicely (just in case they aren’t in the Group).

Of note, you might need to run this subsequently in the Rails Console if you want to delete the Category before the background job recounts everything:

Jobs::CategoryStats.new.execute(category_id: category_id)
Script for the reverse (Group Inbox to Category), click to reveal

This script should send things back in the other direction nicely (untested)

category_id = =CategoryID= 
group_name = '=GroupName='

group = Group.find_by('LOWER(name) = ?', group_name.downcase)
raise "Group not found" unless group

topic_ids = TopicAllowedGroup.where(group_id: group.id).pluck(:topic_id)
topics = Topic.where(id: topic_ids, archetype: Archetype.private_message, deleted_at: nil)

topics.find_each do |topic|
  topic.archetype = Archetype.default  # or "regular"
  topic.subtype = nil
  topic.category_id = target_category_id
  topic.save!

end