会迁移的内容¶
下面的每一条都是工具的默认行为。如果 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)以及文章与工单之间的链接也一样。