Migrate a Phorum forum to Discourse

If you have an old Phorum forum and thinking to migrate to Discourse, then this tutorial is for you. The process is straightforward and we will be using the official Phorum Importer script. Let’s get started.

On a high level, we will do the following.

  • Preparing the local development environment.
  • Exporting the database from the production environment.
  • Importing the production database to a local Discourse instance.
  • Running the Phorum importer script.

What can be migrated

  • Categories
    • Every forums and folders => Root category
  • Topics & Posts
  • Users (with the following attributes)
    • banned status
    • username
    • real name => name
    • email
    • admin status
    • date added
    • last active

Preparing Local Development Environment

Follow one of these guides to install Discourse itself and consult this guide if you have any problems.

Install MySQL DB server;

Ubuntu 18.04:

$ sudo apt update
$ sudo apt install mysql-server -y

After finishing installing MySQL, check its status:

$ systemctl status mysql.service

If it is not running, run the following:

$ sudo systemctl start mysql

MacOS:

$ brew install mysql@5.7
$ echo 'export PATH="/usr/local/opt/mysql@5.7/bin:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile

Check the services status:

$ brew services list

You should see something like this:

...
mysql@5.7         started
...

Otherwise, run the following and check again:

$ brew services start mysql@5.7

For Windows, you can follow the official installation guide.

This environment will be our Discourse server.

Exporting The Database from Production Environment:

Export the production database (from Phorum production server):

$ mysqldump -u USER_NAME -p DATABASE_NAME > phorum_dump.sql

Copy the database dump to the Discourse server.

:bulb: Use scp or rsync to copy the database. And of course, you can gzip it first.

Importing The Production Database to Discourse server

On Discourse server, Create a database:

$ mysql -u root

:bulb: If your database user has a password you should use: mysql -u root -p then type your password.

mysql> CREATE DATABASE phorum;

Make sure that the database is created:

mysql> SHOW DATABASES;

You should see something like this:

+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| phorum             |
| sys                |
+--------------------+
5 rows in set (0.04 sec)

This is an empty database. Our next step is to import the production database into it.

$ mysql -u root phorum < phorum_dump.sql

While we are here, let’s get the table prefix. We will need it later:

$ mysql -u root
mysql> USE phorum;
mysql> SHOW TABLES;

You will see something like this:

+---------------------------+
| Tables_in_phorum          |
+---------------------------+
| pw_banlists           |
| pw_files              |
| pw_forum_group_xref   |
| pw_forums             |
| pw_groups             |
| pw_messages           |
| pw_messages_edittrack |
| pw_pm_buddies         |
| pw_pm_folders         |
| pw_pm_messages        |
| pw_pm_xref            |
| pw_search             |
| pw_settings           |
| pw_subscribers        |
| pw_user_custom_fields |
| pw_user_group_xref    |
| pw_user_newflags      |
| pw_user_permissions   |
| pw_users              |
+---------------------------+
19 rows in set (0.00 sec)

From the output above you can see that the prefix is pw_.

Running Phorum Importer Script

First, install the importer dependencies:

$ cd ~/discourse
$ echo "gem 'mysql2', require: false" >> Gemfile
$ bundle install

Now let’s configure the script to fits our requirements. In our example this will do:

PHORUM_DB = "phorum"
TABLE_PREFIX = "pw_"
BATCH_SIZE = 1000

# ...

host: "localhost",
username: "root",
password: "", # Change this if you have a password for MySQL database
database: PHORUM_DB

If you want to create a url redirecting, then you should uncomment the following:

# categories.each do |category|
#   Permalink.create(url: "list.php?#{category['id']}", category_id: category_id_from_imported_category_id(category['id'].to_i))
# end

#...

# results.each do |post|
#   if post['parent_id'] == 0
#     topic = topic_lookup_from_imported_post_id(post['id'].to_i)
#     Permalink.create(url: "read.php?#{post['category_id']},#{post['id']}", topic_id: topic[:topic_id].to_i)
#   end
# end

It will be like this:

categories.each do |category|
  Permalink.create(url: "list.php?#{category['id']}", category_id: category_id_from_imported_category_id(category['id'].to_i))
end

#...

results.each do |post|
  if post['parent_id'] == 0
    topic = topic_lookup_from_imported_post_id(post['id'].to_i)
    Permalink.create(url: "read.php?#{post['category_id']},#{post['id']}", topic_id: topic[:topic_id].to_i)
  end
end

Run the importer with a clean Discourse instance:

bundle exec rake db:drop db:create db:migrate
bundle exec ruby script/import_scripts/phorum.rb

The importer will connect to the MySQL server and migrates our Phorum database to Discourse database.

Start Discourse server after the importer finish importing:

$ bundle exec rails server

Start Sidekiq to process the migrated data:

$ bundle exec sidekiq

:bulb: You can monitor sidekiq progress at http://localhost:3000/sidekiq/queues.

Setup your Discourse production server by following this tutorial.

Perform a backup for Discourse database and upload it to your Discourse production server by following this tutorial.

It’s done :tada:

7 Likes

I’m about to start work on migrating a Phorum forum to Discourse. I would like to preserve files attached to posts (mostly pictures). Looks like this is not supported in the current migration script. Has anyone noodled on that already?

1 Like

I’ve made a couple tweaks to the migration script for “phorum” including migrating attachments/files/uploads. The pull request is here:

1 Like

Thanks for the scripts!

However I feel like the manual is quite raw and unclear. I had to refer to e.g. Xenforo migraiton guide to actually understand what do you suggest here. Let me add some missing steps:

  • Have Discourse up and running
  • Create mysqldump from the original Phorum - all clear, all good.
  • Copy database dump into Discourse folder docker cp/path/to/backup/phorum_db.sql.gz app:/shared/phorum_db.sql.gz (assuming app as standard container name)
    And here the missing parts:
  • Actually jump into Discourse Docker container and install MySQL there:
docker exec -it app bash
apt-get update && apt-get upgrade
# pv just for convenience to display progress, lsb-release is required by something
 apt-get install -y lsb-release pv mariadb-server mariadb-client libmariadb-dev
 service mariadb start

Note MySQL installation is a bit more tricky, but Mariadb should work as fine.

Then create a database: mysqlCREATE database phorum.

Then fill it from backup: pv phorum_db.sql.gz | gunzip | mysql phorum (no need in gunzip if your dump is bare sql)

Add MySql support to Ruby:

cd /var/www/discourse/
echo "gem 'mysql2'" >>Gemfile
bundle config unset deployment
bundle install --no-deployment

Modify data in migration script (database credentials, prefix, permalinks):

nano /var/www/discourse/script/import_scripts/phorum.rb

And then back to the original post guide, all good there:

Run the importer with a clean Discourse instance:

git config --global --add safe.directory /var/www/discourse
bundle exec ruby script/import_scripts/phorum.rb

Iа Discourse database is not really clean, better clean it with bundle exec rake db:drop db:create db:migrate beforehand.

If it fails with “PG::ConnectionBad: connection to server on socket “/var/run/postgresql/.s.PGSQL.5432” failed: FATAL: Peer authentication failed for user “discourse” (PG::ConnectionBad)”:

  1. If you get the error 'Peer authentication failed for user "discourse"':
  2. Edit the file /etc/postgresql/13/main/pg_hba.conf
  3. change all ‘peer’ to ‘trust’ and save the file
  4. reload postgres server: /etc/init.d/postgresql reload (or maybe psql -U postgres -c "SELECT pg_reload_conf();", but this was failing for me sometimes)

First it runs users migrations which might be relatively slow (~1.2k users/minute as per script output. I had 100k users, so…). Then it creates categories and starts migrating messages and topics (same speed, ~1.2k/minute), but at this point you can already access the website and see how does it look like.

Seems way smoother than I would expect for such an ancient engine like Phorum. It seems there is no migration of Private Messages (PM), but should be not a blocker for me at least.

Thanks again for the script and efforts!

It’s hard to write a guide for something so complicated that covers all the steps. Figuring out just which pieces a particular user might need to be told is complex. Good work finding another guide with the missing pieces.

You might have been able to include the mysql template to get mysql installed, or maybe you’d access a mysql server that was already installed somehow (what I usually do).

If you wanted you could look at some other scripts that import PMs to use as an example.