会迁移的内容

下面的每一条都是工具的默认行为。如果 RT 中的某项数据在导入包里没有对应位置,它要么保存在记录的 extra 中,要么计入清单(manifest)的 not_carried_over(见不会迁移的内容)。记录标识(source_ref)直接采用 RT 自己的 id,因此再次导出同一个 RT 时,每条记录的标识都相同,之后的导入只会添加新增的内容。

队列

每个队列变成一个 项目,并启用工作项(Issue)追踪、工时记录和维基(Wiki)三个模块。

RT Specivo
Name 名称。标识符由名称转成小写得到,只保留字母、数字和 -(Old Projects 变成 old-projects);非拉丁字母的名称得到 queue-<id>。标识符相同时(如 IT Help 和 IT-Help),按队列 id 顺序依次追加 -2、-3……以保证唯一;文章类对应的项目(kb-<class name>)排在队列之后。Specivo 据此生成项目标识。
Description 描述
Disabled 已归档(status 9):只读且不再显眼,和 RT 中禁用的队列一样
生命周期、Correspond 和 Comment 地址、主题标签 extra
RT 的 Everyone 组在该队列或所有队列上的权限 extra(rt_everyone_rights)。项目保持私有(见不会迁移的内容)。

RT 内部的 ___Approvals 队列不会导出。--queue NAME(可重复)只导出指定的队列,可以按名称或 id 指定。

工单

每张工单按工单号顺序变成所属队列项目中的一个 工作项。Specivo 会自行为工作项编号(SUPPORT-1、SUPPORT-2……),所以 RT 的工单号保存在工作项的 extra 中(rt_ticket),正文中对它的引用则变成链接(见引用)。

RT Specivo
Subject 主题
Creator 作者
Owner 负责人。Nobody 表示无人负责。
Status 状态(见状态)
Priority 优先级(见优先级);数值保存在 extra 中
Due、Starts 截止日期、开始日期:按 RT 的时区(或 --timezone,或 UTC)取日期
Created、LastUpdated 创建时间、更新时间
Resolved 关闭时间(工单处于非活跃状态时)
TimeEstimated 预计工时(小时)
TimeLeft 剩余预估(小时)
TimeWorked 工时记录(见工时);总计保存在 extra 中
Told、Started、InitialPriority、FinalPriority、Type、SLA extra
Requestors 关注者,姓名另存于 extra(requestors)
Cc、AdminCc、自定义角色 关注者(角色中的组展开为其用户),每个角色的成员另存于 extra(roles)
Description(RT 6.0) 描述

描述 取第一条消息,即创建工单那条事务(transaction)的消息。RT 6.0 的工单可以有自己的描述,此时以它为描述,第一条消息则成为第一条日志条目。

不导出:状态为 deleted 的工单、提醒和审批,各自计数。工单的提醒保存在其 extra 中(reminders:主题、状态、所有者、截止),因为 Specivo 没有对应的位置。--limit N 让每个队列最多导出 N 张工单,便于试运行。

合并的工单

被合并的工单(RT 仍保留它,并用 EffectiveId 指向合并目标工单)不会单独成为工作项。它的消息会并入保留下来的那张工单:第一条消息、回复、评论、文件、已用工时和链接变更,分别成为保留工作项的日志条目、文件和工时记录,并在各自的 extra 中标记 merged_from(被合并工单的编号)。它自身的字段变更(状态、所有者、关注者、主题、自定义字段等)不会重放,因为那些变更改的是被合并的工单,而不是保留的工单;这些变更会计数。保留工作项的 extra 中列出了被合并的工单号(merged_tickets)。

被合并的工单只有在自身也会被导出时才会并入:它所在的队列在导出范围内,且它未被删除。来自其他队列的被合并工单不会并入保留工作项,以免一个队列只对自己人员可见的文字流入另一个队列的项目;它会计数。保留工单不在导入包中的被合并工单(保留工单属于未导出的队列、已删除、是提醒或审批,或被 --limit 截掉)同样如此。

对被合并工单编号的引用以及指向它的链接,都和在 RT 中一样指向保留工单:指向保留的工作项,或指向另一个导入包中的对应工作项;如果保留工单已删除、是提醒或审批,则不指向任何地方(文字原样保留)。

状态

RT 的状态按名称变成 Specivo 的状态。状态的分类来自列出它的生命周期:initial 对应 backlog,active 对应 active,inactive 对应 closed。RT 5.0 和 6.0 会把在 Web 界面中修改过的生命周期存入数据库,工具会读取它们;否则工具按 RT 的默认生命周期处理。不在任何已知生命周期中的状态(例如 RT 配置文件里定义的生命周期中的状态)按名称归类,导出结束时会报告,也可以用 --status-category-map STATUS=CATEGORY,... 指定分类(可选分类:backlog、active、done、closed)。

不会创建 deleted 状态:已删除的工单不导出。

优先级

RT 的优先级是数字。RT 5.0 和 6.0 按名称显示优先级(%PriorityAsString,按队列配置:默认 0 起为 Low,50 起为 Medium,100 起为 High),工具采用 RT 为每张工单显示的名称。RT 4.4 显示数字,RT 5.0 中被设置为显示数字的队列也一样:这些工单按同样的三档归类,所属档位记在 extra 中(priority_bucket)。任何队列用到的每个名称都成为一个 Specivo 优先级,从低到高排列;数值始终保存在 extra 中。

历史

工单的每条事务变成一条 日志条目,带有作者和时间。同一个人在同一秒内产生的多条事务(例如接手工单、打开并回复)合为一条,因为它们在 RT 中本就是一次操作。评论是例外:评论始终单独成一条,整条私有,这样同一秒内的变更或回复不会随之变成私有,评论也不会随它们变成公开。每条日志条目都在 extra 中保留 RT 事务 id。

RT 事务 日志条目
Correspond(回复) 公开备注:消息正文及其文件
Comment(评论) 私有备注(其文件默认不导出,见文件)
Status 状态变更,记录状态
Give、Take、Steal、Untake、Set Owner 负责人变更,记录用户
Set Queue 项目变更,记录项目
Set Priority 优先级变更,记录优先级(同一名称内的数值变化以数值保留,priority)
Set Subject、Due、Starts、TimeEstimated 主题、截止日期、开始日期、预计工时
Set Description(RT 6.0) 描述变更,Markdown 格式
设置其他字段(Type、InitialPriority、FinalPriority、SLA) 以字段名命名的变更,值按 RT 的记录保留
AddWatcher、DelWatcher、SetWatcher requestor、cc、admincc 或自定义角色的变更,记录人员或组
AddLink、DeleteLink 关联变更(blocked_by、blocks、relates、has_child),记录 #number;父级(MemberOf)记为父工作项变更
Merge(MergedInto) merged 关联变更,记录被合并的工单
CustomField 自定义字段变更,含旧值和新值
Set TimeWorked、TimeLeft;Told 不记录:已用工时作为工时记录迁移,而 Specivo 不保留剩余预估的历史

其余事务(RT 发出邮件的副本、提醒变更、转发、系统错误)只计数,不迁移。

消息

回复或评论的正文来自 RT 存储的消息,每个 MIME 部分一行记录:

  • 读者看到的正文是第一个没有文件名且未标记为附件的文本部分;之后的同类部分(例如邮件列表的页脚)追加在其后;
  • 对于 multipart/alternative,优先使用 HTML 部分以保留格式,否则使用纯文本;
  • 其他有内容的部分都成为工作项的 文件,作者和时间取自该事务;没有名称的部分命名为 part-<id>.<ext>。转发消息中的各部分也作为文件。HTML 通过 Content-ID(cid:)引用显示的图片同样作为文件,并用图片占位符在原位置显示。

HTML 会清理为只含 Specivo 能渲染的标签和属性(脚本、样式和事件处理器一律去掉),处理方式与 Redmine 迁移工具相同,然后转换为 Markdown。图片占位符只为消息自身的文件生成;消息中的任何内容都无法让占位符指向其他文件。纯文本 按原样保留:Markdown 会解释为格式的字符会被转义,换行保留。

Message-ID、发件人和收件人(From、To、Cc、Bcc,以及从 RT 发出的消息的 RT-Send-CC 和 RT-Send-BCC)和主题写入条目的 extra,不进入正文。

文本按所属部分声明的字符集解码;RT 存储邮件时会转为 UTF-8,所以先尝试 UTF-8。RT 编码过的内容(base64、quoted-printable)会解码。RT 转移到 外部存储 的内容,在用 --external-storage-path 指定其根目录后从 Disk 后端读取;见导出。

对其他工单的引用

正文中以下列任一形式提到工单时,会变成工作项占位符,Specivo 再把它替换为工作项的新编号:

  • RT 的主题标签,[rt.example.com #123];
  • RT #123、RT#123、RT 123(RT 须为大写);
  • ticket #123、ticket 123、Ticket #123、ticket#123。

单独的 #123 只有加 --link-hash-numbers 才会链接:在邮件里,#2 既可能是工单,也常常是列表序号或颜色值。代码、链接和 URL 中的文字不做处理,RT 中没有对应工单的编号也不处理。被单独导出到另一个导入包的其他队列的工单,变成保留原文的占位符({{issue:123|RT #123}}):两个导入包都加载后它就是链接,之前则显示原文。对已删除工单、提醒和审批的引用保持为文字。

文件

消息中的每个文件,以及 Image 或 Binary 类型自定义字段的每个值,都成为工作项的 附件。重复存储的内容在导入包中只存一份。附件在 extra 中保留 RT 事务(和 Content-ID);来自自定义字段的文件会注明它属于哪个字段。

评论中的文件 默认不导出,只计数。评论是私有备注,但 Specivo 的附件没有私有标记:能看到工作项的人都能看到附件。评论内嵌显示的图片同样不导出,备注中不显示图片。加 --include-comment-files 可以照样迁移这些文件,作为工作项的文件,所有能看到工作项的人都能看到。

工时

每条记录了工时(TimeTaken)的事务,变成工作项所属项目的一条 工时记录,活动为 Work;创建工单的事务也算在内(创建时填写的已用工时由 RT 记在这里)。小时数保留两位小数,分钟数存在 extra 中。RT 6.0 会记录做事的人和日期(TimeWorker、TimeWorkedDate),工时记录采用它们;更早的版本两者都不记录,工时记录就归事务创建者,日期取事务当天。负向修正(RT 中减少了已用工时)只计数,不迁移:Specivo 的工时记录只表示已用工时。

RT 链接 Specivo
MemberOf 子工作项的父工作项(第二个父级作为 relates(关联到)保留,并计数)
DependsOn blocks(阻塞):被依赖的工单阻塞另一张工单
RefersTo relates,每对工单一条
MergedInto 见合并的工单

RT 允许两张工单既是父子又互相引用;Specivo 不允许工作项与其父工作项(或任何上级)之间建立关联,所以这类关联不导出并计数:父子关系本身已经表达了这一点。

指向另一个队列工单(该队列单独导出到自己的导入包)的链接写成跨包引用:两个导入包都加载后 Specivo 会建立关联。指向非工单对象(URL、文章、资产)的链接保存在工作项的 extra 中(links)并计数。指向已删除工单、提醒和审批的链接计数。

RT 用 URI 标识工单(fsck.com-rt://<Organization>/ticket/<number>),并且可以链接到另一个 RT 的工单。当 RT 把链接存为本地(LocalBase、LocalTarget),或 URI 的主机名是本 RT 的 $Organization 时,这条链接就视为本 RT 两张工单之间的链接。数据库中有 $Organization 设置时工具直接读取,否则取 RT 存为本地的 URI 中的主机名。指向另一个 RT 工单的链接保存在工作项的 extra 中(links)并计数,历史中显示其 URI。如果工具无法确定 organization(没有设置,也没有本地链接),就只把 RT 存为本地的链接视为工单之间的链接。

人员

RT 中除 RT_System 和 Nobody 以外的每个 用户:

RT Specivo
Name 登录名
RealName(没有则用 Name) 显示名称
EmailAddress 邮箱
Lang 语言
特权(Privileged)且已启用 活跃
非特权用户(请求人、邮件发送者) 锁定:保留作者归属,但不能登录;--activate-unprivileged 可让他们变为活跃
已禁用 锁定
SuperUser 权限(直接拥有或通过组获得) 管理员(Specivo 只在使用 --import-admins 时才采用)
Organization、NickName、Timezone extra

密码不迁移:Specivo 无法使用 RT 的哈希,每个账户都要重新设置密码。

用户自定义组 变成用户组,包含其中的所有用户(直接成员和通过子组加入的成员)。RT 的 Privileged 组在获得项目成员资格时变成 RT privileged users 用户组(见成员资格)。RT 的其他内部组(Everyone、Unprivileged)和角色组在 Specivo 中不成为用户组。

只导出部分队列(--queue)时,只迁移内容中涉及的人员:其工单(以及合并进来的工单和它们的提醒)的创建者、所有者、请求人、Cc、AdminCc 和自定义角色成员,历史记录的作者和记录过工时的人,所有者和关注者变更中提到的人,其文章的作者,以及在其任一项目中获得成员资格的用户和用户组的成员。只有在其任一项目中获得成员资格的用户组才会迁移。完整导出则迁移所有用户和用户组。RT privileged users 只包含本次导出涉及的特权用户,而不是 RT 的全部特权用户。

成员资格与角色

RT 把权限授予用户、组和角色,范围是某个队列或全局所有队列。工具会算出每个用户和用户自定义组在每个导出队列上拥有的权限,并据此赋予相应的 Specivo 角色:

拥有的权限 角色
AdminQueue Manager(经理)
OwnTicket 或 ModifyTicket Developer(开发者)
ShowTicket,且无上述权限 Reporter(报告者)

授予队列 AdminCc 或 Cc 角色的权限,会落到该队列的 AdminCc 和 Cc 关注者身上。授予按工单计的角色(Owner、Requestor、工单自身的 Cc 和 AdminCc)以及 RT 的 Everyone 和 Unprivileged 组的权限没有指向具体的人,因此不产生成员资格;它们只计数。组的成员通过组持有该组的角色。

RT 的 Privileged 组

RT 的 Privileged 组包含所有特权用户,也就是相对于请求人的工作人员。授予它的队列权限或全局权限变成一个用户组 RT privileged users(source_ref 为 rt-privileged,RT 的组 id 存在 extra 中),成员是本次导出涉及的特权用户,包括已禁用的。在它拥有权限的每个导出队列上,该用户组按上表获得相应角色的成员资格:某队列上只有 ShowTicket 则为 Reporter,OwnTicket 为 Developer,AdminQueue 为 Manager。全局权限对每个队列都生效。

只有至少获得一个成员资格时才会写出该用户组。Privileged 组不对应任何角色的权限(SeeQueue、CreateTicket、ShowApprovalsTab 等)照旧计数。如果某个用户自己(或通过用户自定义组)也有成员资格,那么他在项目中同时持有这几个角色。

Specivo 创建这三个角色时不带任何权限,由管理员自行配置;每个角色的 extra 中列出了对应的 RT 权限。RT 的权限本身不迁移。

自定义字段

工单的自定义字段变成工作项自定义字段,应用到 RT 中启用了该字段的队列对应的项目;RT 全局启用的字段则应用到所有项目。Specivo 按 RT 中的名称显示每个字段;元数据键由名称生成(字母、数字和 _,非拉丁字母的名称则为 cf_<id>),可用 --cf-key-map NAME=KEY 覆盖。

RT 类型 Specivo
Select list,可选值为字段的值(以及工单中仍保留的任何值)
Freeform、Combobox、Autocomplete string;建议值存在 extra 中
Text、Wikitext text,原样保留
HTML(RT 6.0) text,转换为 Markdown
Date date(YYYY-MM-DD)
DateTime string,ISO 8601 格式的 UTC 时间
IPAddress、IPAddressRange string,按 RT 的显示形式(192.0.2.10、198.51.100.0-198.51.100.255)
Image、Binary 不建字段:每个值作为工作项的文件

允许多个值的字段为 multiple。每次值的变更都在历史中记为自定义字段变更。队列、用户、组、事务和资产的自定义字段连同其值一起计数。

文章

RT 的文章变成 维基页面,并保留每个修订版本:

  • 位置:每篇文章属于一个类(class)。RT 中只应用于一个导出队列的类,其文章放入该队列项目的维基。其他类(全局应用、应用于多个队列或未应用于任何队列)各自成为一个只有维基的独立项目,名为 Articles: 。只导出部分队列(--queue)时,只迁移这些队列的类。
  • 正文:文章自定义字段的值(默认只有一个字段 Content)。HTML 转换为 Markdown。有多个字段时,每个字段成为一节,以字段名为标题。摘要(summary)保存在页面的 extra 中。
  • 修订版本:RT 记录字段值的每次变更。工具从当前值反推出文章创建时的文本,再按每次变更正向重放;同一个人在同一秒内的变更合为一个修订版本。
  • 标题 取文章名称,同一维基内重名时追加文章编号以保证唯一。

已禁用的文章只计数,不迁移;主题(topic)以及文章与工单之间的链接也一样。