delphij's Chaos

选择chaos这个词是因为~~实在很难找到一个更合适的词来形容这儿了……

15 Jan 2023

历史 Movable Type 评论迁移到了 Remark42

上回书 说到留言板的问题时提到留言板当时没有解决, 在文内的模板中可以看到我当时是采用了一种非常对付的方法: 直接将之前的评论直接作为文章内容输出出来。这导致了页面不太美观。

趁着周末我把之前 Movable Type 的数据库重新捋了一下,把其中的 2256 条留言用类似 isso 迁移时的方法转换成了 json,然后就可以在 remark42 中导入了。 中国有句俗话叫三搬一火,意思是搬三次家大约等于失一次火, 这次留言内容搬家我也丢掉了一些东西:Remark42 的一项设计理念便是尽量不保存可以追踪用户的数据,例如用户的 E-mail 在 Remark42 中只会保存一个与之对应的 SHA1,对于网站的主人来说, 这意味着他们不能直接从这些保存的数据中获得用户的 E-mail 地址(当然, 实际情况中,这类单向函数并不能阻止他们在知道这些信息的情况下验证某个 SHA1 是不是某个 E-mail 地址,但总归这要比把数据存在数据库里安全得多), 因此这个迁移过程也就意味着所有相关的明文数据消失了。除此之外,Movable Type 还保存了许多类似于用户网站地址这样的信息,我在转换时考虑了一下,由于许多人的网站都已经不在了, 迁移的意义不太大,因此最终决定不迁移这些数据了。

考虑到现在还在用 Movable Type 的人应该已经没有几个了, 我感觉我的方法可能对其他人没有太大的参考意义,这里只是简单做个记录。 代码写的比较乱,就不拿出来丢人了。

首先是把 MySQL 的数据给 dump 出来。比较需要关注的三个表是 mt_comment(留言内容)、 mt_fileinfo (由 Movable Type 管理的文件列表,用于计算网页的 URL), 以及 mt_entry (blog文章内容)。实际需要的是前两个。

第一个表(mt_comment)中我们关心的内容是:

  • comment_entry_id:留言相关的blog文章id。
  • comment_id:留言的唯一id。
  • comment_parent_id:留言回应的留言id。
  • comment_created_on:留言的创建时间。Movable Type的留言可以在事后修改,相关字段较多,我用的留言时间是这个字段的内容。
  • comment_text:留言内容
  • comment_author:留言作者自称的名字;如果是OpenID,则是OpenID。
  • comment_email:留言作者的电子邮件。最终它将被 SHA1 处理之后变成一个依然可以识别,但较难披露具体是谁的hash值。

对于没有留下电子邮件地址的留言者(这也包括使用了OpenID和其他不提供email的留言者), 用户身份将是 anonymous_*;如果用户留过电子邮件地址,则会生成一个 email_* 的识别码, 假如用户现时仍在使用该邮件地址,则系统可以将其对应出来。

第二个表(mt_fileinfo)中我们只关心两个字段:

  • fileinfo_entry_id:留言相关的blog文章id。
  • fileinfo_url:留言的网址中本地的部分。

一篇blog文章可能有多个不同的网址(对应不同的发布格式)。我在 hugo 中大致沿用了 Movable Type 时期的主 URL 格式: 年份/月份/文章名 ,这样可以很容易地用正则表达式表达出来。 当然由于做的是 SQL 查询,因此只要简单地用 LIKE "/20%.html" 也就足够了。

有了这些数据之后拼 json 比较简单,我的做法是把一条留言的数据塞进一个 dict,然后用 Python 的 json 直接 json.dumps(item, ensure_ascii=False)+'\n'。 我最开始犯了一些比较愚蠢的低级错误(例如生成 url 时用了旧的 URL,以及最开始某些地方和 schema 不一致),但这些简单重新导出一下即可。 将导出的 json 补到之前的备份 json 后面就可以了。

确认数据大致正常之后用这样一个脚本把目前文章里直接嵌入的历史留言删掉:

#!/bin/sh

# Remove comments

TARGET_LINE=$(grep -En '#### Archived.*Comment' $1 | cut -f1 -d:)
TARGET_LINE=$((${TARGET_LINE} - 3))

mv $1 $1.bak
head -n ${TARGET_LINE} $1.bak > $1
if grep -q '#### Archived trackback' $1.bak; then
	TRACKBACK_LINE=$(grep -En '#### Archived trackback' $1.bak | cut -f1 -d:)
	TRACKBACK_LINE=$((${TRACKBACK_LINE} - 2))
	tail -n +${TRACKBACK_LINE} $1.bak >> $1
fi

rm -f $1.bak

由于 Trackback 我还没有找到很好的替代方案, 因此暂时还先这么放在那吧。