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

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

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

「いいね!」 1

スクリプトをありがとうございます!

しかし、マニュアルはかなり粗く、不明瞭だと感じました。例えば、Xenforo移行ガイドなどを参照して、ここで何を提案しているのかを実際に理解する必要がありました。いくつか不足している手順を追加させてください。

  • Discourse を起動して実行する
  • 元の Phorum から mysqldump を作成する - すべてクリア、すべて良好。
  • データベースダンプを Discourse フォルダにコピーする docker cp/path/to/backup/phorum_db.sql.gz app:/shared/phorum_db.sql.gz (標準コンテナ名として app を想定)

ここで不足している部分です。

  • 実際に Discourse Docker コンテナに入り、そこに MySQL をインストールします。
docker exec -it app bash
apt-get update && apt-get upgrade
# pv は進行状況を表示するために便利ですが、lsb-release は何かに必要です
apt-get install -y lsb-release pv mariadb-server mariadb-client libmariadb-dev
service mariadb start

MySQL のインストールは少しトリッキーですが、MariaDB で十分機能するはずです。

次にデータベースを作成します: mysqlCREATE database phorum

次にバックアップからデータを投入します: pv phorum_db.sql.gz | gunzip | mysql phorum (ダンプが生の SQL の場合は gunzip は不要です)。

Ruby に MySQL サポートを追加します。

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

移行スクリプトでデータを変更します (データベース認証情報、プレフィックス、パーマリンク):

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

そして元の投稿ガイドに戻ると、すべて順調です。

クリーンな Discourse インスタンスでインポータを実行します。

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

もし Discourse データベースが実際にはクリーンでない場合は、事前に bundle exec rake db:drop db:create db:migrate でクリーンアップすることをお勧めします。

“PG::ConnectionBad: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL: Peer authentication failed for user "discourse" (PG::ConnectionBad)” で失敗した場合:

  1. ‘Peer authentication failed for user “discourse”’ というエラーが発生した場合:
  2. ファイル /etc/postgresql/13/main/pg_hba.conf を編集します。
  3. すべての ‘peer’ を ‘trust’ に変更してファイルを保存します。
  4. postgresql サーバーをリロードします: /etc/init.d/postgresql reload (または psql -U postgres -c "SELECT pg_reload_conf();" かもしれませんが、これは時々失敗しました)。

最初にユーザー移行が実行されます。これは比較的遅い可能性があります (~1.2k ユーザー/分、スクリプト出力による。私は 100k ユーザーを持っていましたので…)。次にカテゴリが作成され、メッセージとトピックの移行が開始されます (同じ速度、~1.2k/分)。しかし、この時点ですでにウェブサイトにアクセスして、どのように見えるかを確認できます。

Phorum のような古いエンジンにしては、予想よりもはるかにスムーズに進んでいるようです。プライベートメッセージ (PM) の移行はないようですが、少なくとも私にとっては問題にはならないでしょう。

スクリプトと労力に改めて感謝します!

非常に複雑で、すべての手順を網羅するガイドを書くのは難しいです。特定のユーザーが知る必要があるのはどの部分なのかを特定するのは複雑です。不足している部分を別のガイドで見つけたのは素晴らしいことです。

MySQLのインストールを取得するためにMySQLテンプレートを含めることもできたかもしれませんが、あるいは何らかの方法で既にインストールされているMySQLサーバーにアクセスすることもできたかもしれません(私が通常行うことです)。

もし望むなら、PMをインポートする他のスクリプトを例として参照することもできたでしょう。