typo3-content-blocks
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTYPO3 Content Blocks Development
TYPO3 Content Blocks 开发
Compatibility: TYPO3 v13.x and v14.x (v14 preferred) All code examples in this skill are designed to work on both TYPO3 v13 and v14.
TYPO3 API First: Always use TYPO3's built-in APIs, core features, and established conventions before creating custom implementations. Do not reinvent what TYPO3 already provides. Always verify that the APIs and methods you use exist and are not deprecated in your target TYPO3 version (v13 or v14) by checking the official TYPO3 documentation.
兼容性: TYPO3 v13.x 和 v14.x(推荐v14) 本技能中的所有代码示例均适用于TYPO3 v13和v14版本。
TYPO3 API优先原则: 在创建自定义实现之前,始终使用TYPO3的内置API、核心功能和既定约定。不要重复造轮子,TYPO3已提供的功能无需重新开发。在使用API和方法前,请务必通过官方TYPO3文档确认它们在目标版本(v13或v14)中存在且未被弃用。
1. The Single Source of Truth Principle
1. 唯一可信来源原则
Content Blocks is the modern approach to creating custom content types in TYPO3. It eliminates redundancy by providing a single YAML configuration that generates:
- TCA (Table Configuration Array)
- Database schema (SQL)
- TypoScript rendering
- Backend forms and previews
- Labels and translations
Content Blocks是TYPO3中创建自定义内容类型的现代化方案。它通过单一YAML配置消除冗余,自动生成:
- TCA(表配置数组)
- 数据库架构(SQL)
- TypoScript渲染配置
- 后端表单与预览
- 标签与翻译
Why Content Blocks?
为什么选择Content Blocks?
| Traditional Approach | Content Blocks Approach |
|---|---|
| Multiple TCA files | One |
| Manual SQL definitions | Auto-generated schema |
| Separate TypoScript | Auto-registered rendering |
| Scattered translations | Single |
| Complex setup | Simple folder structure |
| 传统方式 | Content Blocks方式 |
|---|---|
| 多个TCA文件 | 单个 |
| 手动编写SQL定义 | 自动生成数据库架构 |
| 独立的TypoScript配置 | 自动注册渲染规则 |
| 分散的翻译文件 | 单个 |
| 复杂的设置流程 | 简洁的文件夹结构 |
2. Installation
2. 安装
bash
undefinedbash
undefinedInstall via Composer (DDEV recommended)
通过Composer安装(推荐使用DDEV)
ddev composer require friendsoftypo3/content-blocks
ddev composer require friendsoftypo3/content-blocks
After installation, clear caches
安装完成后,清除缓存
ddev typo3 cache:flush
undefinedddev typo3 cache:flush
undefinedSecurity Configuration (Classic Mode)
安全配置(经典模式)
For non-composer installations, deny web access to ContentBlocks folder:
apache
undefined对于非Composer安装的项目,需禁止Web访问ContentBlocks文件夹:
apache
undefined.htaccess addition
.htaccess中添加以下规则
RewriteRule (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|ContentBlocks|Resources/Private|Tests?|Documentation|docs?)/ - [F]
undefinedRewriteRule (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|ContentBlocks|Resources/Private|Tests?|Documentation|docs?)/ - [F]
undefined3. Content Types Overview
3. 内容类型概述
Content Blocks supports four content types:
| Type | Folder | Table | Use Case |
|---|---|---|---|
| | | Frontend content (hero, accordion, CTA) |
| | Custom/existing | Structured records (news, products, team) |
| | | Custom page types (blog, landing page) |
| | | Extended file metadata (photographer, copyright) |
Content Blocks支持四种内容类型:
| 类型 | 文件夹 | 数据表 | 使用场景 |
|---|---|---|---|
| | | 前端内容(Hero横幅、折叠面板、CTA按钮等) |
| | 自定义/现有表 | 结构化记录(新闻、产品、团队成员等) |
| | | 自定义页面类型(博客、落地页等) |
| | | 扩展文件元数据(摄影师信息、版权声明等) |
4. Folder Structure
4. 文件夹结构
EXT:my_sitepackage/
└── ContentBlocks/
├── ContentElements/
│ └── my-hero/
│ ├── assets/
│ │ └── icon.svg
│ ├── language/
│ │ └── labels.xlf
│ ├── templates/
│ │ ├── backend-preview.html
│ │ ├── frontend.html
│ │ └── partials/
│ └── config.yaml
├── RecordTypes/
│ └── my-record/
│ ├── assets/
│ │ └── icon.svg
│ ├── language/
│ │ └── labels.xlf
│ └── config.yaml
├── PageTypes/
│ └── blog-article/
│ ├── assets/
│ │ ├── icon.svg
│ │ ├── icon-hide-in-menu.svg
│ │ └── icon-root.svg
│ ├── language/
│ │ └── labels.xlf
│ ├── templates/
│ │ └── backend-preview.html
│ └── config.yaml
└── FileTypes/
└── image-extended/
├── language/
│ └── labels.xlf
└── config.yamlEXT:my_sitepackage/
└── ContentBlocks/
├── ContentElements/
│ └── my-hero/
│ ├── assets/
│ │ └── icon.svg
│ ├── language/
│ │ └── labels.xlf
│ ├── templates/
│ │ ├── backend-preview.html
│ │ ├── frontend.html
│ │ └── partials/
│ └── config.yaml
├── RecordTypes/
│ └── my-record/
│ ├── assets/
│ │ └── icon.svg
│ ├── language/
│ │ └── labels.xlf
│ └── config.yaml
├── PageTypes/
│ └── blog-article/
│ ├── assets/
│ │ ├── icon.svg
│ │ ├── icon-hide-in-menu.svg
│ │ └── icon-root.svg
│ ├── language/
│ │ └── labels.xlf
│ ├── templates/
│ │ └── backend-preview.html
│ └── config.yaml
└── FileTypes/
└── image-extended/
├── language/
│ └── labels.xlf
└── config.yaml5. Creating Content Elements
5. 创建内容元素
Kickstart Command (Recommended)
快速启动命令(推荐)
bash
undefinedbash
undefinedInteractive mode
交互式模式
ddev typo3 make:content-block
ddev typo3 make:content-block
One-liner
单行命令
ddev typo3 make:content-block
--content-type="content-element"
--vendor="myvendor"
--name="hero-banner"
--title="Hero Banner"
--extension="my_sitepackage"
--content-type="content-element"
--vendor="myvendor"
--name="hero-banner"
--title="Hero Banner"
--extension="my_sitepackage"
ddev typo3 make:content-block
--content-type="content-element"
--vendor="myvendor"
--name="hero-banner"
--title="Hero Banner"
--extension="my_sitepackage"
--content-type="content-element"
--vendor="myvendor"
--name="hero-banner"
--title="Hero Banner"
--extension="my_sitepackage"
After creation, update database
创建完成后,更新数据库
ddev typo3 cache:flush -g system
ddev typo3 extension:setup --extension=my_sitepackage
undefinedddev typo3 cache:flush -g system
ddev typo3 extension:setup --extension=my_sitepackage
undefinedMinimal Content Element
最简内容元素配置
yaml
undefinedyaml
undefinedEXT:my_sitepackage/ContentBlocks/ContentElements/hero-banner/config.yaml
EXT:my_sitepackage/ContentBlocks/ContentElements/hero-banner/config.yaml
name: myvendor/hero-banner
fields:
- identifier: header useExistingField: true
- identifier: bodytext useExistingField: true
undefinedname: myvendor/hero-banner
fields:
- identifier: header useExistingField: true
- identifier: bodytext useExistingField: true
undefinedFull Content Element Example
完整内容元素示例
yaml
undefinedyaml
undefinedEXT:my_sitepackage/ContentBlocks/ContentElements/hero-banner/config.yaml
EXT:my_sitepackage/ContentBlocks/ContentElements/hero-banner/config.yaml
name: myvendor/hero-banner
group: default
description: "A full-width hero banner with image and CTA"
prefixFields: true
prefixType: full
basics:
- TYPO3/Appearance
- TYPO3/Links fields:
- identifier: header useExistingField: true
- identifier: subheadline type: Text label: Subheadline
- identifier: hero_image type: File minitems: 1 maxitems: 1 allowed: common-image-types
- identifier: cta_link type: Link label: Call to Action Link
- identifier: cta_text type: Text label: Button Text
undefinedname: myvendor/hero-banner
group: default
description: "带图片和CTA按钮的全宽Hero横幅"
prefixFields: true
prefixType: full
basics:
- TYPO3/Appearance
- TYPO3/Links fields:
- identifier: header useExistingField: true
- identifier: subheadline type: Text label: 副标题
- identifier: hero_image type: File minitems: 1 maxitems: 1 allowed: common-image-types
- identifier: cta_link type: Link label: 调用链接
- identifier: cta_text type: Text label: 按钮文本
undefinedFrontend Template
前端模板
html
<!-- templates/frontend.html -->
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:cb="http://typo3.org/ns/TYPO3/CMS/ContentBlocks/ViewHelpers"
data-namespace-typo3-fluid="true">
<f:asset.css identifier="hero-banner-css" href="{cb:assetPath()}/frontend.css"/>
<section class="hero-banner">
<f:if condition="{data.hero_image}">
<f:for each="{data.hero_image}" as="image">
<f:image image="{image}" alt="{data.header}" class="hero-image"/>
</f:for>
</f:if>
<div class="hero-content">
<h1>{data.header}</h1>
<f:if condition="{data.subheadline}">
<p class="subheadline">{data.subheadline}</p>
</f:if>
<f:if condition="{data.cta_link}">
<f:link.typolink parameter="{data.cta_link}" class="btn btn-primary">
{data.cta_text -> f:or(default: 'Learn more')}
</f:link.typolink>
</f:if>
</div>
</section>
</html>html
<!-- templates/frontend.html -->
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:cb="http://typo3.org/ns/TYPO3/CMS/ContentBlocks/ViewHelpers"
data-namespace-typo3-fluid="true">
<f:asset.css identifier="hero-banner-css" href="{cb:assetPath()}/frontend.css"/>
<section class="hero-banner">
<f:if condition="{data.hero_image}">
<f:for each="{data.hero_image}" as="image">
<f:image image="{image}" alt="{data.header}" class="hero-image"/>
</f:for>
</f:if>
<div class="hero-content">
<h1>{data.header}</h1>
<f:if condition="{data.subheadline}">
<p class="subheadline">{data.subheadline}</p>
</f:if>
<f:if condition="{data.cta_link}">
<f:link.typolink parameter="{data.cta_link}" class="btn btn-primary">
{data.cta_text -> f:or(default: '了解更多')}
</f:link.typolink>
</f:if>
</div>
</section>
</html>Backend Preview Template
后端预览模板
html
<!-- templates/backend-preview.html -->
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:be="http://typo3.org/ns/TYPO3/CMS/Backend/ViewHelpers"
data-namespace-typo3-fluid="true">
<div class="content-block-preview">
<strong>{data.header}</strong>
<f:if condition="{data.subheadline}">
<br/><em>{data.subheadline}</em>
</f:if>
<f:if condition="{data.hero_image}">
<f:for each="{data.hero_image}" as="image">
<be:thumbnail image="{image}" width="100" height="100"/>
</f:for>
</f:if>
</div>
</html>html
<!-- templates/backend-preview.html -->
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:be="http://typo3.org/ns/TYPO3/CMS/Backend/ViewHelpers"
data-namespace-typo3-fluid="true">
<div class="content-block-preview">
<strong>{data.header}</strong>
<f:if condition="{data.subheadline}">
<br/><em>{data.subheadline}</em>
</f:if>
<f:if condition="{data.hero_image}">
<f:for each="{data.hero_image}" as="image">
<be:thumbnail image="{image}" width="100" height="100"/>
</f:for>
</f:if>
</div>
</html>6. Creating Record Types (Custom Tables)
6. 创建记录类型(自定义数据表)
Record Types create custom database tables for structured data like teams, products, events, etc.
记录类型用于为结构化数据(如团队成员、产品、活动等)创建自定义数据库表。
Extbase-Compatible Table Naming
兼容Extbase的表命名规则
IMPORTANT: For Extbase compatibility, use the naming convention:
tx_extensionkey_domain_model_*yaml
undefined重要提示: 为了兼容Extbase,请使用命名约定:
tx_extensionkey_domain_model_*yaml
undefined✅ CORRECT - Extbase compatible table name
✅ 正确 - 兼容Extbase的表名
name: myvendor/team-member
table: tx_mysitepackage_domain_model_teammember
labelField: name
fields:
- identifier: name type: Text
- identifier: position type: Text
- identifier: email type: Email
- identifier: photo type: File allowed: common-image-types maxitems: 1
```yamlname: myvendor/team-member
table: tx_mysitepackage_domain_model_teammember
labelField: name
fields:
- identifier: name type: Text
- identifier: position type: Text
- identifier: email type: Email
- identifier: photo type: File allowed: common-image-types maxitems: 1
```yaml❌ WRONG - Short table names don't work with Extbase
❌ 错误 - 短表名无法兼容Extbase
name: myvendor/team-member
table: team_member # Won't work with Extbase!
undefinedname: myvendor/team-member
table: team_member # 无法与Extbase兼容!
undefinedMinimal Record Type
最简记录类型配置
yaml
undefinedyaml
undefinedEXT:my_sitepackage/ContentBlocks/RecordTypes/team-member/config.yaml
EXT:my_sitepackage/ContentBlocks/RecordTypes/team-member/config.yaml
name: myvendor/team-member
table: tx_mysitepackage_domain_model_teammember
labelField: name
fields:
- identifier: name type: Text
undefinedname: myvendor/team-member
table: tx_mysitepackage_domain_model_teammember
labelField: name
fields:
- identifier: name type: Text
undefinedFull Record Type Example
完整记录类型示例
yaml
undefinedyaml
undefinedEXT:my_sitepackage/ContentBlocks/RecordTypes/team-member/config.yaml
EXT:my_sitepackage/ContentBlocks/RecordTypes/team-member/config.yaml
name: myvendor/team-member
table: tx_mysitepackage_domain_model_teammember
labelField: name
fallbackLabelFields:
- email languageAware: true workspaceAware: true sortable: true softDelete: true trackCreationDate: true trackUpdateDate: true internalDescription: true restriction: disabled: true startTime: true endTime: true security: ignorePageTypeRestriction: true # Allow on normal pages fields:
- identifier: name type: Text required: true
- identifier: position type: Text
- identifier: email type: Email
- identifier: phone type: Text
- identifier: bio type: Textarea enableRichtext: true
- identifier: photo type: File allowed: common-image-types maxitems: 1
- identifier: social_links
type: Collection
labelField: platform
fields:
- identifier: platform
type: Select
items:
- label: LinkedIn value: linkedin
- label: Twitter/X value: twitter
- label: GitHub value: github
- identifier: url type: Link
- identifier: platform
type: Select
items:
undefinedname: myvendor/team-member
table: tx_mysitepackage_domain_model_teammember
labelField: name
fallbackLabelFields:
- email languageAware: true workspaceAware: true sortable: true softDelete: true trackCreationDate: true trackUpdateDate: true internalDescription: true restriction: disabled: true startTime: true endTime: true security: ignorePageTypeRestriction: true # 允许在普通页面使用 fields:
- identifier: name type: Text required: true
- identifier: position type: Text
- identifier: email type: Email
- identifier: phone type: Text
- identifier: bio type: Textarea enableRichtext: true
- identifier: photo type: File allowed: common-image-types maxitems: 1
- identifier: social_links
type: Collection
labelField: platform
fields:
- identifier: platform
type: Select
items:
- label: LinkedIn value: linkedin
- label: Twitter/X value: twitter
- label: GitHub value: github
- identifier: url type: Link
- identifier: platform
type: Select
items:
undefinedMulti-Type Records (Single Table Inheritance)
多类型记录(单表继承)
Create multiple types for one table:
yaml
undefined为单个表创建多种类型:
yaml
undefinedEXT:my_sitepackage/ContentBlocks/RecordTypes/person-employee/config.yaml
EXT:my_sitepackage/ContentBlocks/RecordTypes/person-employee/config.yaml
name: myvendor/person-employee
table: tx_mysitepackage_domain_model_person
typeField: person_type
typeName: employee
priority: 999 # Default type (loaded first)
labelField: name
languageAware: false
workspaceAware: false
fields:
- identifier: name type: Text
- identifier: department type: Text
```yamlname: myvendor/person-employee
table: tx_mysitepackage_domain_model_person
typeField: person_type
typeName: employee
priority: 999 # 默认类型(优先加载)
labelField: name
languageAware: false
workspaceAware: false
fields:
- identifier: name type: Text
- identifier: department type: Text
```yamlEXT:my_sitepackage/ContentBlocks/RecordTypes/person-contractor/config.yaml
EXT:my_sitepackage/ContentBlocks/RecordTypes/person-contractor/config.yaml
name: myvendor/person-contractor
table: tx_mysitepackage_domain_model_person
typeName: contractor
fields:
- identifier: name type: Text
- identifier: company type: Text
- identifier: contract_end type: DateTime
undefinedname: myvendor/person-contractor
table: tx_mysitepackage_domain_model_person
typeName: contractor
fields:
- identifier: name type: Text
- identifier: company type: Text
- identifier: contract_end type: DateTime
undefinedRecord Types as Collection Children
作为集合子项的记录类型
Define a record that can be used in IRRE collections:
yaml
undefined定义可用于IRRE集合的记录:
yaml
undefinedEXT:my_sitepackage/ContentBlocks/RecordTypes/slide/config.yaml
EXT:my_sitepackage/ContentBlocks/RecordTypes/slide/config.yaml
name: myvendor/slide
table: tx_mysitepackage_domain_model_slide
labelField: title
fields:
- identifier: title type: Text
- identifier: image type: File maxitems: 1
- identifier: link type: Link
```yamlname: myvendor/slide
table: tx_mysitepackage_domain_model_slide
labelField: title
fields:
- identifier: title type: Text
- identifier: image type: File maxitems: 1
- identifier: link type: Link
```yamlEXT:my_sitepackage/ContentBlocks/ContentElements/slider/config.yaml
EXT:my_sitepackage/ContentBlocks/ContentElements/slider/config.yaml
name: myvendor/slider
fields:
- identifier: slides type: Collection foreign_table: tx_mysitepackage_domain_model_slide shareAcrossTables: true shareAcrossFields: true minitems: 1
undefinedname: myvendor/slider
fields:
- identifier: slides type: Collection foreign_table: tx_mysitepackage_domain_model_slide shareAcrossTables: true shareAcrossFields: true minitems: 1
undefined7. Creating Page Types (Custom doktypes)
7. 创建页面类型(自定义doktypes)
Page Types extend the table with custom page types – ideal for blog articles, landing pages, news pages, or other page variants with special properties.
pages页面类型扩展表,添加自定义页面类型——非常适合博客文章、落地页、新闻页或具有特殊属性的其他页面变体。
pagesWhen to Use Page Types
何时使用页面类型
| Use Case | Example |
|---|---|
| Structured page properties | Blog with author, teaser image, publish date |
| Plugin integration | News lists, event calendars reading page properties |
| Different page behavior | Landing pages without navigation |
| SEO-specific fields | Custom meta fields per page type |
| 使用场景 | 示例 |
|---|---|
| 结构化页面属性 | 包含作者、 teaser图片、发布日期的博客 |
| 插件集成 | 读取页面属性的新闻列表、事件日历 |
| 差异化页面行为 | 无导航的落地页 |
| SEO专属字段 | 每种页面类型的自定义元字段 |
Minimal Page Type
最简页面类型配置
yaml
undefinedyaml
undefinedEXT:my_sitepackage/ContentBlocks/PageTypes/blog-article/config.yaml
EXT:my_sitepackage/ContentBlocks/PageTypes/blog-article/config.yaml
name: myvendor/blog-article
typeName: 1705234567
fields:
- identifier: author_name type: Text
undefinedname: myvendor/blog-article
typeName: 1705234567
fields:
- identifier: author_name type: Text
undefinedFull Page Type Example
完整页面类型示例
yaml
undefinedyaml
undefinedEXT:my_sitepackage/ContentBlocks/PageTypes/blog-article/config.yaml
EXT:my_sitepackage/ContentBlocks/PageTypes/blog-article/config.yaml
name: myvendor/blog-article
typeName: 1705234567 # Unix timestamp (unique identifier)
group: default # Options: default, link, special
fields:
- identifier: author_name type: Text label: Author required: true
- identifier: teaser_text type: Textarea label: Teaser
- identifier: hero_image type: File allowed: common-image-types maxitems: 1
- identifier: publish_date type: DateTime label: Publish Date
- identifier: reading_time type: Number label: Reading Time (minutes)
undefinedname: myvendor/blog-article
typeName: 1705234567 # Unix时间戳(唯一标识符)
group: default # 选项:default、link、special
fields:
- identifier: author_name type: Text label: 作者 required: true
- identifier: teaser_text type: Textarea label: 预览文本
- identifier: hero_image type: File allowed: common-image-types maxitems: 1
- identifier: publish_date type: DateTime label: 发布日期
- identifier: reading_time type: Number label: 阅读时长(分钟)
undefinedPage Type Options
页面类型选项
| Option | Type | Required | Description |
|---|---|---|---|
| integer | ✓ | Unique doktype number (use Unix timestamp) |
| string | Group in selector: |
Reserved typeName values: 199, 254 (cannot be used)
| 选项 | 类型 | 必填 | 描述 |
|---|---|---|---|
| 整数 | ✓ | 唯一的doktype编号(建议使用Unix时间戳) |
| 字符串 | 选择器中的分组: |
保留的typeName值: 199、254(不可使用)
Icons for Page States
页面状态图标
Page Types support state-specific icons. Add these to your assets folder:
ContentBlocks/PageTypes/blog-article/
├── assets/
│ ├── icon.svg # Default icon
│ ├── icon-hide-in-menu.svg # Hidden in menu state
│ └── icon-root.svg # Site root state
└── config.yaml页面类型支持特定状态的图标。将以下图标添加到assets文件夹:
ContentBlocks/PageTypes/blog-article/
├── assets/
│ ├── icon.svg # 默认图标
│ ├── icon-hide-in-menu.svg # 隐藏在菜单中的状态图标
│ └── icon-root.svg # 站点根节点状态图标
└── config.yamlBackend Preview
后端预览
Create a to preview custom page properties:
backend-preview.htmlhtml
<!-- templates/backend-preview.html -->
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:be="http://typo3.org/ns/TYPO3/CMS/Backend/ViewHelpers"
data-namespace-typo3-fluid="true">
<div class="card card-size-medium">
<div class="card-body">
<be:link.editRecord uid="{data.uid}" table="{data.mainType}" fields="author_name">
<strong>Author:</strong> {data.author_name}
</be:link.editRecord>
<f:if condition="{data.publish_date}">
<br/><small>Published: <f:format.date format="d.m.Y">{data.publish_date}</f:format.date></small>
</f:if>
</div>
</div>
</html>创建以预览自定义页面属性:
backend-preview.htmlhtml
<!-- templates/backend-preview.html -->
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:be="http://typo3.org/ns/TYPO3/CMS/Backend/ViewHelpers"
data-namespace-typo3-fluid="true">
<div class="card card-size-medium">
<div class="card-body">
<be:link.editRecord uid="{data.uid}" table="{data.mainType}" fields="author_name">
<strong>作者:</strong> {data.author_name}
</be:link.editRecord>
<f:if condition="{data.publish_date}">
<br/><small>发布日期: <f:format.date format="d.m.Y">{data.publish_date}</f:format.date></small>
</f:if>
</div>
</div>
</html>Frontend Integration
前端集成
Page Types have no automatic frontend rendering. Add the ContentBlocksDataProcessor to your TypoScript:
typoscript
undefined页面类型没有自动前端渲染。需在TypoScript中添加ContentBlocksDataProcessor:
typoscript
undefinedConfiguration/TypoScript/setup.typoscript
Configuration/TypoScript/setup.typoscript
page = PAGE
page {
10 = FLUIDTEMPLATE
10 {
templateName = Default
templateRootPaths.10 = EXT:my_sitepackage/Resources/Private/Templates/
dataProcessing {
# Process Content Blocks page data
1 = content-blocks
}
}}
Then access fields in your Fluid template:
```html
<!-- Resources/Private/Templates/Default.html -->
<f:if condition="{data.author_name}">
<p class="author">By {data.author_name}</p>
</f:if>
<f:if condition="{data.hero_image}">
<f:for each="{data.hero_image}" as="image">
<f:image image="{image}" class="hero-image"/>
</f:for>
</f:if>page = PAGE
page {
10 = FLUIDTEMPLATE
10 {
templateName = Default
templateRootPaths.10 = EXT:my_sitepackage/Resources/Private/Templates/
dataProcessing {
# 处理Content Blocks页面数据
1 = content-blocks
}
}}
然后在Fluid模板中访问字段:
```html
<!-- Resources/Private/Templates/Default.html -->
<f:if condition="{data.author_name}">
<p class="author">作者:{data.author_name}</p>
</f:if>
<f:if condition="{data.hero_image}">
<f:for each="{data.hero_image}" as="image">
<f:image image="{image}" class="hero-image"/>
</f:for>
</f:if>Remove from Page Tree Drag Area
从页面树拖拽区域移除
To hide your page type from the "Create new page" drag area:
typoscript
undefined若要在“创建新页面”拖拽区域隐藏页面类型:
typoscript
undefinedConfiguration/user.tsconfig
Configuration/user.tsconfig
options {
pageTree {
doktypesToShowInNewPageDragArea := removeFromList(1705234567)
}
}
undefinedoptions {
pageTree {
doktypesToShowInNewPageDragArea := removeFromList(1705234567)
}
}
undefined8. Creating File Types (Extended Metadata)
8. 创建文件类型(扩展元数据)
New in version 1.2
File Types extend the table with custom fields – perfect for photographer credits, copyright notices, or additional file options.
sys_file_metadata版本1.2新增功能
文件类型扩展表,添加自定义字段——非常适合摄影师署名、版权声明或其他文件选项。
sys_file_metadataAvailable File Type Names
可用的文件类型名称
| typeName | File Types |
|---|---|
| JPEG, PNG, GIF, WebP, SVG |
| MP4, WebM, OGG |
| MP3, WAV, OGG |
| TXT, PDF, Markdown |
| ZIP, Office formats |
| typeName | 文件类型 |
|---|---|
| JPEG、PNG、GIF、WebP、SVG |
| MP4、WebM、OGG |
| MP3、WAV、OGG |
| TXT、PDF、Markdown |
| ZIP、Office格式 |
Minimal File Type
最简文件类型配置
yaml
undefinedyaml
undefinedEXT:my_sitepackage/ContentBlocks/FileTypes/image-extended/config.yaml
EXT:my_sitepackage/ContentBlocks/FileTypes/image-extended/config.yaml
name: myvendor/image-extended
typeName: image
fields:
- identifier: photographer type: Text label: Photographer
undefinedname: myvendor/image-extended
typeName: image
fields:
- identifier: photographer type: Text label: 摄影师
undefinedFull File Type Example
完整文件类型示例
yaml
undefinedyaml
undefinedEXT:my_sitepackage/ContentBlocks/FileTypes/image-extended/config.yaml
EXT:my_sitepackage/ContentBlocks/FileTypes/image-extended/config.yaml
name: myvendor/image-extended
typeName: image
prefixFields: false # Keep original column names
fields:
- identifier: image_overlay_palette
type: Palette
label: 'LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette'
fields:
Reuse existing TYPO3 core fields
- identifier: alternative useExistingField: true
- identifier: description useExistingField: true
- type: Linebreak
- identifier: link useExistingField: true
- identifier: title useExistingField: true
- type: Linebreak
Custom fields
- identifier: photographer type: Text label: Photographer
- identifier: copyright type: Text label: Copyright Notice
- identifier: source_url type: Link label: Source URL
- type: Linebreak
- identifier: crop useExistingField: true
undefinedname: myvendor/image-extended
typeName: image
prefixFields: false # 保留原始列名
fields:
- identifier: image_overlay_palette
type: Palette
label: 'LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette'
fields:
复用TYPO3核心现有字段
- identifier: alternative useExistingField: true
- identifier: description useExistingField: true
- type: Linebreak
- identifier: link useExistingField: true
- identifier: title useExistingField: true
- type: Linebreak
自定义字段
- identifier: photographer type: Text label: 摄影师
- identifier: copyright type: Text label: 版权声明
- identifier: source_url type: Link label: 来源URL
- type: Linebreak
- identifier: crop useExistingField: true
undefinedFile Type Options
文件类型选项
| Option | Type | Required | Description |
|---|---|---|---|
| string | ✓ | One of: |
| boolean | Disable prefixing (recommended: |
| 选项 | 类型 | 必填 | 描述 |
|---|---|---|---|
| 字符串 | ✓ | 可选值: |
| 布尔值 | 禁用前缀(推荐设置为 |
Use Cases for File Types
文件类型使用场景
| Use Case | Fields to Add |
|---|---|
| Photography agency | |
| Video platform | |
| Document management | |
| E-commerce | |
| 使用场景 | 需添加的字段 |
|---|---|
| 摄影机构 | |
| 视频平台 | |
| 文档管理 | |
| 电商平台 | |
Accessing File Type Fields
访问文件类型字段
In Fluid templates, access custom metadata through FAL references:
html
<f:for each="{data.images}" as="image">
<figure>
<f:image image="{image}" alt="{image.alternative}"/>
<f:if condition="{image.properties.photographer}">
<figcaption>
Photo: {image.properties.photographer}
<f:if condition="{image.properties.copyright}">
| © {image.properties.copyright}
</f:if>
</figcaption>
</f:if>
</figure>
</f:for>在Fluid模板中,通过FAL引用访问自定义元数据:
html
<f:for each="{data.images}" as="image">
<figure>
<f:image image="{image}" alt="{image.alternative}"/>
<f:if condition="{image.properties.photographer}">
<figcaption>
摄影:{image.properties.photographer}
<f:if condition="{image.properties.copyright}">
| © {image.properties.copyright}
</f:if>
</figcaption>
</f:if>
</figure>
</f:for>9. Field Types Reference
9. 字段类型参考
Simple Fields
简单字段
| Type | Description | Example |
|---|---|---|
| Single line text | |
| Multi-line text | |
| Email address | |
| Link/URL | |
| Integer/Float | |
| Date and/or time | |
| Color picker | |
| Boolean checkbox | |
| Radio buttons | |
| URL slug | |
| Password field | |
| 类型 | 描述 | 示例 |
|---|---|---|
| 单行文本 | |
| 多行文本 | |
| 邮箱地址 | |
| 链接/URL | |
| 整数/浮点数 | |
| 日期和/或时间 | |
| 颜色选择器 | |
| 布尔复选框 | |
| 单选按钮 | |
| URL别名 | |
| 密码字段 | |
Relational Fields
关联字段
| Type | Description | Example |
|---|---|---|
| File references (FAL) | |
| Record relations | |
| Dropdown selection | |
| System categories | |
| Inline records (IRRE) | |
| Folder reference | |
| Language selector | |
| 类型 | 描述 | 示例 |
|---|---|---|
| 文件引用(FAL) | |
| 记录关联 | |
| 下拉选择 | |
| 系统分类 | |
| 内联记录(IRRE) | |
| 文件夹引用 | |
| 语言选择器 | |
Structural Fields
结构字段
| Type | Description | Example |
|---|---|---|
| Tab separator | |
| Group fields | |
| Line break in palette | |
| FlexForm container | |
| JSON field | |
| 类型 | 描述 | 示例 |
|---|---|---|
| 标签页分隔符 | |
| 字段分组 | |
| 分组内换行 | |
| FlexForm容器 | |
| JSON字段 | |
Common Field Options
通用字段选项
yaml
fields:
- identifier: my_field
type: Text
label: My Field Label # Static label (or use labels.xlf)
description: Help text # Field description
required: true # Make field required
default: "Default value" # Default value
placeholder: "Enter text..." # Placeholder text
prefixField: false # Disable prefixing for this field
useExistingField: true # Reuse existing TCA field
displayCond: 'FIELD:other:=:1' # Conditional display
onChange: reload # Reload form on changeyaml
fields:
- identifier: my_field
type: Text
label: 我的字段标签 # 静态标签(或使用labels.xlf)
description: 帮助文本 # 字段描述
required: true # 设置为必填字段
default: "默认值" # 默认值
placeholder: "输入文本..." # 占位文本
prefixField: false # 为此字段禁用前缀
useExistingField: true # 复用现有TCA字段
displayCond: 'FIELD:other:=:1' # 条件显示
onChange: reload # 字段变更时重新加载表单File Field Example
文件字段示例
yaml
fields:
- identifier: gallery_images
type: File
allowed: common-image-types
minitems: 1
maxitems: 10
appearance:
createNewRelationLinkTitle: Add Image
showAllLocalizationLink: true
behaviour:
allowLanguageSynchronization: trueyaml
fields:
- identifier: gallery_images
type: File
allowed: common-image-types
minitems: 1
maxitems: 10
appearance:
createNewRelationLinkTitle: 添加图片
showAllLocalizationLink: true
behaviour:
allowLanguageSynchronization: trueSelect Field Example
选择字段示例
yaml
fields:
- identifier: layout
type: Select
renderType: selectSingle
default: default
items:
- label: Default Layout
value: default
- label: Wide Layout
value: wide
- label: Compact Layout
value: compactyaml
fields:
- identifier: layout
type: Select
renderType: selectSingle
default: default
items:
- label: 默认布局
value: default
- label: 宽布局
value: wide
- label: 紧凑布局
value: compactCollection Field Example (Inline IRRE)
集合字段示例(内联IRRE)
yaml
fields:
- identifier: accordion_items
type: Collection
labelField: title
minitems: 1
maxitems: 20
appearance:
collapseAll: true
levelLinksPosition: both
fields:
- identifier: title
type: Text
required: true
- identifier: content
type: Textarea
enableRichtext: true
- identifier: is_open
type: Checkbox
label: Initially Openyaml
fields:
- identifier: accordion_items
type: Collection
labelField: title
minitems: 1
maxitems: 20
appearance:
collapseAll: true
levelLinksPosition: both
fields:
- identifier: title
type: Text
required: true
- identifier: content
type: Textarea
enableRichtext: true
- identifier: is_open
type: Checkbox
label: 默认展开10. Field Prefixing
10. 字段前缀
Content Blocks automatically prefixes field identifiers to avoid collisions.
Content Blocks会自动为字段标识符添加前缀,避免冲突。
Prefixing Types
前缀类型
yaml
undefinedyaml
undefinedFull prefix (default): myvendor_myblock_fieldname
完整前缀(默认): myvendor_myblock_fieldname
name: myvendor/my-block
prefixFields: true
prefixType: full
name: myvendor/my-block
prefixFields: true
prefixType: full
Vendor prefix only: myvendor_fieldname
仅供应商前缀: myvendor_fieldname
name: myvendor/my-block
prefixFields: true
prefixType: vendor
name: myvendor/my-block
prefixFields: true
prefixType: vendor
Custom vendor prefix: tx_custom_fieldname
自定义供应商前缀: tx_custom_fieldname
name: myvendor/my-block
prefixFields: true
prefixType: vendor
vendorPrefix: tx_custom
name: myvendor/my-block
prefixFields: true
prefixType: vendor
vendorPrefix: tx_custom
No prefix (use with caution!)
无前缀(谨慎使用!)
name: myvendor/my-block
prefixFields: false
undefinedname: myvendor/my-block
prefixFields: false
undefinedDisable Prefixing per Field
为单个字段禁用前缀
yaml
fields:
- identifier: my_custom_field
type: Text
prefixField: false # This field won't be prefixedyaml
fields:
- identifier: my_custom_field
type: Text
prefixField: false # 该字段不会添加前缀11. Templating Features
11. 模板功能
Accessing Data in Fluid
在Fluid中访问数据
html
<!-- Basic field access -->
{data.header}
{data.my_field}
<!-- Record metadata -->
{data.uid}
{data.pid}
{data.languageId}
{data.mainType} <!-- Table name: tt_content -->
{data.recordType} <!-- CType: myvendor_heroblock -->
{data.fullType} <!-- tt_content.myvendor_heroblock -->
<!-- Raw database values -->
{data.rawRecord.some_field}
<!-- System properties -->
{data.systemProperties.createdAt}
{data.systemProperties.lastUpdatedAt}
{data.systemProperties.sorting}
{data.systemProperties.disabled}
<!-- Language info -->
{data.languageInfo.translationParent}
{data.languageInfo.translationSource}
<!-- Relations are auto-resolved! -->
<f:for each="{data.gallery_images}" as="image">
<f:image image="{image}" width="400"/>
</f:for>
<!-- Nested collections -->
<f:for each="{data.accordion_items}" as="item">
<h3>{item.title}</h3>
<f:format.html>{item.content}</f:format.html>
</f:for>html
<!-- 基础字段访问 -->
{data.header}
{data.my_field}
<!-- 记录元数据 -->
{data.uid}
{data.pid}
{data.languageId}
{data.mainType} <!-- 数据表名: tt_content -->
{data.recordType} <!-- CType: myvendor_heroblock -->
{data.fullType} <!-- tt_content.myvendor_heroblock -->
<!-- 原始数据库值 -->
{data.rawRecord.some_field}
<!-- 系统属性 -->
{data.systemProperties.createdAt}
{data.systemProperties.lastUpdatedAt}
{data.systemProperties.sorting}
{data.systemProperties.disabled}
<!-- 语言信息 -->
{data.languageInfo.translationParent}
{data.languageInfo.translationSource}
<!-- 关联数据自动解析! -->
<f:for each="{data.gallery_images}" as="image">
<f:image image="{image}" width="400"/>
</f:for>
<!-- 嵌套集合 -->
<f:for each="{data.accordion_items}" as="item">
<h3>{item.title}</h3>
<f:format.html>{item.content}</f:format.html>
</f:for>Asset ViewHelpers
资源视图助手
html
<!-- Include CSS from assets folder -->
<f:asset.css identifier="my-block-css" href="{cb:assetPath()}/frontend.css"/>
<!-- Include JS from assets folder -->
<f:asset.script identifier="my-block-js" src="{cb:assetPath()}/frontend.js"/>
<!-- Cross-block asset reference -->
<f:asset.css identifier="shared-css" href="{cb:assetPath(name: 'vendor/other-block')}/shared.css"/>html
<!-- 从assets文件夹引入CSS -->
<f:asset.css identifier="my-block-css" href="{cb:assetPath()}/frontend.css"/>
<!-- 从assets文件夹引入JS -->
<f:asset.script identifier="my-block-js" src="{cb:assetPath()}/frontend.js"/>
<!-- 跨模块资源引用 -->
<f:asset.css identifier="shared-css" href="{cb:assetPath(name: 'vendor/other-block')}/shared.css"/>Translation ViewHelper
翻译视图助手
html
<!-- Access labels.xlf translations -->
<f:translate key="{cb:languagePath()}:my_label"/>
<!-- Cross-block translation -->
<f:translate key="{cb:languagePath(name: 'vendor/other-block')}:shared_label"/>html
<!-- 访问labels.xlf中的翻译 -->
<f:translate key="{cb:languagePath()}:my_label"/>
<!-- 跨模块翻译引用 -->
<f:translate key="{cb:languagePath(name: 'vendor/other-block')}:shared_label"/>12. Extending Existing Tables
12. 扩展现有数据表
Add custom types to existing tables (like ):
tx_newsyaml
undefined为现有表(如)添加自定义类型:
tx_newsyaml
undefinedEXT:my_sitepackage/ContentBlocks/RecordTypes/custom-news/config.yaml
EXT:my_sitepackage/ContentBlocks/RecordTypes/custom-news/config.yaml
name: myvendor/custom-news
table: tx_news_domain_model_news
typeName: custom_news
fields:
- identifier: title useExistingField: true
- identifier: custom_field type: Text
undefinedname: myvendor/custom-news
table: tx_news_domain_model_news
typeName: custom_news
fields:
- identifier: title useExistingField: true
- identifier: custom_field type: Text
undefined13. Workflow with DDEV
13. 与DDEV配合的工作流
Standard Development Workflow
标准开发工作流
bash
undefinedbash
undefined1. Create new Content Block
1. 创建新的Content Block
ddev typo3 make:content-block
ddev typo3 make:content-block
2. Clear system caches
2. 清除系统缓存
ddev typo3 cache:flush -g system
ddev typo3 cache:flush -g system
3. Update database schema
3. 更新数据库架构
ddev typo3 extension:setup --extension=my_sitepackage
ddev typo3 extension:setup --extension=my_sitepackage
Alternative: Use Database Analyzer in TYPO3 Backend
替代方案:使用TYPO3后端的数据库分析器
Admin Tools > Maintenance > Analyze Database Structure
管理工具 > 维护 > 分析数据库结构
undefinedundefinedUsing webprofil/make Extension
使用webprofil/make扩展
If is installed:
webprofil/makebash
undefined若已安装:
webprofil/makebash
undefinedCreate Content Block with webprofil/make
使用webprofil/make创建Content Block
ddev make:content_blocks
ddev make:content_blocks
Clear caches and update database
清除缓存并更新数据库
ddev typo3 cache:flush
ddev typo3 database:updateschema
undefinedddev typo3 cache:flush
ddev typo3 database:updateschema
undefinedIntegration with Extbase
与Extbase集成
After creating Record Types with proper table names, generate Extbase models:
bash
undefined创建符合表名规则的记录类型后,生成Extbase模型:
bash
undefinedIf typo3:make:model is available
若typo3:make:model可用
ddev typo3 make:model --extension=my_sitepackage
ddev typo3 make:model --extension=my_sitepackage
Generate repository
生成仓库类
ddev typo3 make:repository --extension=my_sitepackage
undefinedddev typo3 make:repository --extension=my_sitepackage
undefined14. Defaults Configuration
14. 默认配置
Create a in project root for default settings:
content-blocks.yamlyaml
undefined在项目根目录创建设置默认配置:
content-blocks.yamlyaml
undefinedcontent-blocks.yaml
content-blocks.yaml
vendor: myvendor
extension: my_sitepackage
content-type: content-element
skeleton-path: content-blocks-skeleton
config:
content-element:
basics:
- TYPO3/Appearance
- TYPO3/Links
group: common
prefixFields: true
prefixType: full
record-type:
prefixFields: true
prefixType: vendor
vendorPrefix: tx_mysitepackage
undefinedvendor: myvendor
extension: my_sitepackage
content-type: content-element
skeleton-path: content-blocks-skeleton
config:
content-element:
basics:
- TYPO3/Appearance
- TYPO3/Links
group: common
prefixFields: true
prefixType: full
record-type:
prefixFields: true
prefixType: vendor
vendorPrefix: tx_mysitepackage
undefined15. Best Practices
15. 最佳实践
DO ✅
推荐做法 ✅
-
Use Extbase-compatible table names for Record Types:yaml
table: tx_myextension_domain_model_myrecord -
Reuse existing fields when possible:yaml
- identifier: header useExistingField: true -
Group related fields with Tabs and Palettes:yaml
- identifier: settings_tab type: Tab label: Settings -
Use meaningful identifiers (snake_case):yaml
- identifier: hero_background_image -
Clear caches after changes:bash
ddev typo3 cache:flush -g system ddev typo3 extension:setup --extension=my_sitepackage -
Use labels.xlf for all user-facing labels
-
为记录类型使用兼容Extbase的表名:yaml
table: tx_myextension_domain_model_myrecord -
尽可能复用现有字段:yaml
- identifier: header useExistingField: true -
使用标签页和分组来组织相关字段:yaml
- identifier: settings_tab type: Tab label: 设置 -
使用有意义的标识符(蛇形命名法snake_case):yaml
- identifier: hero_background_image -
修改后清除缓存:bash
ddev typo3 cache:flush -g system ddev typo3 extension:setup --extension=my_sitepackage -
使用labels.xlf管理所有用户可见的标签
DON'T ❌
不推荐做法 ❌
-
Don't use raw SQL - Content Blocks generates schema automatically
-
Don't duplicate TCA - Config.yaml is the single source of truth
-
Don't use short table names for Extbase integration:yaml
# ❌ Wrong table: team_member # ✅ Correct table: tx_mysitepackage_domain_model_teammember -
Don't use dashes in identifiers:yaml
# ❌ Wrong identifier: hero-image # ✅ Correct identifier: hero_image -
Don't forget shareAcross options when using foreign_table in multiple places
-
不要使用原生SQL - Content Blocks会自动生成数据库架构
-
不要重复编写TCA - Config.yaml是唯一可信来源
-
不要为Extbase集成使用短表名:yaml
# ❌ 错误 table: team_member # ✅ 正确 table: tx_mysitepackage_domain_model_teammember -
不要在标识符中使用短横线:yaml
# ❌ 错误 identifier: hero-image # ✅ 正确 identifier: hero_image -
在多个地方使用foreign_table时,不要忘记设置shareAcross选项
16. Troubleshooting
16. 故障排除
Content Block Not Appearing
Content Block未显示
bash
undefinedbash
undefinedClear all caches
清除所有缓存
ddev typo3 cache:flush
ddev typo3 cache:flush
Rebuild class loading
重建类加载
ddev composer dump-autoload
ddev composer dump-autoload
Check extension setup
检查扩展设置
ddev typo3 extension:setup --extension=my_sitepackage
undefinedddev typo3 extension:setup --extension=my_sitepackage
undefinedDatabase Errors
数据库错误
bash
undefinedbash
undefinedUpdate database schema
更新数据库架构
ddev typo3 database:updateschema
ddev typo3 database:updateschema
Or use Compare Tool
或使用对比工具
Admin Tools > Maintenance > Analyze Database Structure
管理工具 > 维护 > 分析数据库结构
undefinedundefinedField Not Saving
字段无法保存
- Check field identifier is unique (use prefixing)
- Verify field type is correct
- Check for typos in config.yaml
- Ensure labels.xlf has matching keys
- 检查字段标识符是否唯一(使用前缀)
- 验证字段类型是否正确
- 检查config.yaml中的拼写错误
- 确保labels.xlf中有匹配的键
17. Version Constraints
17. 版本约束
php
// ext_emconf.php
$EM_CONF[$_EXTKEY] = [
'title' => 'My Extension',
'version' => '1.0.0',
'state' => 'stable',
'constraints' => [
'depends' => [
'typo3' => '13.0.0-14.99.99',
'content_blocks' => '2.0.0-2.99.99',
],
],
];php
// ext_emconf.php
$EM_CONF[$_EXTKEY] = [
'title' => '我的扩展',
'version' => '1.0.0',
'state' => 'stable',
'constraints' => [
'depends' => [
'typo3' => '13.0.0-14.99.99',
'content_blocks' => '2.0.0-2.99.99',
],
],
];Related Skills
相关技能
For migration between classic TYPO3 extensions and Content Blocks, see the dedicated migration skill:
- typo3-content-blocks-migration - Bidirectional migration guide with:
- TCA → Content Blocks field mapping
- Content Blocks → TCA reverse mapping
- Data migration scripts
- Step-by-step examples
- Checklists for both directions
如需在传统TYPO3扩展与Content Blocks之间迁移,请查看专门的迁移技能:
- typo3-content-blocks-migration - 双向迁移指南,包含:
- TCA → Content Blocks字段映射
- Content Blocks → TCA反向映射
- 数据迁移脚本
- 分步示例
- 双向迁移检查清单
18. Package Management for v14 Compatibility
18. v14兼容性的包管理
If extensions don't support TYPO3 v14 yet, fork and update:
bash
undefined若扩展尚不支持TYPO3 v14,请fork并更新:
bash
undefinedAdd private fork as VCS repository in composer.json
在composer.json中添加私有fork作为VCS仓库
Then update extension for v14 using typo3-rector and typo3-update skills
然后使用typo3-rector和typo3-update技能将扩展更新为v14兼容版本
Required private forks for webconsulting stack:
- `webprofil/wp-t3monitoring-client` (^13 → ^14)
- `webprofil/deployer` (^13 → ^14)
- `webprofil/crawler` (^1.0 → compatible with v14)
- `webprofil/make` (^1.1 → compatible with v14)
---
webconsulting技术栈所需的私有fork:
- `webprofil/wp-t3monitoring-client` (^13 → ^14)
- `webprofil/deployer` (^13 → ^14)
- `webprofil/crawler` (^1.0 → 兼容v14)
- `webprofil/make` (^1.1 → 兼容v14)
---References
参考资料
Credits & Attribution
致谢与归属
This skill incorporates information from the official Content Blocks documentation maintained by the TYPO3 Content Types Team and Friends of TYPO3.
Original documentation: https://docs.typo3.org/p/friendsoftypo3/content-blocks/
Adapted by webconsulting.at for this skill collection
本技能整合了由TYPO3内容类型团队和Friends of TYPO3维护的官方Content Blocks文档中的信息。
由webconsulting.at改编至本技能集合