EmDash 将条目的已发布版本与未发布的更改分开保存。管理面板、 REST API、命令行界面(CLI)以及 Model Context Protocol(MCP)工具使用相同的生命周期 规则。
条目可以是 draft、scheduled 或 published。已发布的条目也可以有草稿和未来的
计划:在计划的草稿发布之前,访客会继续收到线上修订。
回收站与状态是分开的。已移入回收站的条目保留其生命周期元数据,但会
从普通内容读取中排除。
状态转换
下表是内容状态、修订指针与发布时间戳的规范契约。 “无更改”表示操作会保留已存储的值。
| 操作 | 起始状态 | 结果 | 修订效果 | publishedAt | scheduledAt | 重复调用 |
|---|---|---|---|---|---|---|
| 保存更改 | 任意活动条目 | 状态不变 | 在支持修订的集合上,替换草稿修订,同时线上修订保持公开 | 无更改 | 无更改 | 提供的 _rev 会拒绝过期保存;省略则使 REST 写入无条件 |
| 发布 | 草稿、已计划或已发布 | 已发布 | 将草稿修订提升为线上并清除草稿指针 | 首次发布时设置;后续发布保留,除非经授权的调用方覆盖 | 清除 | 保留线上内容与发布时间,但返回新的 _rev |
| 到期时发布 | 已计划,或已发布且有计划草稿 | 已发布 | 与发布相同 | 首次发布使用计划时间;在线上内容上发布新草稿时保留现有值 | 清除 | 后续调度器回合会跳过计划已清除的条目 |
| 取消发布 | 任意活动条目 | 草稿 | 清除线上指针;保留现有草稿或从线上修订创建草稿 | 保留 | 清除 | 已是普通草稿的条目不变 |
| 计划 | 草稿、已计划或已发布 | 草稿变为已计划;已发布条目保持已发布 | 无更改 | 无更改 | 设为请求的未来时间 | 替换现有计划并返回新的 _rev |
| 取消计划 | 已计划,或已发布且有计划 | 已计划条目变为草稿;已发布条目保持已发布 | 无更改 | 无更改 | 清除 | 没有计划的条目不变 |
| 丢弃草稿 | 任意活动条目 | 状态不变 | 清除草稿指针;线上修订保持不变 | 无更改 | 无更改 | 没有草稿的条目不变 |
| 移入回收站 | 任意活动条目 | 在回收站中且不出现在普通读取中 | 保留 | 保留 | 保留 | 对已在回收站中的条目的请求返回未找到 |
| 从回收站恢复 | 在回收站中 | 草稿 | 清除线上指针;保留任何草稿指针 | 保留 | 清除 | 需要仍在回收站中的条目 |
| 永久删除 | 在回收站中 | 已移除 | 移除条目及其修订 | 已移除 | 已移除 | 无法重复或撤销 |
| 恢复修订 | 任意活动条目 | 状态不变 | 在支持修订的集合上,用所选修订的副本替换草稿;线上修订保持不变 | 无更改 | 无更改 | 创建新修订并返回新的 _rev |
在不支持修订的集合上,保存与发布使用内容行,而不是线上与草稿指针。 恢复修订会将所选字段值直接写入该行。
在支持修订的集合上恢复修订不会发布它。在审阅恢复的草稿后 再发布该条目。
权限与写入保护
权限取决于所有权。Author 可对其拥有的条目执行操作;Editor 可对任意条目执行相同 操作。永久删除需要 Admin。
在发布期间设置 publishedAt 需要 content:publish_any,即使调用方拥有该
条目。
REST API 在所示位置接受 _rev 作为可选的乐观并发前置条件。MCP 工具
对相同操作要求它,因此代理必须在更改前读取条目。过期令牌
返回 CONFLICT。受条目锁保护的管理后台与 REST 写入会返回 ENTRY_LOCKED,除非经授权的
请求使用支持的锁覆盖。MCP 写入不参与条目锁。
| 操作 | 权限 | REST _rev | MCP _rev | 管理后台与 REST 锁 | 钩子 |
|---|---|---|---|---|---|
| 保存更改 | content:edit_own 或 content:edit_any | 可选 | 必需 | 强制 | content:beforeSave、content:afterSave |
| 发布 | content:publish_own 或 content:publish_any | 可选 | 必需 | 强制 | content:beforePublish、content:afterPublish |
| 取消发布 | content:publish_own 或 content:publish_any | 可选 | 必需 | 强制 | content:beforeUnpublish、content:afterUnpublish |
| 计划 | content:publish_own 或 content:publish_any | 可选 | 必需 | 强制 | content:beforeSchedule、content:afterSchedule |
| 取消计划 | content:publish_own 或 content:publish_any | 不接受 | 不接受 | 强制 | content:afterUnschedule |
| 丢弃草稿 | content:edit_own 或 content:edit_any | 可选 | 必需 | 强制 | 无 |
| 移入回收站 | content:delete_own 或 content:delete_any | 不接受 | 不接受 | 强制 | content:beforeDelete、content:afterDelete |
| 从回收站恢复 | content:edit_own 或 content:edit_any | 不接受 | 不接受 | 不强制 | content:afterRestore |
| 永久删除 | content:delete_permanent | 不接受 | 不接受 | 不强制 | content:afterDelete |
| 恢复修订 | content:edit_own 或 content:edit_any | 不接受 | 不接受 | 不强制 | 无 |
content:afterDelete 事件在条目移入回收站时将 permanent 设为 false,在永久删除后
设为 true。成功的 after 钩子在状态更改后运行,并可能在响应已发送后
运行。插件可从相应的 before 钩子拒绝保存、发布、取消发布、计划或移入回收站。
当计划发布到达到期时间时,它使用发布钩子。如果
content:beforePublish 钩子拒绝该计划尝试,EmDash 会清除计划并运行
content:afterUnschedule。
冲突与重试
对下一次受保护操作使用每次读取或写入返回的 _rev。EmDash 会拒绝过期令牌,
而不是替换并发更改。再次读取条目,审阅更新的状态,然后决定是否重试。
错误响应并不总是证明没有任何状态更改。如果连接在客户端收到响应之前 结束,请在重试生命周期操作之前读取条目。这也能防止 重试替换另一位编辑者已完成的工作。
修订恢复会一并提交恢复的内容及其审计修订。若任一写入 失败,EmDash 会保留请求之前的内容与修订历史。
HTTP 请求与响应架构见 REST API 参考,工具输入见 MCP 服务器参考,事件负载见 钩子 参考。