论坛从 UseBB 迁移到 SMF

使用 UseBB 作为论坛程序已经有五年历史了,还专门为它做了简体中文语言包。然而从2013年之后,UseBB 的作者就不再更新,后来更是宣布停止支持及计划中的 2.0 版本的开发。随着时间越来越长,出于安全考虑,决定还是将水景一页的讨论区从 UseBB 迁移到 SMF(Simple Machines Forum)。

写这篇博客的目的就是对迁移过程做个记录,以方便有同样需要的朋友做参考。

1. 背景

迁移一个论坛一般只需要将旧有数据库中的内容对应转移到新论坛程序的数据库中即可。说起来很容易,但是真正要做到一一对应,除了要求新论坛程序功能特色多于或等于原论坛程序外,还有对两种论坛程序的数据库结构都熟悉才好写出转换程序。这其实是个很复杂的工作。然后还要对新论坛程序进行个性化设置。鉴于过程的繁琐和对未知的恐惧,转换工作迟迟未能实施。

幸好从网上搜索到 SMF 提供了从 UseBB 到 SMF 的转换脚本程序。虽然是针对 UseBB v0.5.2 版本的,但是因为 UseBB 的数据库结构没有什么变化,该转换程序对 UseBB 1.0.16 版本(最后版本)同样适用。只是转换过程会有些小问题需要矫正。

如果不想用 SMF 也可能需要通过 SMF 的转换程序进行中转。这是我我找到的唯一的一个转换程序。其他论坛程序一般都提供了从 SMF 转到自己程序的转换脚本,却一般都没有针对 UseBB 的,毕竟 UseBB 比较小众。可是既然 SMF 本身也是数一数二的论坛程序,何必不直接用它呢?!

2. 转换的基本过程

我是先在本地虚拟机上搭建论坛程序进行测试的。这样至少能保证不造成破坏。强烈建议先测试,后实施。

转换大致分为下面几个步骤,

  1. 在一个新目录(如 /smf/ )中安装 SMF 论坛软件(先不要动原来的 UseBB 论坛程序);
  2. (可能需要)原 UseBB 数据库字符编码转换;
  3. 使用转换脚本进行数据库迁移(下面会有详细说明,并提供水景一页修改过的转换脚本下载);
  4. 进一步修理导入 SMF 的数据库记录(因为导入过程会对一些字符进行处理,而其中有些是我们不希望有的);
  5. 确认没有问题后,切换 SMF 到原 UseBB 目录;
  6. SMF 配置;
  7. 网址转换(301 跳转)。

下面就按照这个思路进行详细介绍一些心得体会和注意事项。当然是按照水景一页讨论区的特殊情况来说明的。仅供参考。

3. 全新安装 Simple Machines Forum

一定要是全新安装。因为数据库迁移的过程使用的是数据在数据库间的直接拷贝,如果 SMF 中已有数据,极有可能会被替换或破坏。

具体安装过程就不啰嗦了。SMF 的安装脚本设计得很到位了,提示信息也很丰富。

这里有点需要注意的是,为了更好的支持简体中文字符,建议在安装的时候选择使用 UTF-8 作为默认的字符编码。即,在安装过程的第四步 Step 4: Forum Settings 的时候勾选“UTF-8 Character Set”。下面的操作都是默认为采用了这样的设置的。

4. 原 UseBB 数据库字符编码转换

这个之所以标注为“可能需要”,是考虑到有人可能不想用 UTF-8 编码(真的有人要支持中文而不用UTF吗?),或者有人的 UseBB 数据库编码格式已经转换为 UTF-8 了。

之前水景一页也折腾过 UseBB 的字符编码的问题。但是折腾得不到位,因此数据库里一直有两种形式的编码:一种是属于 latin1 的乱码(形如 水景一页,一种是 UTF-8 字符在 SGML、HTML、XML 等语言中的字符值引用(NCR,Numeric Character Reference,形如 水景一页参考1参考2),又称转义序列(Escape Sequence)。原因是复杂的,请参考之前的讨论。如果直接导入 SMF,并设置 SMF 使用 UTF-8 编码,乱码在页面会显示为乱码而字符值引用在 UTF-8 的页面上显示是正常的汉字(或其他亚洲字符)。

在这种情况下执行论坛迁移的话,新的 SMF 的数据库里面也会显示为乱码。虽然在两边都可以进行数据库字符编码的转换,但是 UseBB 的数据库的表更少,写转换语句更轻松些。所以先在 UseBB 里进行编码转换,然后再将数据库内容迁移到 SMF 的数据库里。需要特别注意的是,一定要先备份 UseBB 的数据库,避免因转换过程出错造成不可挽回的损失。

4.1 确认 UseBB 数据库的字符集和排序规则

作为站长,对自己的论坛程序应该还是比较了解的吧。如果是 UseBB 默认的,那么应该就是默认的 MySQL 字符集为 latin1_swedish_ci,排序规则(Collation)也是 latin1_swedish_ci。

如果曾经跟随水景一页进行过 UseBB 字符编码的折腾,则其 UseBB 论坛的数据库应该是跟这里介绍的一样,嗯,其实也还是默认的 latin1_swedish_ci。如下图所示。

改动过的 UseBB 数据库字符编码和排序规则

改动过的 UseBB 数据库字符编码和排序规则

这个是可以从 phpMyAdmin 中查看到的。

如果看到的 Collation 那一列全都显示的是 utf8_general_ci,则跳过下面的 4.2 直接到 4.3,并且因为这里都不一样了,也许后面会有不少变故。

如果点开一个表,比如 usebb_posts,看到 content 那一列里面的数据没有乱码,则应该直接跳到 4.4。

如果其中的汉字显示全部是正常的,则直接跳到

4.2 修改 USEBB 数据库的字符集和排序规则

这个是可以在 phpMyAdmin 的 SQL 页面执行的,也可以在服务器里登录到 mysql 命令行执行。这里以在 phpMyAdmin 中的操作为例。假设 UseBB 所使用的数据库名称为 usebb,表的前缀也为默认的 usebb_

转换字符编码的指令格式为,

ALTER DATABASE usebb character set utf8;

转换排序规则(Collation)的指令格式为,

ALTER TABLE tablename CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;

这两条指令参考了这里

所以对于 UseBB 的数据库来说,有一部分表是不包含中文字符的,所以就不需要转换了。需要转换的几个表的操作指令我都列在下面了,也许可以直接复制粘贴就行。

打开 phpMyAdmin 中的 usebb 对应的数据库,点击打开 SQL 标签页(上图的第二个标签页),在数据库查询窗口中复制粘贴下面的语句并执行:

ALTER TABLE 'usebb_badwords' CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE 'usebb_cats' CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE 'usebb_forums' CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE 'usebb_members' CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE 'usebb_posts' CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE 'usebb_searches' CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE 'usebb_topics' CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;

也可以将下一节 4.3 的语句接着粘贴在后面再执行。

4.3 转换乱码

这里先转换乱码(形如 水景一页),也就是转换 latin1 编码到 utf8 编码,后面 4.4 节再说转义的字符值引用的问题。

经过前面 4.2 节的折腾,几个会产生乱码,需要转换的表的 Collation 已经或即将要转换为 utf8_general_ci 了。数据库 usebb 也已经或即将要转换为 utf8_general_ci 编码。下面就需要将表中的一些列里面的一些属于 latin1 的乱码转换为正常的 UTF-8 编码的字符。

这里使用数据库查询的正则表达式匹配来查找需要转换的具体值。因为同一列(比如帖子内容)有些是 latin1 的乱码,有些却是字符值引用(NCR),不能都执行这个转换,否则会将本来是 NCR 的那些字符又搞成全是 ? 号了,那可能就回天乏术了(嗯,如果有备份就没问题 ^_^)。

转换 latin1 的乱码到正常的 utf8 字符采用的是数据库直接操作。可以在 phpMyAdmin 的 SQL 页面执行,也可以在服务器里登录到 mysql 执行。这里以在 phpMyAdmin 中的操作为例。

因为前面说了,数据库的表中有两类文本(乱码和字符值引用),根据字符值引用的特征(&#_____;)很容易通过正则表达式筛选出来出来,剩下的全部都当成有乱码存在的单元。当然这样处理比较粗糙,可能会有些小问题,但是对于我自己的情况,结果是可以接受的。

这里使用的转换语句格式(参考这里)为,

UPDATE `TABLE_NAME` SET `Column_Name`=CONVERT(binary CONVERT(`Column_Name` USING latin1) USING utf8) WHERE `Column_Name` NOT REGEXP "\&\#[0-9]{5}\;";

可以看出来,需要对每个中的每个需要转换的操作一次。好在 UseBB 本身的数据库的表不多,需要转换的列也不是太多,直接一个个的处理了。

对应所有需要处理的表及其列的 SQL 执行语句如下(如果 table_name 的前缀不是 usebb_,请注意修改)。打开 phpMyAdmin 中的 usebb 对应的数据库,点击打开 SQL 标签页,在数据库查询窗口中复制粘贴下面的语句并执行:

UPDATE 'usebb_badwords' SET 'word'=CONVERT(binary CONVERT('word' USING latin1) USING utf8) WHERE 'word' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_badwords' SET 'replacement'=CONVERT(binary CONVERT('replacement' USING latin1) USING utf8) WHERE 'replacement' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_cats' SET 'name'=CONVERT(binary CONVERT('name' USING latin1) USING utf8) WHERE 'name' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_forums' SET 'name'=CONVERT(binary CONVERT('name' USING latin1) USING utf8) WHERE 'name' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_forums' SET 'descr'=CONVERT(binary CONVERT('descr' USING latin1) USING utf8) WHERE 'descr' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_members' SET 'name'=CONVERT(binary CONVERT('name' USING latin1) USING utf8) WHERE 'name' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_members' SET 'displayed_name'=CONVERT(binary CONVERT('displayed_name' USING latin1) USING utf8) WHERE 'displayed_name' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_members' SET 'signature'=CONVERT(binary CONVERT('signature' USING latin1) USING utf8) WHERE 'signature' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_members' SET 'real_name'=CONVERT(binary CONVERT('real_name' USING latin1) USING utf8) WHERE 'real_name' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_members' SET 'location'=CONVERT(binary CONVERT('location' USING latin1) USING utf8) WHERE 'location' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_members' SET 'occupation'=CONVERT(binary CONVERT('occupation' USING latin1) USING utf8) WHERE 'occupation' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_members' SET 'interests'=CONVERT(binary CONVERT('interests' USING latin1) USING utf8) WHERE 'interests' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_posts' SET 'content'=CONVERT(binary CONVERT('content' USING latin1) USING utf8) WHERE 'content' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_searches' SET 'results'=CONVERT(binary CONVERT('results' USING latin1) USING utf8) WHERE 'results' NOT REGEXP "\&\#[0-9]{5}\;";
UPDATE 'usebb_topics' SET 'topic_title'=CONVERT(binary CONVERT('topic_title' USING latin1) USING utf8) WHERE 'topic_title' NOT REGEXP "\&\#[0-9]{5}\;";

当然,可以把前面第 4.2 节的操作语句和这里的操作语句一起放进去一次执行,当然顺序不能反了。如下图,

在 phpMyAdmin 中直接运行数据库查询语句进行文本的编码转换

在 phpMyAdmin 中直接运行数据库查询语句进行文本的编码转换

4.4 字符值引用的处理

字符值引用可以有两种途径处理,导出数据库后用文本编辑器处理,或者 SMF 内置的数据库工具。

:因为字符值引用不会造成网页上字符的显示问题,因此这一步也可以等到全部迁移完成之后再进行。

SMF 内置工具是在完全设置好 SMF 之后通过其内置的数据库工具(管理 –> 功能和选项 –> 维护 –> 论坛维护 –> 数据库,下面有个“Convert HTML Entities to UTF-8 Characters”)执行一下就可以进行转换。 但是不知道现在还有没有问题。我刚开始不知道这个方法,所以采用了另一种方法。

根据字符值引用的特点,每个代码与一个字符是对应的。NotePad++ 有个插件 HTML Tag 可以将选中的文本中的字符值引用转换为对应的正常字符。有了 NotePad++ 就可以直接从其 Plugins –> Plugin Manager 中找到 HTML Tag 并安装。然后,

  1. 将经过前面处理的 UseBB 的数据库再次导出,如果是压缩过的则解压之,假设为 usebb-with-htmlentities.sql;
  2. 用 NotePad++ 打开 usebb-with-htmlentities.sql,Ctrl + A 全选所有内容,然后依次点击 Plugins –> HTML Tag –> Decode entities 即可,如下图,

    使用 NotePad++ 插件 HTML Tag 转换 HTML Entities 字符值引用

    使用 NotePad++ 插件 HTML Tag 转换 HTML Entities 字符值引用;

  3. 等待转换完成 – 这个貌似效率不高,水景一页讨论区这个不到 2MB 大小的数据库在我的电脑上等待了大约2分钟,所以如果数据库较大,要做好心理准备;
  4. 转换完成后保存该文件,压缩成 zip 格式即可;
  5. 到 UseBB 所在数据库,全选所有数据表,将它们清空,或者直接删除;
  6. 将刚才转换后的文件上传并导入数据库。

5. 将 UseBB 数据库内容迁移至 SMF 数据库

再次提醒:该操作会覆盖 SMF 数据库中的原有的部分内容!

5.1 数据迁移

SMF 提供了一个转换工具,见这里

这个工具存在 2 个错误以及几个已知问题(见这里)。其中的错误为:

  1. 错误 1,
    Converting members...Wrong value type sent to the database. Date expected. (birthdate)

    这是因为 UseBB 中默认的生日日期是 0 而 SMF 中默认是 0001-01-01,两者不匹配。至于如果是正常的生日日期,不知道会不会有问题。水景一页没有测试,而且发现没有论坛会员填写生日。所以可将下载的 smf_2-0_usebb_converter.zip 解压后,将其中 usebb_to_smf.sql (可用 NotePad++ 打开编辑)中第 29 行的

    birthday AS birthdate,

    改为

    IF(birthday = 0, '0001-01-01', convert(birthday, DATE)) AS birthdate,

    因为 UseBB 的生日格式是 int(8) 的 YYYYMMDD 而 SMF 的格式是 (DATE) 的 YYYY-MM-DD

  2. 错误 2,是在转移 usebb_badwords 的时候,可能转换程序有问题,也可能是因为我的 usebb_badwords 里面是空的,这一步无法通过。解决办法是, 删除或注释掉 usebb_to_smf.sql 中 139-168 行的内容,所以 bad words 也不会导入进新论坛程序

进行了上述错误修正的 UseBB 到 SMF 2 的转换文件顺便提供给大家:

smf_2-0_usebb_converter_cnzhx.zip

该工具使用方法为

  1. 将解压得到的 2 个文件放到新安装的 SMF 论坛所在目录;
  2. 从浏览器访问 convert.php 文件然后根据提示操作。

需要注意的就是,跟前面对应,在提示的配置中注意勾选那个使用默认的 UTF-8 编码的选项。

5.2 对导入数据后的 SMF 数据库进行后处理

特别提示:切记要做好备份!

在迁移数据的时候,一些特殊字符被执行了特殊处理,在执行 convert 转换的时候会因为考虑到安全问题而被转义(escape),导致诸如超链接之类的显示不正常,如,

  1. 所有英文半角单引号 ' 都被加上了个反斜杠成了 \'
  2. 所有英文半角双引号 " 都被加上了个反斜杠成了 \"
  3. 所有反斜杠 \ 都被加上了个反斜杠成了 \\
  4. 所有关于字体大小的 BBCode 标签、表情符号等都没有正确转换,即,这些标签及符号都被按照普通的字符显示出来了
  5. 如果原来启用了以 html 显示,到这里也可能不起作用而只是将 html 标签显示为普通文本

其中 A B 好解决,剩下的似乎没办法。也就只好这样了。解决办法是,

  1. 导出 SMF 的数据库,编辑 .sql 文件,全局进行如下的搜索替换:
    • \\' 替换为 '
    • \\" 替换为 "
    • \\\\ 替换为 \\(这个替换一定要在前两个后面,而且极有可能造成问题;如果测试发现有问题就不要做这个替换)
  2. 将修改后的 SMF 数据库重新导入服务器(在导入前需要清空服务器上的 SMF 的数据库)。

6. 更换文件夹

再次检查一下迁移到的 SMF 论坛,多查看几个页面,看看是否对迁移结果满意。如果满意,那就可以用这个 SMF 论坛替换原来的论坛了。

两种方案(应该无所谓优劣吧,反正搜索引擎会正确对待 301 跳转):

  1. 保留原来的 UseBB 论坛,但是将链接执行 301 跳转到新的基于 SMF 的论坛。如果是这样,现在就可以直接跳到下一节了。
  2. 备份原来的基于 UseBB 的论坛(假设在 /bbs/ 目录下),将新的 SMF 论坛的安装目录(/smf/)改为 /bbs/。

我采用的是方法 2. (不知道为毛脑子抽筋要搞这么麻烦)。需要执行的操作有:

  1. 将原来的 /bbs 目录改名为 /bbs-backup;
  2. 将新的 /smf 目录改名为 /bbs;
  3. 修改新的 /bbs 目录下的 Settings.php 文件,将其中的网址目录都做对应修改;
  4. 以管理员身份(嗯,这里应该是需要使用原来 UseBB 论坛的那个管理的账号和密码了)登录基于 SMF 的论坛的管理后台(新文件夹的)后,新的 SMF 会提示主题问题(因为安装目录改变了),根据提示修改 URL 以及目录即可(主要是 Theme Settings);
  5. 还有其它一些与目录有关的设置,如附件上传目录、头像上传目录等等。

建议把 SMF 的管理后台每个角落都浏览一遍,毕竟还要进行配置设置的嘛 :D

7. 链接跳转

UseBB 的链接设置跟 SMF 的不同。即使采用了搜索引擎友好的 URL 形式,也还是需要做链接跳转,将原来的 UseBB 论坛的帖子链接转到新的论坛里。

太麻烦了!这里只跳转了两类链接:

  • 版块
    • 旧:/bbs/forum-5.html
    • 新:/smf/index.php?board=5.0
  • 主题
    • 旧:/bbs/topic-297.html
    • 新:/smf/index.php?topic=297.0

使用服务器的 URL ReWrite 功能,增加下面的规则:

RewriteBase /bbs/
RewriteRule ^forum-([0-9]+)\.html$ index.php?board=$1.0 [R=301,L]
RewriteRule ^topic-([0-9]+)\.html$ index.php?topic=$1.0 [R=301,L]
RewriteRule ^topic-([0-9]+)-([0-9]+)\.html$ index.php?topic=$1.$20 [R=301,L]

下面这种,因为旧的链接里面缺少了对应的 topic 的 ID,因此无法改写url,只好改成临时的,由 SMF 自己负责跳转。

  •  回复
    • 旧:/bbs/topic-post1014.html
    • 新:/smf/index.php?topic=604.msg1014#msg1014
    • 临时:/smf/index.php?msg=1014

其它那些不十分重要的链接就算了吧,就让它们 404 去吧……快累趴下了。

迁移算是基本完成了。

2016.02.11 更新

增加针对主题分页的链接的重写规则(上面规则的第四个(行)),可以避免网址 404 错误但是无法准确定位到正确的位置。之前遗漏了单个主页分页的链接地址的重写。在 UseBB 中单个主题的多个回复超过一定条数会显示为多页,链接为 topic-{n}-{m}.html,其中,{n} 是 topic 编号,{m} 是分页编号。而 SMF 中形式如 topic={n}.{x},其中 {x} 是某个分页中的第一个回复的编号。

想了很久很久了,又测试了很多天,写这个记录又花了好几个小时。也不知道有人打赏没 :-)

 ©

本文发表于水景一页。永久链接:<http://cnzhx.net/blog/forum-converted-from-usebb-to-smf/>。转载请保留此信息及相应链接。

2 条关于 “论坛从 UseBB 迁移到 SMF” 的评论

    • 嗯,是很好啊,看到很多论坛都是用的phpBB。我两个都用过,感觉不相上下吧。

时间过去太久,评论已关闭。
如果您有话要说,请到讨论区留言并给出此文章链接。
谢谢您的理解 :-)