mcpField
T
typo3-workspaces

typo3-workspaces

github.com/dirnbauer开发3355 次安装13 周前更新
代码仓库

关于 typo3-workspaces

面向 TYPO3 v14 开发者的 Workspaces 技能,涵盖版本控制、覆盖机制、发布流程和文件处理限制。它强调使用 TYPO3 内置 API,并说明工作区风险及防护做法,尤其涉及文件与文件集合。

主要功能

  1. 01说明工作区版本控制与覆盖
  2. 02介绍发布流程及 API 用法
  3. 03记录 FAL 与文件集合限制
  4. 04识别文件机密性风险
  5. 05列出适用于工作区的文件实践

适用场景

  1. 01开发 TYPO3 v14 Workspaces 功能
  2. 02检查工作区版本行为
  3. 03避免未发布文件被访问
  4. 04选择更安全的文件集合模式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dirnbauer/webconsulting-skills --skill typo3-workspaces

安装后在 Claude Code 中输入/typo3-workspaces确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
04-datahandler-setup-script.md
05-making-extensions-workspace-compatible.md
06-migrating-queries-without-workspaces-to-with-workspaces.md
07-translations-with-workspaces.md
08-debugging-and-troubleshooting.md
09-testing-workspace-support.md
09a-workspace-diagnostics-and-repair-playbook.md
10-best-practices.md
11-psr-14-events-reference.md
credits-and-attribution.md
full-guide.md
related-skills.md
v14-only-changes.md

详细介绍

TYPO3 工作区

来源:https://github.com/dirnbauer/webconsulting-skills

**兼容性:**TYPO3 v14.x 此技能中的所有代码示例均适用于 TYPO3 v14。

**TYPO3 API 优先:**始终优先使用 TYPO3 内置 API、核心功能和既定约定,再创建自定义实现。不要重新实现 TYPO3 已提供的功能。使用前请查阅 TYPO3 官方文档,确认所用 API 和方法存在,且在 TYPO3 v14 中未被弃用。

来源

此技能基于 17 个权威来源:

  1. TYPO3 工作区扩展文档
  2. 版本控制(工作区扩展)
  3. 创建自定义工作区
  4. 配置选项(工作区)
  5. PSR-14 事件(工作区)
  6. 版本控制与工作区(TYPO3 详解 / 核心 API)
  7. TCA versioningWS 参考
  8. 限制构建器(TYPO3 详解 / Doctrine DBAL)
  9. b13 博客:TYPO3 覆盖机制的高效之道(Benni Mack)
  10. Scheduler 任务(工作区)
  11. 用户指南(工作区)
  12. TYPO3-CORE-SA-2025-022:工作区模块中的信息泄露
  13. 本地化内容指南
  14. 文件集合(TYPO3 详解)
  15. FAL 数据库架构
  16. Forge 错误 #60343:sys_file_metadata 无法识别工作区
  17. Forge 功能 #97923:sys_file_collection 的合并 folder_identifier 字段

1. 核心概念

什么是工作区?

工作区允许编辑人员准备内容更改,而不影响线上网站。更改会经过可配置的审核流程,然后才会发布。

工作区分为两种:

  • LIVE 工作区(ID=0):默认状态。每项更改都会立即生效。必须明确授予后端用户/用户组访问权限。
  • 自定义工作区(ID>0):安全的编辑环境。更改会进行版本控制、可供预览,并经过多个阶段后才会正式发布。
版本控制的工作原理(数据库层面)

离线(工作区)版本与实时记录存储在同一个数据库表中。它们通过以下字段识别:

字段用途
t3ver_oid指向实时记录的 uid(实时记录和工作区新建记录为 0)
t3ver_wsid此版本所属的工作区 ID(实时记录为 0)
t3ver_state特殊状态标记(见下文)
t3ver_stage工作流阶段(0=编辑中,-10=准备发布)
pid实际页面 ID(与实时记录的 pid 相同)

注意: 在 TYPO3 v10.4 之前,离线版本的 pid = -1。从 v10.4 起(Important #89555,“与工作区相关的数据库记录包含正确的页面 ID”),工作区记录会存储其实际 pid;v11 中的单独变更(Breaking #92497)移除了移动占位记录。如果你在旧代码或文档中遇到 pid = -1,说明内容已过时。

t3ver_state 值(TYPO3 v14):

值含义
0默认:对现有实时记录进行工作区修改
1仅在工作区中创建的新记录(没有对应的实时记录)
2删除占位记录(记录标记为在发布时删除)
4移动指针(记录将在发布时移动,并存储新的 pid/排序值)

v11 中已移除: t3ver_state = -1(旧的“新版本”对应项)、t3ver_state = 3(旧的“移动占位记录”)以及 t3ver_move_id 字段。如果你在旧代码中看到这些值,它们在 TYPO3 v14 中已不再使用。

覆盖机制

TYPO3 始终先获取实时记录,然后再叠加工作区版本。对于启用了工作区的翻译,处理链如下:

  1. 获取默认语言的实时记录(language=0,workspace=0)
  2. 叠加工作区版本(搜索 t3ver_oid=<uid> AND t3ver_wsid=<current_ws>)
  3. 叠加语言翻译(在目标语言中搜索 l10n_parent=<uid>)
  4. 叠加翻译的工作区版本

覆盖过程中始终保留实时记录的 uid,从而确保所有引用和链接保持完整。

发布工作流
  • 发布:草稿内容通过工作区发布流程替换实时内容。
  • TYPO3 v14 仍接受 DataHandler version 命令,使用 action => 'publish'(首选)或 action => 'swap'(别名,在 DataHandlerHook 中按与 publish 相同的方式处理)。工作区版本 UID 的参数键仍为 swapWith,而不是 uid。
  • 不要依赖 sys_workspace 上历史遗留的双向工作区“交换模式”(在 v11 中移除,#92206)——这与 cmdmaps 中的 swap 关键字是两回事。

2. 关键:文件/FAL 限制

文件(FAL)不会进行版本控制。 这是 TYPO3 Workspaces 最重要的限制。 另请参阅下文第 2a 节,其中介绍了额外的文件集合限制(基于文件夹的集合)。

这意味着什么
  • fileadmin/ 中的文件只存在于实时工作区
  • 如果你覆盖一个文件,此更改会立即影响所有工作区
  • sys_file_reference 记录支持工作区版本控制;物理文件和 sys_file 记录不会像内容覆盖那样进行版本控制
  • 替换现有引用中的文件,在工作区预览中仍可能遇到边缘情况;请针对你使用的确切 TYPO3 次版本进行测试,并尽可能使用新文件名,而不是原位替换。
可预测文件名带来的安全问题

场景: 编辑者创建某个页面的工作区版本,将 geschaeftsbericht2024.pdf 替换为 geschaeftsbericht2025.pdf。工作区尚未发布。但:

  1. 新 PDF 会立即上传到 fileadmin/(文件处于实时状态!)
  2. 外部人员猜测 URL,只需将年份加 1
  3. 在引用该文件的内容元素发布之前,geschaeftsbericht2025.pdf 就已可访问

这会带来真实的安全/保密风险。

变通方案

1. 使用难以猜测的文件名:

# Instead of:
fileadmin/reports/geschaeftsbericht2025.pdf

# Use hashed/random names:
fileadmin/reports/gb-a8f3e2b1c9d4.pdf

2. 将机密文件存储在 Web 根目录之外:

// config/system/settings.php
// Use a private storage that is NOT publicly accessible
// Deliver files programmatically via a controller

3. 使用 EXT:secure_downloads (leuchtfeuer/secure-downloads):

通过 PHP 脚本交付文件,该脚本会检查访问权限。无法直接通过文件 URL 访问。

4. 在服务器级别保护敏感目录:

# Apache (.htaccess in a subdirectory)
<IfModule mod_authz_core.c>
    Require all denied
</IfModule>
# NGINX
location /fileadmin/confidential/ {
    deny all;
    return 403;
}

5. 为每个工作区版本使用单独的文件引用:

使用不同的名称上传新文件。切勿覆盖现有文件。内容元素的工作区版本引用新文件,线上版本仍保留对旧文件的引用。发布后,两个文件都存在,但只有新文件会被引用。

工作区安全的文件处理规则
  • 切勿覆盖在多个工作区的内容元素中使用的文件
  • 始终为工作区内容上传名称唯一的新文件
  • 切勿依赖“页面尚未发布”来保护文件机密性
  • 对任何机密文档,考虑使用 EXT:secure_downloads
  • 审查 fileadmin/ 中是否存在命名模式容易预测的文件

2a. 文件集合与工作区

文件集合(sys_file_collection)在记录级别进行工作区版本控制,但基于文件夹的集合会在运行时从线上文件系统解析文件——不存在可用于覆盖的数据库关联。

文件集合的工作方式

sys_file_collection 存储三种集合类型,由 type 字段区分:

type 值解析机制涉及的数据库关联
static (0)sys_file_reference 行(tablenames='sys_file_collection',fieldname='files')每个文件对应一行 sys_file_reference
folder (1)folder_identifier 字符串(例如 1:/user_upload/gallery/)无——没有联结表,也没有引用行
category (2)通过 sys_category_record_mm 解析 category uidMM 表(通过父记录覆盖处理)

sys_file_collection 的 TCA 设置了 versioningWS => true。这意味着集合记录本身会创建带有 t3ver_* 字段的工作区版本。不过,记录级版本控制只会对存储在 sys_file_collection 中的字段进行版本控制,不会对集合解析出的文件进行版本控制。

各集合类型的工作区安全性
集合类型是否对记录进行版本控制?是否对文件绑定进行版本控制?是否对物理文件进行版本控制?是否适用于工作区?
Static是是(sys_file_reference 的 versioningWS = true)否部分适用
基于文件夹是(仅记录)否——不存在数据库绑定否不适用
基于分类是部分适用(通过父记录覆盖处理分类 MM)否部分适用
基于文件夹的集合为何会破坏工作区隔离

对于基于文件夹的集合,集合与文件之间的“关联”不是数据库关联。集合记录只存储 folder_identifier 字符串。调用 FolderBasedFileCollection::loadContents() 时,TYPO3 会调用 $storage->getFilesInFolder(),直接从线上存储驱动列出文件。集合与文件之间不存在可支持覆盖的数据库行。

后果:

  • 向文件夹添加文件后,它会立即在所有工作区中可见
  • 从文件夹中移除文件后,它会立即从所有工作区中移除
  • 更改工作区版本中的 folder_identifier 字段会指向另一个文件夹,但文件夹内容始终来自线上
  • 如果创建工作区版本后文件夹内容发生变化,使用基于文件夹的集合的“文件链接”内容元素(CType=uploads)在工作区预览中显示的文件可能与预期不同
静态集合更安全

静态集合(type=static)通过 sys_file_reference 行绑定文件。sys_file_collection 和 sys_file_reference 的 versioningWS 均为 true。在工作区中向静态集合添加或移除文件时,TYPO3 会为 sys_file_reference 行创建工作区版本。集合与文件之间的绑定会进行版本控制。

剩余的限制是:物理文件(sys_file)仍不进行版本控制(参见第 2 节)。如果覆盖文件,所有位置都会受到影响。

基于文件夹的集合的变通方法

1. 在需要工作区隔离时优先使用静态集合:

使用 type=static,而不是 type=folder。文件与集合之间的绑定是一个会进行工作区版本控制的 sys_file_reference 行。

2. 为每个工作区版本使用单独的文件夹:

对于基于文件夹的集合,为工作区更改创建一个新文件夹(例如 gallery-v2/),并在集合记录的工作区版本中更改 folder_identifier。线上版本仍指向原始文件夹。

3. 工作区草稿引用共享文件夹时,切勿修改文件夹内容:

如果工作区内容使用了基于文件夹的集合,在工作区发布之前,避免向该文件夹添加或移除文件。

4. 发布后清理旧文件夹:

使用 AfterRecordPublishedEvent 在工作区版本发布后移除过时的文件夹:

<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\EventListener;

use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Resource\StorageRepository;
use TYPO3\CMS\Workspaces\Event\AfterRecordPublishedEvent;

#[AsEventListener]
final class CleanupOldCollectionFolderListener
{
    public function __construct(
        private readonly StorageRepository $storageRepository,
    ) {}

    public function __invoke(AfterRecordPublishedEvent $event): void
    {
        if ($event->getTable() !== 'sys_file_collection') {
            return;
        }

        // After publishing a folder-based collection, remove the old folder
        // if it is no longer referenced by any collection.
        // Implementation depends on your naming convention (e.g. gallery-v1/, gallery-v2/).
    }
}
工作区安全的文件集合规则
  • 优先使用静态集合(type=static),而不是基于文件夹的集合,尤其是在使用工作区时
  • 切勿在未发布的工作区中,当基于文件夹的集合引用某个文件夹时,向该文件夹添加或移除文件
  • 如果必须使用基于文件夹的集合,为每个工作区版本使用单独的文件夹
  • 向编辑人员说明:无论工作区状态如何,基于文件夹的集合都会显示线上文件夹中的内容
  • 在依赖文件集合开展编辑工作流之前,先在工作区预览中测试其行为

3. 安装与设置清单

安装
# Composer (TYPO3 v14)
composer require typo3/cms-workspaces

# Non-Composer: activate in Admin Tools > Extensions
完整设置清单

使用此清单确认工作区设置已完成:

扩展与系统
  • 已安装并启用 typo3/cms-workspaces
  • 数据库架构已更新(运行 bin/typo3 extension:setup,或使用项目中等效的架构更新封装命令)
  • 已安装 typo3/cms-scheduler(自动发布所必需)
  • 已在服务端配置调度器 cron 作业
后端用户组
  • 已创建工作区专用的后端用户组(例如“工作区编辑人员”)
  • 用户组拥有明确的 LIVE 工作区访问权限(“挂载与工作区”选项卡 > 勾选“Live”)
  • 用户组有权访问自定义工作区(在工作区记录中指定为所有者或成员)
  • DB 挂载设置正确(用户组可编辑的页面)
  • 文件挂载设置正确(用户组可访问的目录)
  • 用户组对所有相关表具有 tables_modify 权限
  • 用户组对工作区将包含的页面类型具有相应权限
后端用户
  • 用户已分配到工作区用户组
  • 用户可以在后端侧边栏中看到工作区选择器(v14.2+;更早的 v14 版本可能仍在顶部栏显示)
  • 仅在非管理员用户需要 LIVE 工作区访问权限时,才为其授予该权限
工作区记录
  • 已在根页面(pid=0)上将工作区记录创建为系统记录
  • 已设置标题和描述
  • 已指定所有者(使用用户组,而非单个用户)
  • 已指定成员(使用用户组,而非单个用户)
  • 如有需要,已创建自定义阶段(位于“编辑中”与“准备发布”之间)
  • 已按阶段配置通知设置
  • 如果工作区应限制为特定页面树,已设置挂载点
  • 如果需要自动发布,已设置发布日期
  • 已配置“发布权限”(如有需要,可将发布权限限制为所有者)
表(TCA)
  • 所有自定义扩展表都在 $GLOBALS['TCA'][<table>]['ctrl'] 中设置了 'versioningWS' => true
  • 不应进行版本控制的表设置了 'versioningWS' => false
  • 需要在工作区中实时编辑的表设置了 'versioningWS_alwaysAllowLiveEdit' => true
  • 数据库中存在 t3ver_* 列(在 versioningWS = true 时自动创建)

Inline / IRRE 子表(弃用 #106821): 如果子表缺少 versioningWS,Core 会记录弃用警告,并可能在构建 TCA 时自动添加此标志——不会抛出硬错误。请在 TCA 中显式设置此标志;它将在 v15 中成为硬性要求。

Scheduler 任务
  • 已创建并启用“工作区自动发布”任务
  • 已创建并启用“工作区预览链接清理”任务
  • Cron 频率适当(例如,每 15 分钟一次)
TSconfig
  • 已设置 options.workspaces.previewLinkTTLHours(默认值:48)
  • 如需限制语言,已设置 options.workspaces.allowed_languages.<wsId>
  • 如需限制预览模式,已配置 workspaces.splitPreviewModes
  • 已为自定义记录预览页面设置 options.workspaces.previewPageId

详细参考

如果任务需要详细示例、较长的模板、故障排查矩阵、附录或上文未包含的部分,请阅读完整指南。对于范围较窄的任务,请勿加载此文件,以便该技能采用渐进式披露。

评论

支持 Markdown