The TYPO3 Content Blocks extension,
friendsoftypo3/content-blocks
, provides a simplified
way to register and use content types as sub-resources inside extensions.
A Content Block is a simplified, component-based approach of defining
Content Types in TYPO3. This includes Content Elements, Page Types and generic
Record Types. A YAML file serves as a central definition. Content Blocks acts
hereby as a compiler for more complex low-level code. It adheres to best
practices by default, significantly reducing boilerplate code.
Note
Content Blocks started out as a layer on top of TYPO3 and still is. The
long-term goal is to integrate the concept of a Content Block into the Core
as a first-class citizen. As of now, some knowledge of the underlying Core
API is required to fully grasp the possibilities of this extension and how
to customize it.
For more technical, historical and conceptual insights about Content Blocks we
recommend these further readings:
The goal is to encapsulate all resources belonging to the Content Block
inside one component. This leads to re-usable components, which can be
easily copy-pasted into other projects.
This file is the basis for the definition. It defines exactly one
Content Type. Using YAML over PHP includes a wider range of people, which is
able to modify Content Blocks without the need of a developer.
The registration works by simply placing a Content Block into a dedicated
folder. For this purpose an already loaded extension is required as a host.
Depending on the Content Type the Content Block is put into a predestinated
sub-folder.
Content Blocks is the name of the extension and generates the code for the Core API.
A single Content Block is a small chunk of information, which defines exactly one Content Type.
A Content Type is an entity in TYPO3, which defines a set of fields and their behavior.
A Content Element is a special Content Type, which has a frontend rendering definition.
A Page Type is a special Content Type, which defines the behavior of a web page.
A Record Type is a generic Content Type.
Installation
Composer installations
composer require friendsoftypo3/content-blocks
Copied!
Classic mode
Activate the Content Blocks extension in the Extension Manager.
Security
In classic mode it is important to deny access to the Content Blocks folder
by the webserver. For this a small adjustment to the standard TYPO3 .htaccess
file in the section "Access block for folders" is needed:
The minimal viable definition consists of a folder with a YAML file named
config.yaml inside. All other resources are split into three folders
named assets, templates and language.
First of all, a
name has to be defined. It must be unique inside your
installation. It consists, similarly to composer package names, of a vendor and
a package part separated by a slash. It is used to prefix new field names, new
tables and record type identifiers.
Inside
fields you define the structure and configuration of the
necessary fields. The
identifier has to be unique per Content Block.
It is possible to reuse existing fields with the flag
useExistingField.
This allows e.g. to use the same field
header or
bodytext across
multiple Content Blocks with different configuration. Be aware that system
fields shouldn't be reused. A list of sane reusable fields can be referenced in
the documentation. Furthermore, own custom fields can be reused as well.
For more information about the YAML syntax refer to YAML RFC
assets
The assets folder contains public resources. If you are familiar with the
directory structure of extensions, this would be the Resources/Public
folder. This is the place where you can put your CSS, JavaScript or image files
inside. In order to include these in your template, you must use custom
Content Block ViewHelpers.
This folder will be symlinked or copied to
Resources/Public/ContentBlocks/<vendor>/<name> of the host extension. This
is to keep the modular style of a Content Type but still leverage the TYPO3
asset publishing mechanism.
icon.svg
This is the icon for the Content Type. There is a fallback to a default icon,
but it is recommended to replace it with your own, custom icon. You can find
many official TYPO3 icons here.
Allowed file extensions are svg, png and gif (in preferred order).
icon-hide-in-menu.svg
This is a special icon for Page Types for the "hide in menu" state. The same
logic applies as for the standard icon.svg.
icon-root.svg
This is a special icon for Page Types for the "is_siteroot" state. The same
logic applies as for the standard icon.svg.
templates
The templates folder contains private resources. If you are familiar with the
directory structure of extensions, this would be the Resources/Private
folder. There is a limited set of directories and files, which you can place
here.
backend-preview.html
The backend-preview.html can be added to customize the backend preview for
your editors.
For larger Content Elements, you can divide your frontend.html template into
smaller chunks by creating separate partials here.
Partials are included as you normally would in any Fluid template.
Note
Due to current Fluid restrictions, partials have to start with an uppercase
letter. This restriction might be lifted in later Fluid versions (v5 or above).
You can also add layouts to your Content Block if needed.
<f:layoutname="MyLayout">
Copied!
language
This is the folder for your translations. In fact, if you only have one
language, there is no actual need to maintain translations here. However, it is
best practice to separate labels and configuration.
labels.xlf
This XLF file is the english basis for your translations. All translations
for backend labels as well as for frontend labels are defined here. Translations
to other languages are defined in separate files prefixed with the language code
e.g. de.labels.xlf.
In order to register a new Content Block, a folder ContentBlocks has to be
created on the root level inside a loaded extension. Depending on the Content
Type you want to create, you place the new Content Block into a dedicated
folder. These are named ContentElements, PageTypes and RecordTypes.
my_extension
Classes
Configuration
ContentBlocks
ContentElements
content-block-1
content-block-2
PageTypes
content-block-3
content-block-4
RecordTypes
content-block-5
content-block-6
ext_emconf.php
composer.json
The system loads them automatically as soon as it finds any folder inside these
directories, which has a file with the name config.yaml
inside. Refer to the YAML reference, on how to define
this file.
Tip
Use the command make:content-block to quickly create a
new Content Block.
Tip
You can copy and paste any Content Block from one project to another, and it
will be automatically available.
Administration
Make sure to add Content Blocks as a dependency in the host extension.
You will need to allow the generated database fields, tables
(if using Collections) and CType in the backend
user group permissions.
Commands
Content Blocks comes with a few CLI commands. Here you can find a list of
available commands.
The command
make:content-block creates a bare-minimum Content Block.
This is actually an alias for
content-blocks:create, which is inspired
by the EXT:make extension.
Your Content Block name (this is not the title). Lowercase, separated by dashes.
extension
extension
Type
string
Required
true
Note
If you use the step-by-step process to navigate through the creation,
only locally installed extensions will be displayed. In case you have
a package installed via vcs, you have to provide it via CLI option.
The host extension, where to store your new Content Block.
title
title
Type
string
The human-readable title for your Content Block.
type-name
type-name
Type
string|int
Custom type name. Required for content-type
page-type (must be int).
Important
The
type-name option is required and has to be an integer value, if you
choose the
page-type content type.
skeleton-path
skeleton-path
Type
string
Default
content-blocks-skeleton
A path relative to the current working directory, which holds a skeleton of
a Content Block. Only needed, if you want to use a different name or path
other than content-blocks-skeleton.
Alternatively, the command can guide you through the creation by omitting the
required options:
vendor/bin/typo3 make:content-block
Copied!
On non-composer installations use:
typo3/sysext/core/bin/typo3 make:content-block
Copied!
Example interaction:
Choose the content type of your content block [Content Element]:
[content-element] Content Element
[page-type ] Page Type
[record-type ] Record Type
> content-element
Enter your vendor name:
> my-vendor
Enter your content block name:
> my-content-block-name
Choose an extension inwhich the content block should be stored:
[sitepackage] Test Package for content blocks
> sitepackage
Copied!
After running the make command
In order to create newly added database tables or fields, you have to clear the
caches and then run the database compare. You can do the same in the TYPO3
Backend by using the Database Analyzer. Repeat this step every time you add new
fields to your Content Block definition.
vendor/bin/typo3 cache:flush -g system
vendor/bin/typo3 extension:setup --extension=my_sitepackage
Copied!
Content Block skeleton
New in version 1.1
It is now possible to define a "skeleton" for your Content Blocks. To do this
create a folder called content-blocks-skeleton in your project root. This
folder may contain default templates or assets for one or more Content Types. It
is used as a base when creating new types with the
make:content-block
command. In order to add a skeleton for Content Elements, create a folder called
content-element within that directory. Then, the structure is identical to
your concrete Content Block as you know it. You may place any files there. They
will simply be copied when a new Content Block is created. It is not possible to
define language/labels.xlf or config.yaml this way, as they are dynamically
generated based on your arguments.
content-blocks-skeleton
content-element
assets
icon.svg
templates
backend-preview.html
frontend.html
page-type
record-type
In case you want to name the skeleton folder differently or place it somewhere
else, you can override the default folder by providing the option
--skeleton-path with a relative path to your current working directory.
It is now possible to define default options for this command via a yaml config
file. By default, the command looks for a file called content-blocks.yaml in
the current working directory. The location and name can be overridden with the
--config-path option.
This config sets defaults for
vendor,
skeleton-path,
extension and
content-type. These are all possible options right
now.
Now, whenever you run this command, these options will be set by default. This
does not mean, the questions for these options will be skipped, only that they
are the default value, if you just press Enter without any input. They will
be visible in brackets at the very right [default value].
New in version 1.3
The content-blocks.yaml file supports arbitrary default values for the generated
config.yaml file.
Add a
config key followed by a Content Type identifier. In this case,
we set default values for Content Elements, so we use
content-element.
You can also set default values for page-type, record-type or file-type.
Values defined in here override the generated configuration by the command.
+-----------+-------------------+-------------------------+-----------------+-----------------------------+
| vendor | name | extension | content-type | table |
+-----------+-------------------+-------------------------+-----------------+-----------------------------+
| example | tabs | content_blocks_examples | Content Element | tt_content |
| example | accordion | content_blocks_examples | Content Element | tt_content |
| example | card-group | content_blocks_examples | Content Element | tt_content |
| example | cta | content_blocks_examples | Content Element | tt_content |
| example | icon-group | content_blocks_examples | Content Element | tt_content |
| example | imageslider | content_blocks_examples | Content Element | tt_content |
| example | example-page-type | content_blocks_examples | Page Type | pages |
| hov | record1 | content_blocks_examples | Record Type | tx_hov_domain_model_record1 |
| hov | record2 | content_blocks_examples | Record Type | tx_hov_domain_model_record1 |
| hov | notype | content_blocks_examples | Record Type | tx_hov_domain_model_notype |
+-----------+-------------------+-------------------------+-----------------+-----------------------------+
Copied!
Language Generate command
The command
content-blocks:language:generate updates the labels.xlf
content for the specified Content Block. Already set labels in both the
labels.xlf file and in the config.yaml are considered. The labels.xlf
file has precedence over inline labels in the YAML definition. Optional keys
like descriptions or labels for existing fields will only be be generated if
they have been set manually. Custom translations, which don't belong to the
automatic language keys, will be kept and appended to the end.
Arguments
content-block
content-block
Required
true (false if
--extension provided)
Type
string
The Content Block to generate the xlf for.
Options
print
print
Shortcut
p
Type
bool
Print labels.xlf to terminal instead of writing to file system.
extension
extension
Shortcut
e
Type
string
Write labels.xlf to all Content Blocks within the given extension.
Write up-to-date labels.xlf file for Content Block example/example.
<?xml version="1.0"?><xliffversion="1.2"xmlns="urn:oasis:names:tc:xliff:document:1.2"><filedatatype="plaintext"original="labels.xlf"source-language="en"date="2023-12-03T08:37:53+00:00"product-name="demo/demo"><header/><body><trans-unitid="title"><source>My demo title</source></trans-unit><trans-unitid="description"><source>This is just a demo/demo</source></trans-unit><trans-unitid="header.label"><source>Existing field override</source></trans-unit><trans-unitid="slug.label"><source>My Slug</source></trans-unit><trans-unitid="slug.description"><source>My Slug Description</source></trans-unit><trans-unitid="my_collection.label"><source>my_collection</source></trans-unit><trans-unitid="my_collection.text.label"><source>text</source></trans-unit><trans-unitid="my_collection.my_collection.label"><source>my_collection</source></trans-unit><trans-unitid="my_collection.my_collection.text.label"><source>text</source></trans-unit><trans-unitid="external_table.label"><source>external_table</source></trans-unit><trans-unitid="external_table_2.label"><source>external_table_2</source></trans-unit><trans-unitid="related_content.label"><source>related_content</source></trans-unit><trans-unitid="my-custom-key"><source>My translation</source></trans-unit></body></file></xliff>
Copied!
Publish Assets Command
The command
content-blocks:assets:publish publishes your public Content
Block assets into the Resources/Public folder of the host extension. Normally,
this is performed automatically every time Content Blocks is compiled. In some
deployment scenarios this command could be performed in the CI pipeline to
publish assets without the requirement for a database connection.
YAML reference
The heart of a Content Block is the config.yaml file. Here you can
find all possible configuration options. There are slight differences, whether
you are dealing with Content Elements,
Page Types or
Record Types. In general Content Elements
and Page Types are a special concept in TYPO3. The Core already defines the
table names, the type field, etc. You just have to define a new type. This is
done by providing the
name attribute, which will be converted to the
type name. Page Types require an integer value for the type. Therefore you need
to set it additionally with
typeName.
With TYPO3 you can also create custom Record Types. They require you to define
a custom
table and a
labelField field. Per default all extra
features like workspaces, language support, frontend restrictions, etc. are
enabled. You can selectively disable each one of them, if you don't use them.
Every editing interface configuration must contain exactly one name. The name
is made up of vendor and content block name separated by a / just like the
vendor/package notation in a traditional composer.json file. It must be
unique and must have at least 3 characters. Both parts, the vendor and
content block name, have to be lowercase and be separated by a dash -.
name:my-vendor/my-content-block-name
Copied!
title
title
Type
string
This is the title of the Content Block. If you have a labels.xlf file, you
should define it there with the key
title. If both are defined, the
translation file has precedence. If nothing is defined, the title falls back
to
name.
title:"My super duper Content Block"
Copied!
prefixFields
prefixFields
Type
boolean
Default
true
The default behavior is to convert the both
name parts into a prefix.
All dashes are removed in this process and the parts are combined with an
underscore to prevent collisions. In order to better reuse fields between
Content Blocks, it can be useful to deactivate this option.
Determines how to prefix the field if
prefixFields is enabled. Can be
either
full (default) or
vendor. The latter removes the
second part of
name from the prefix.
If set, this prefix will be used instead of the vendor part of
name.
This is especially useful if you want to adhere to the best practice of
prefixing fields with tx_extension.
The priority can be used to prioritize certain Content Blocks in the loading
order. Higher priorities will be loaded before lower ones. This affects e.g.
the order in the "New Content Element Wizard".
# This Content Block will be displayed before others without a priority set.priority:10
Copied!
Note
The default loading order is undefined and depends on the
(file-)system and the order, in which extensions are loaded.
basics
basics
Type
array
Globally defined
basics are appended to the very end of your
fields array. Most commonly used to include shared
Tabs.
The main entry point for the field definitions. Fields defined in this array
are displayed in the backend exactly in the same order. You can create new
custom fields or reuse existing ones, which are defined via TCA. Learn
here what is needed to define a
field.
This is the description of the Content Element. If you have a labels.xlf
file, you should define it there with the key
description. If both are
defined, the translation file has precedence.
description:"Here comes my description"
Copied!
group
group
Type
string
Default
default
The group is used for the grouping of the record type selector in the edit
view of records. In addition, it is used for the "New Content Element Wizard"
for the tab grouping. By default, all new types are placed in the default
group.
group:special
Copied!
The Core defines these groups for Content Elements:
default
menu
special
forms
plugins
typeName
typeName
Type
string
Default
automatically generated from
name
The identifier of the new Content Element. It is automatically generated from
the name, if not defined manually.
typeName:my_content_element
Copied!
saveAndClose
saveAndClose
Type
bool
Default
false
Can be activated in order to skip the edit view when adding the Content
Element via the NewContentElementWizard. This can be useful if you have a
Content Element or Plugin without configuration.
The
typeName has to be a numerical value. There are some reserved
numbers, which you can't use either: 199, 254.
typeName:1701284021
Copied!
Tip
We recommend to use the current unix timestamp fo your type name. This
is almost guaranteed unique. The kickstart command will default to the
current timestamp as well.
group
group
Type
string
Default
default
The group is used for the grouping of the record type selector in the edit
view of records. In addition, it is used for the "Create multiple pages" view
for selecting the type. By default, all new types are placed in the default
group.
The custom table name to be used for the new Record Type.
Warning
Avoid using dashes "-" inside your table names. They are not guaranteed to
be escaped in the database. We recommend to always use snake case.
table:tx_vendor_my_custom_table_name
Copied!
group
group
Type
string
The group is used for the grouping of the record type selector in the edit
view of records. By default records are not grouped.
Note
Grouping only makes sense, if you define multiple types
for one
table. Otherwise, the record type selector is not
displayed.
Note
The group needs to be registered first, before you can use it here. See
this guide on how to do it.
group:my_group
Copied!
labelField
labelField
Type
string|array
Required
true
Defines which field should be used as the title of the record. If not
defined, the first valid child field will be used as the label. It is
possible to define an array of fields, which will be displayed
comma-separated in the backend.
# a single field for the labellabelField:title# multiple fields will be displayed comma-separatedlabelField:-title-text
Copied!
fallbackLabelFields
fallbackLabelFields
Type
array
Defines which fields should be used as fallback, if
labelField is not
filled. The first filled field which is found will be used. Can only be used
if there is only one
labelField field defined.
# fallback fields will be used, if title from labelField is emptylabelField:titlefallbackLabelFields:-text1-text2
Copied!
typeField
typeField
Type
string
The field identifier to use as the type switch. This field will be
automatically generated and prepended as the very first field. The item list
is filled automatically as well. There is no need to define this field
manually in your fields list. Useful, if you want to define multiple types
for a single table (single table inheritance).
typeField:type
Copied!
typeName
typeName
Type
string
Default
automatically generated from
name
The identifier of the new Record Type. It is automatically generated from
the name, if not defined manually.
typeName:type1
Copied!
languageAware
languageAware
Type
boolean
Default
true
If set to
false, language related fields are not created. Namely
sys_language_uid,
l10n_parent,
l10n_source and
l10n_diffsource.
# disable language supportlanguageAware:false
Copied!
workspaceAware
workspaceAware
Type
boolean
Default
true
Creates workspace related fields. Namely
t3ver_oid,
t3ver_wsid,
t3ver_state and
t3ver_stage. If
EXT:workspaces is not installed, these fields
won't be created.
# disable workspaces supportworkspaceAware:false
Copied!
editLocking
editLocking
Type
boolean
Default
true
If set to
false, the functionality to lock the editing for editors is
removed. This refers to the
editlock field.
# disable edit lock fieldeditLocking:false
Copied!
restriction
restriction
Type
array
Default
true (for all sub properties)
There are several restrictions in TYPO3, which filter records by certain
constraints.
disabled
Adds a checkbox to hide the record in the frontend.
startTime
Adds a date picker to set the start time when to display the record.
endTime
Adds a date picker to set the end time when to stop displaying the record.
userGroup
Adds a selection to choose user groups, which are allowed to view the record.
When deleting records in the TYPO3 backend, they are not really deleted in
the database. They are merely flagged as deleted. Disabling this option,
removes this safety net.
# records will be really deleted in the backendsoftDelete:false
Copied!
trackCreationDate
trackCreationDate
Type
boolean
Default
true
Tracks the timestamp of the creation date. Disabling this option removes this
information.
trackCreationDate:false
Copied!
trackUpdateDate
trackUpdateDate
Type
boolean
Default
true
Tracks the timestamp of the last update. Disabling this option removes this
information.
trackUpdateDate:false
Copied!
sortable
sortable
Type
boolean
Default
true
Tracks the order of records. Arrows will appear to sort records explicitly.
Disabling this option removes this functionality. This corresponds to the
TCA option
sortby.
sortable:false
Copied!
sortField
sortField
Type
string|array
The field identifier to use for sorting records. If set, this will disable
the
sortable option automatically. This corresponds to the TCA option
default_sortby. It is possible to define multiple sorting fields with
an array.
# simple sort by one field in ascending ordersortField:title# sorting by multiple fields with different orderssortField:-identifier:titleorder:desc-identifier:textorder:asc
Copied!
internalDescription
internalDescription
Type
boolean
Default
false
If enabled, this adds a new tab Notes with a description field. When filled
with text, a record information will be displayed in the editing view. This
corresponds with the TCA ctrl option
descriptionColumn. This field is
supposed to be used only for the backend.
internalDescription:true
Copied!
rootLevelType
rootLevelType
Type
string
Default
onlyOnPages
Restricts the place, where the record can be created. Possible values are
onlyOnPages (default),
onlyOnRootLevel and
both. This
corresponds to the TCA ctrl option
rootLevel.
rootLevelType:'onlyOnRootLevel'
Copied!
security
security
Type
array
ignoreWebMountRestriction
default false, Allows users to access records that are not in their defined web-mount, thus bypassing this restriction.
ignoreRootLevelRestriction
default false, Allows non-admin users to access records that are on the root-level (page ID 0), thus bypassing this usual restriction.
ignorePageTypeRestriction
default false (but true if table is used as
foreign_table), Allows to use the record on any kind of page type.
These are the Content Blocks Field Types, which you can use out of the box. They
cover mostly basic types. Custom Field Types can
be developed from scratch. More semantic field types may be added over time.
If you are familiar with TCA types, then you will
probably recognize most of these types. Some types have been renamed for better
clarity.
Note
The documented field options here are not complete. You are allowed to use
every option, which you can also find in the TCA
documentation. Have a look at the TCA type mapping if
you are unsure, which field type is the counterpart to the TCA type.
By default labels should be defined inside the labels.xlf file. But in
case there is only one language for the backend you may define labels directly
in the YAML configuration. Translation files have precedence over this.
If set to true, the identifier is treated as an existing field from the Core
or your own defined field in TCA. To learn more about reusing fields read
this article.
fields:identifier:bodytextuseExistingField:true
Copied!
prefixField
prefixField
Type
boolean
Default
true
If set to false, the prefixing is disabled for this field. This overrules the
global option prefixFields.
Can be used to display the field only under certain conditions.
Please have a look at the official documentation
for more information.
# Simple, only one rule.displayCond:'FIELD:identifier:=:value'
Copied!
# Multiple rules combined with AND.displayCond:AND:-'FIELD:identifier:=:value'-'FIELD:another_identifier:=:1'
Copied!
Tip
Fields used in a condition should have the column option
onChange
set to
reload.
onChange
onChange
Type
string
Can be used to trigger a reload of the Content Type when this specific
field is changed. Should be used, if a rule of
displayCond is used
for this field.
Depending on the relationship, the category relations is stored (internally)
in a different way. Possible keywords are oneToOne, oneToMany or
manyToMany (default).
maxitems
maxitems
Type
integer
Default
"0"
Maximum number of items. Defaults to a high value. JavaScript record
validation prevents the record from being saved if the limit is not satisfied.
minitems
minitems
Type
integer
Default
"0"
Minimum number of items. Defaults to 0. JavaScript record validation
prevents the record from being saved if the limit is not satisfied.
The field can be set as required by setting minitems to at least 1.
treeConfig.startingPoints
treeConfig.startingPoints
Type
string
Allows to set one or more roots (category uids), from which the categories
should be taken from.
The default value corresponds to a bit value. If you only have one checkbox
having 1 or 0 will work to turn it on or off by default. For more than one
checkbox you need to calculate the bit representation.
items
items
Type
array
Only necessary if more than one checkbox is desired. Contains the checkbox
elements as separate array items. The label can also be defined as a
LLL-reference.
XLF translation keys for items have the following convention:
<body><trans-unitid="FIELD_IDENTIFIER.items.0.label"><source>Label for first Checkbox item</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.items.1.label"><source>Label for second Checkbox item</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.items.n.label"><source>Label for nth Checkbox item</source></trans-unit></body>
Copied!
renderType
renderType
Type
string
Default
check
checkboxToggle
checkboxLabeledToggle
allowedCustomProperties
allowedCustomProperties
Type
array
Default
["itemsProcConfig"]
Sometimes it is needed to provide custom configuration for the itemsProcFunc
functionality. These extra properties need to be explicitly allowed via this
option. This option receives an array of those strings. By default, the
custom option
itemsProcConfig is allowed.
The
Collection type generates a field for
Inline-Relational-Record-Editing (IRRE), which
allows nesting of other field types. This field type allows
building structures like image sliders, accordions, tabs and so on.
Collections will automatically create custom tables and use the
identifier as table name. It is possible to override this with the
setting
table. Collections are always hidden in the List
module. Usually Collections only have one type. To realise multiple types it is
recommended to extract the definition to a separate
Record Type and use
foreign_table instead.
Custom icon
In order to define a custom icon for your Collection field, you may place an
image file inside assets folder called {identifier}.svg. So for example
if your identifier for the Collection is
my_collection, then your image
should be named my_collection.svg. Alternatively, you can also provide png
or gif files. These should be 64x64px.
Settings
Tip
All options from Record Types
can be used here as well.
Defines which field should be used as the title of the record. If not
defined, the first valid child field will be used as the label. It is
possible to define an array of fields, which will be displayed
comma-separated in the backend.
# a single field for the labellabelField:title# multiple fields will be displayed comma-separatedlabelField:-title-text
Copied!
fallbackLabelFields
fallbackLabelFields
Type
array
Defines which fields should be used as fallback, if
labelField is not
filled. The first filled field which is found will be used. Can only be used
if there is only one
labelField field defined.
# fallback fields will be used, if title from labelField is emptylabelField:titlefallbackLabelFields:-text1-text2
Copied!
table
table
Type
string
Alternative table name for the Collection. Default is
identifier with
prefix if enabled.
table:tx_vendor_my_custom_table_name
Copied!
fields
fields
Type
array
Required
true
Configures a set of fields as repeatable child objects. All fields defined in
field types are possible as children. It is also possible to further nest
Collection fields.
Minimum number of child items. Defaults to 0. JavaScript record validation
prevents the record from being saved if the limit is not satisfied.
maxitems
maxitems
Type
integer
Default
"0"
Maximum number of child items. Defaults to a high value. JavaScript record
validation prevents the record from being saved if the limit is not satisfied.
relationship
relationship
Type
string
Default
oneToMany
The relationship defines the cardinality between the relations. Possible
values are
oneToMany (default),
manyToOne and
oneToOne. In case of a [x]toOne relation, the processed field will
be filled directly with the record instead of a collection of records. In
addition,
maxitems will be automatically set to
1.
appearance.collapseAll
appearance.collapseAll
Type
bool|null
Default
null
Default (null): Last collapsed/expanded state is remembered
true: Show all child records collapsed
false: Show all child records expanded
appearance.levelLinksPosition
appearance.levelLinksPosition
Type
string
Default
top
Defines where to show the "New record" link in relation to the child records.
Valid keywords are
top,
bottom and
both.
foreign_table
foreign_table
Type
string (table)
It is possible to reference another table instead of creating a new one. This
table can be defined by another Content Block,
but can also be an existing table defined by the Core or another extension.
Note
When you use
foreign_table it is not possible to define
fields anymore. They will not be evaluated.
foreign_field
foreign_field
Type
string (field)
It is possible to override the field name pointing to the parent record. Per
default it is called
foreign_table_parent_uid. This corresponds with
the TCA option foreign_field.
shareAcrossTables
shareAcrossTables
Type
boolean
Default
false
Allows to reference a Record Type across multiple tables, if
foreign_table
is used.
Make sure to add this to every Collection, which shares the foreign_table.
This will create a new field called
tablenames. It corresponds to the
TCA option foreign_table_field.
The field name can be overridden by defining
foreign_table_field explicitly.
This option allows you to restrict possible record types for the type
selector of the child record. The order of definition is used to sort the
items. The first item in the list will always be the default type.
allowedRecordTypes:-text-images
Copied!
overrideType
overrideType
Type
array
Default
[]
Type Overrides can be used to override the Record Definition in the context of
as single field. This option only makes sense, if you don't define
fields,
but an external
foreign_table, that you want to override.
Refer to the API documentation if you want to
learn more.
Renders a select box with static values next to the input field. When
a value is selected in the box, the value is transferred to the field. Keys:
items (array)
An array with selectable items. Each item is an array with the first being
the label in the select drop-down (LLL reference possible), and the second
being the value transferred to the input field.
The
Email type creates an input field, which is validated against an
email pattern. If the input does not contain a valid email address, a flash
message warning will be displayed.
name:example/emailfields:-identifier:emailtype:Emailautocomplete:truedefault:'developer@localhost.de'placeholder:'Enter your email address'required:true
Copied!
File
The
File type generates a field for file relations.
If enabled, an additional image or media palette will be rendered. For
image files it consists of the additional fields crop, alternative and
link. For audio and media files an additional autoplay field is added.
For other file types, like plain text, this option has no effect. Disable
this option, if you don't need these additional fields.
allowed
allowed
Type
string|array
Default
''
Possible values: common-image-types, common-media-types or your custom
list of file types.
maxitems
maxitems
Type
integer
Default
99999
Maximum number of child items. Defaults to a high value. JavaScript record
validation prevents the record from being saved if the limit is not satisfied.
minitems
minitems
Type
integer
Default
"0"
Minimum number of child items. Defaults to 0. JavaScript record validation
prevents the record from being saved if the limit is not satisfied.
The field can be set as required by setting minitems to at least 1.
relationship
relationship
Type
string
Default
oneToMany
The relationship defines the cardinality between the relations. Possible
values are
oneToMany (default),
manyToOne and
oneToOne. In case of a [x]toOne relation, the processed field will
be filled directly with the file reference instead of a collection of file
references. In addition,
maxitems will be automatically set to
1.
cropVariants
cropVariants
Type
array
Default
[]
It is possible to define crop variants for this specific field and Content
Block. This documentation only covers the most basic configuration. Refer to
the TCA documentation
for a complete overview of possibilities.
Example configuration below. The aspect ratios can be defined as a float
value or a fraction. Only the simple division operation a / b is allowed.
Type Overrides can be used to override the File Definition in the context of
as single field. Refer to the API documentation
if you want to learn more.
The
FlexForm field allows you to group multiple fields into one database
column. It is mostly used to store configuration options, rather than actual
content. By using FlexForm it is possible to conserve the database, but it also
has its limitations. For example nesting of Collections is disallowed.
Sheets are used to further group FlexForm fields into separate tabs. Note that
you need at least 2 Sheets for a tab navigation to appear in the backend. This
is purely cosmetical and, like Palettes and Tabs, has no effect on frontend
rendering.
Warning
Due to the fact that FlexForm is stored as XML in the database, changing the
Sheet identifiers (or moving fields into other Sheets) retrospectively is
destructive. You will lose your data.
XLF translation keys for Sheets have the following convention:
<body><trans-unitid="FIELD_IDENTIFIER.sheets.SHEET_IDENTIFIER.label"><source>Label for Sheet</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.sheets.SHEET_IDENTIFIER.description"><source>Description for Sheet</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.sheets.SHEET_IDENTIFIER.linkTitle"><source>Link title for Sheet</source></trans-unit></body>
Copied!
Section
type: Section
Sections are like Collections for FlexForm. The difference is, that you can't
reference foreign types. Instead you define anonymous structures, which are only
available for this specific FlexForm field.
A Section requires you to define at least one
Container, which holds
the available fields. You can have multiple Containers with different fields. If
you define more than one Container, the editing interface will display multiple
buttons to choose from.
XLF translation keys for Sections and Containers have the following convention:
<body><trans-unitid="FIELD_IDENTIFIER.sections.SECTION_IDENTIFIER.title"><source>Label for Section</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.sections.SECTION_IDENTIFIER.container.CONTAINER_IDENTIFIER.title"><source>Label for Container</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.sections.SECTION_IDENTIFIER.container.CONTAINER_IDENTIFIER.FIELD_IDENTIFIER.label"><source>Label for field in Container</source></trans-unit></body>
Copied!
Folder
The
Folder type enables to select one or more folders. This field type
is resolved to an array of
\TYPO3\CMS\Core\Resource\Folder objects.
Enables to set an entrypoint, from which to select folders by default.
maxitems
maxitems
Type
integer
Maximum number of items. Defaults to a high value. JavaScript record
validation prevents the record from being saved if the limit is not satisfied.
minitems
minitems
Type
integer
Minimum number of items. Defaults to 0. JavaScript record validation
prevents the record from being saved if the limit is not satisfied.
The field can be set as required by setting minitems to at least 1.
relationship
relationship
Type
string
Default
oneToMany
The relationship defines the cardinality between the relations. Possible
values are
oneToMany (default),
manyToOne and
oneToOne. In case of a [x]toOne relation, the processed field will
be filled directly with the folder instead of a collection of folders. In
addition,
maxitems will be automatically set to
1.
In most cases you want to retrieve the files within the folders. To achieve
this, you have to access the files via the
getFiles() method. In Fluid
this looks like this for a field with
identifier: folder.
Hint
If you've defined
relationship: manyToOne, then you can omit the
outer
f:for loop.
Size for the input field. Min value is 10 and max value is 50.
rows
rows
Type
integer
Default
5
Amount of rows for the textarea. Min value is 1 and max value is 20.
required
required
Type
boolean
Default
false
If set, the Json textarea needs to be filled.
readOnly
readOnly
Type
boolean
Default
false
If set, the Json textarea is read only.
enableCodeEditor
enableCodeEditor
Type
boolean
Default
true
In case
enableCodeEditor is set to
true, which is the default
and the system extension t3editor is installed and active, the JSON value
is rendered in the corresponding code editor. Otherwise it is rendered in a
standard textarea HTML element.
The
Linebreak field is used inside Palette fields to add a manual
linebreak. Otherwise all fields within a Palette are displayed next to each
other. Note: Contrary to all other field types, Linebreaks don't need an
identifier.
Settings
ignoreIfNotInPalette
ignoreIfNotInPalette
Type
boolean
Default
false
Normally, linebreaks can only be defined inside of a palette. With this flag
set to true, linebreaks can also appear outside of palettes (but do nothing).
This is especially useful in combination with
Basics when you want a set of fields to be used
both in palettes and on root level.
For in-depth information about palettes and linebreaks refer to the TCA documentation.
Link
The
Link type creates a field with a link wizard. It is possible to link
to pages, files or even records (if configured). This field is resolved to an
object of type
\TYPO3\CMS\Core\LinkHandling\TypolinkParameter .
As this field is an object of type
\TYPO3\CMS\Core\LinkHandling\TypolinkParameter
you have to check for the property
url to determine whether the field is
set or not.
Note
Alternatively, you can set the field
nullable: true. In this case
the value will resolve to null if not set.
The
Palette field is used to group other fields. Grouped fields will be
displayed next to each other rather than below each other.
Pre-defined palettes as for example the
TYPO3/Header palette for Content
Elements are available as Basics.
Warning
Palettes are defined on table definition level. This means if you define a
Palette with identifier "my_palette" in Content Block A and Content Block B
of the same type (e.g. ContentElement), then they will override each other.
So make sure to have unique identifiers. An exception to this rule is using
Palettes inside Basics. Then you can share them,
while the configuration always stays the same, as they are centrally defined.
Labels
XLF translation keys for Palettes have the following convention:
<body><trans-unitid="palettes.PALETTE_IDENTIFIER.label"><source>Label for Palette</source></trans-unit><trans-unitid="palettes.PALETTE_IDENTIFIER.description"><source>Description for Palette</source></trans-unit><trans-unitid="COLLECTION_IDENTIFIER.palettes.PALETTE_IDENTIFIER.label"><source>Label for Palette in Collection</source></trans-unit><trans-unitid="COLLECTION_IDENTIFIER1.COLLECTION_IDENTIFIER2.palettes.PALETTE_IDENTIFIER.label"><source>Label for Palette in nested Collection</source></trans-unit></body>
The
Radio type creates a set of radio buttons. The value is typically
stored as integer value, each radio item has one assigned number, but it can be
a string, too.
XLF translation keys for items have the following convention:
<body><trans-unitid="FIELD_IDENTIFIER.items.1.label"><source>Label for item with value 1</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.items.2.label"><source>Label for item with value 2</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.items.VALUE.label"><source>Label for item with value VALUE</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.items.label"><source>Label for item with empty value</source></trans-unit></body>
Copied!
allowedCustomProperties
allowedCustomProperties
required
false
type
array
default
["itemsProcConfig"]
Sometimes it is needed to provide custom configuration for the itemsProcFunc
functionality. These extra properties need to be explicitly allowed via this
option. This option receives an array of those strings. By default, the
custom option
itemsProcConfig is allowed.
This table can be defined by another Content Block, but can also be an
existing table defined by the Core or another extension.
maxitems
maxitems
Type
integer
Maximum number of items. Defaults to a high value. JavaScript record
validation prevents the record from being saved if the limit is not satisfied.
minitems
minitems
Type
integer
Minimum number of items. Defaults to 0. JavaScript record validation
prevents the record from being saved if the limit is not satisfied.
The field can be set as required by setting minitems to at least 1.
relationship
relationship
Type
string
Default
oneToMany
The relationship defines the cardinality between the relations. Possible
values are
oneToMany (default),
manyToOne and
oneToOne. In case of a [x]toOne relation, the processed field will
be filled directly with the record instead of a collection of records. In
addition,
maxitems will be automatically set to
1.
For this you need the following setting according to the TCA documentation.
fieldWizard:selectIcons:disabled:false
Copied!
XLF translation keys for items have the following convention:
<body><trans-unitid="FIELD_IDENTIFIER.items.one.label"><source>Label for item with value one</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.items.two.label"><source>Label for item with value two</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.items.VALUE.label"><source>Label for item with value VALUE</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.items.label"><source>Label for item with empty value</source></trans-unit></body>
Copied!
default
default
Type
string
Default value set if a new record is created.
maxitems
maxitems
Type
integer
Maximum number of child items. Defaults to a high value. JavaScript record
validation prevents the record from being saved if the limit is not satisfied.
If maxitems ist set to greater than 1, multiselect is automatically enabled.
minitems
minitems
Type
integer
Minimum number of child items. Defaults to 0. JavaScript record validation
prevents the record from being saved if the limit is not satisfied.
The field can be set as required by setting minitems to at least 1.
relationship
relationship
Type
string
Default
oneToMany
Note
This can only be used in combination with
foreign_table.
The relationship defines the cardinality between the relations. Possible
values are
oneToMany (default),
manyToOne and
oneToOne. In case of a [x]toOne relation, the processed field will
be filled directly with the record instead of a collection of records. In
addition,
maxitems will be automatically set to
1. If the
renderType is set to
selectSingle, a relationship
manyToOne is automatically inferred.
dbFieldLength
dbFieldLength
Type
int
Default
255
This option can be used to set an alternative size for the database
varchar column. The default size is 255.
allowedCustomProperties
allowedCustomProperties
Type
array
Default
["itemsProcConfig"]
Sometimes it is needed to provide custom configuration for the itemsProcFunc
functionality. These extra properties need to be explicitly allowed via this
option. This option receives an array of those strings. By default, the
custom option
itemsProcConfig is allowed.
name:example/selectfields:-identifier:selecttype:SelectrenderType:selectSingledefault:'one'items:-label:'The first'value:one-label:'The second'value:two-label:'The third'value:threeforeign_table:pagesforeign_table_where:'AND {#pages}.{#pid} = 123 ORDER BY uid'
Copied!
Select multiple:
name:example/selectfields:-identifier:select_side_by_sidetype:SelectrenderType:selectMultipleSideBySidedefault:'one'minitems:1maxitems:3items:-label:'The first'value:one-label:'The second'value:two-label:'The third'value:threeforeign_table:pagesforeign_table_where:'AND {#pages}.{#pid} = 123 ORDER BY uid'
Copied!
Select tree:
name:example/selectfields:-identifier:select_treetype:SelectrenderType:selectTreesize:5foreign_table:'pages'foreign_table_where:'ORDER BY pages.uid'treeConfig:parentField:pid
Copied!
Select with icons:
name:example/selectfields:-identifier:select_iconstype:SelectrenderType:selectSinglefieldWizard:selectIcons:disabled:falsedefault:'image-left'items:-label:'Image beside text (left)'value:image-lefticon:content-beside-text-img-left-label:'Image beside text (right)'value:image-righticon:content-beside-text-img-right-label:'Image above text (center)'value:image-aboveicon:content-beside-text-img-above-cent
Copied!
SelectNumber
The
SelectNumber type generates a simple select field, which only allows
numbers / integers.
For this you need the following setting according to the TCA documentation.
fieldWizard:selectIcons:disabled:false
Copied!
XLF translation keys for items have the following convention:
<body><trans-unitid="FIELD_IDENTIFIER.items.1.label"><source>Label for item with value one</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.items.2.label"><source>Label for item with value two</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.items.VALUE.label"><source>Label for item with value VALUE</source></trans-unit></body>
name:example/select-numberfields:-identifier:select_number_iconstype:SelectNumberfieldWizard:selectIcons:disabled:falsedefault:2items:-label:'Image beside text (left)'value:1icon:content-beside-text-img-left-label:'Image beside text (right)'value:2icon:content-beside-text-img-right-label:'Image above text (center)'value:3icon:content-beside-text-img-above-cent
Copied!
Slug
The
Slug type generates a slug field, which generates a unique string
for the record.
Options related to the generation of the slug. Keys:
fields (array)
An array of fields to use for the slug generation. Adding multiple fields
to the simple array results in a concatenation. In order to have fallback
fields, a nested array must be used.
The
Tab field can be used to create a new tab in the editor interface.
It needs an unique identifier and can be placed between any two fields. Note:
Prohibited inside Palettes.
Pre-defined tabs as for example the
TYPO3/Appearance tab for Content
Elements are available as Basics.
Labels
XLF translation keys for Tabs have the following convention:
<body><trans-unitid="tabs.TAB_IDENTIFIER"><source>Label for Tab</source></trans-unit><trans-unitid="COLLECTION_IDENTIFIER.tabs.TAB_IDENTIFIER"><source>Label for Tab in Collection</source></trans-unit><trans-unitid="COLLECTION_IDENTIFIER1.COLLECTION_IDENTIFIER2.tabs.TAB_IDENTIFIER"><source>Label for Tab in nested Collection</source></trans-unit></body>
Value for the maxlength attribute of the <input> field. Javascript
prevents adding more than the given number of characters.
min
min
Type
integer
Value for the minlength attribute of the <input> field. Javascript
prevents adding less than the given number of characters. Note: Empty values
are still allowed. Use in combination with
required if this should be
a non-empty value.
placeholder
placeholder
Type
string
Placeholder text for the field.
required
required
Type
boolean
Default
false
If set, the field becomes mandatory.
size
size
Type
integer
Abstract value for the width of the <input> field.
valuePicker
valuePicker
Type
array
Renders a select box with static values next to the input field. When
a value is selected in the box, the value is transferred to the field. Keys:
items (array)
An array with selectable items. Each item is an array with the first being
the label in the select drop-down (LLL reference possible) and the second
being the value transferred to the input field.
Example:
valuePicker:items:-['Want to join our team? Take the initiative!','Job offer general']-['We are looking for ...','Job offer specific']
Abstract value for the height of the <textarea> field. Max value is 20.
required
required
Type
boolean
Default
false
If set, the field will become mandatory.
enableRichtext
enableRichtext
Type
boolean
Default
false
If set to true, the system renders a Rich Text Editor if that is enabled for
the editor (default: yes), and if a suitable editor extension is loaded
(default: rte_ckeditor).
If either of these requirements is not met, the system falls back to
a <textarea> field.
richtextConfiguration
richtextConfiguration
Type
string
The value is a key in
$GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets']
array and specifies the YAML configuration source field used for that RTE
field. It does not make sense without having property
enableRichtext
set to true.
Extension rte_ckeditor registers three presets: default, minimal and
full and points to YAML files with configuration details.
Integrators may override for instance the default key to point to an own
YAML file which will affect all core backend RTE instances to use that
configuration.
If this property is not specified for an RTE field, the system will fall back
to the default configuration.
The following examples are for templating with Fluid. Content Blocks brings some
additional features like own variables and ViewHelpers with it.
Accessing variables
Inside your frontend.html or backend-preview.html file you can access the
properties of your Content Element as usual by the
{data} variable.
This variable, however, is special. It has real superpowers!
In contrast to the usual array, we are dealing with an object here. This allows
us to magically access our own custom properties very easily. Normally you would
access the processed properties. This is done by simply accessing the desired
property like
{data.header}. The raw properties have to be accessed by
{data.rawRecord.some_field}.
All fields with relations are resolved automatically to an array. This includes
Collection, Select, Relation, File, Folder, Category and FlexForm
fields. There is no need to provide additional DataProcessors for them.
Content Blocks applies relation resolving for you (recursively!).
Have a look at this code example to grasp what's possible:
<!-- Any property, which is available in the Record (like normal) -->
{data.title}
{data.uid}
{data.pid}
<!-- Normal access to custom relational properties --><f:foreach="{data.collection1}"as="item">{item.title}</f:for><!-- Recursive access to custom relational properties --><f:foreach="{data.collection1}"as="item"><f:foreach="{item.categories}"as="category">
{category.title}
</f:for></f:for><!-- Language related properties -->
{data.languageId}
{data.languageInfo.translationParent}
{data.languageInfo.translationSource}
<!-- The overlaid uid -->
{data.overlaidUid}
<!-- Types are a combination of the table name and the Content Type name. --><!-- Example for table "tt_content" and CType "textpic": --><!-- "tt_content" (this is basically the table name) -->
{data.mainType}
<!-- "textpic" (this is the CType) -->
{data.recordType}
<!-- "tt_content.textpic" (Combination of mainType and record type, separated by a dot) -->
{data.fullType}
<!-- System related properties -->
{data.systemProperties.deleted}
{data.systemProperties.disabled}
{data.systemProperties.lockedForEditing}
{data.systemProperties.createdAt}
{data.systemProperties.lastUpdatedAt}
{data.systemProperties.publishAt}
{data.systemProperties.publishUntil}
{data.systemProperties.userGroupRestriction}
{data.systemProperties.sorting}
{data.systemProperties.description}
<!-- Computed properties depending on the request context -->
{data.computedProperties.versionedUid}
{data.computedProperties.localizedUid}
{data.computedProperties.requestedOverlayLanguageId}
{data.computedProperties.translationSource} <!-- Only for pages, contains the Page model --><!-- Workspace related properties -->
{data.versionInfo.workspaceId}
{data.versionInfo.liveId}
{data.versionInfo.state.name}
{data.versionInfo.state.value}
{data.versionInfo.stageId}
<!-- To access the raw (unprocessed) database record use `rawRecord` -->
{data.rawRecord.some_field}
Note that we are omitting _processed when accessing properties, even
though you might think this would be correct due to the debug output.
Frontend & backend
Content Blocks allows you to provide a separate template for the frontend and
the backend out of the box. The variables are the same for both templates, and
while using the asset ViewHelpers, you can also ship JavaScript and CSS as you
need. The main goal behind this is, that you can provide a better user
experience for the editors. With this feature, there is the possibility to
provide nearly the same layout in the frontend and the backend, so the editors
easily find the element they want to edit.
The frontend template is located in templates/frontend.html and the backend
template in templates/backend-preview.html.
Asset ViewHelpers
Content Blocks provides a new AssetPathViewHelper to access assets from within
the current Content Block in the template.
<f:comment><!-- Include the assets/frontend.css stylesheet --></f:comment><f:asset.cssidentifier="myCssIdentifier"href="{cb:assetPath()}/frontend.css"/><f:comment><!-- Include the assets/frontend.js script --></f:comment><f:asset.scriptidentifier="myJavascriptIdentifier"src="{cb:assetPath()}/frontend.js"/>
Copied!
Alternatively, you can set
name manually.
<f:comment><!-- The name of the Content Block is set explicitly --></f:comment><f:asset.scriptidentifier="myJavascriptIdentifier"href="{cb:assetPath(name: 'vendor/name')}/frontend.js"/>
Copied!
LanguagePath ViewHelper
Content Blocks provides a LanguagePathViewHelper to retrieve the LLL-path for
the labels.xlf file of the current Content Block.
Partials are a very useful feature of Fluid. You can use them to split up your
templates into smaller parts. If you want to use a partial in a Content Block,
you can create a subdirectory partials in the templates directory and place
your partials there.
This part is automatically added, but you can also
extend or overwrite this TypoScript configuration
in your sitepackage.
Analogous to partials, you can also use layouts. You can create a subdirectory
layouts in the templates directory and place your layouts there. The
configuration is added automatically, but you can also extend or overwrite the
TypoScript configuration in your sitepackage. Afterwards you can use your
layouts as usual in Fluid.
Shareable resources
There is the technical possibility to use resources from the whole TYPO3 setup
(e.g. translations, scripts, or partials from other extensions), but we do not
recommend to do so. Content Blocks are intended to work independent of external
resources so they can be easily copy-pasted between projects. Be aware of this
downside, when you add dependencies to your Content Block.
API
This section provides more indepth information about the Core functionalities
of Content Blocks.
It is also possible to translate the
items option of
Select, Radio
and Checkbox fields.
The convention is best explained by examples. Have a look at the example
beneath.
Note
Labels defined in labels.xlf will always override
label defined in
config.yaml.
Workflow
The recommended workflow is defining the
label in the config.yaml first.
When you are done, you run the command to
auto-generate the labels.xlf file. After that you can remove the labels from the
yaml definition. You can also skip the first step and generate the xlf without
defining labels first. This will add the
identifier as label and you can
adjust it afterwards. Either way, it is recommended to maintain a labels.xlf
file so you don't mix labels with configuration.
Convention schema
The schema below displays most common conventions. Text in uppercase refers to
values defined in
identifier.
Tip
You don't have to remember all these rules. The command
content-blocks:language:generate creates
the labels.xlf file with all available keys for you.
<?xml version="1.0"?><xliffversion="1.2"xmlns="urn:oasis:names:tc:xliff:document:1.2"><filedatatype="plaintext"original="labels.xlf"source-language="en"product-name="example"><header/><body><!-- Title and description of the Content Type --><trans-unitid="title"><source>This is the Content Type backend title</source></trans-unit><trans-unitid="description"><source>This is the Content Type backend description</source></trans-unit><!-- Field labels and descriptions for the backend --><trans-unitid="FIELD_IDENTIFIER.label"><source>This is the backend label for FIELD_IDENTIFIER</source></trans-unit><trans-unitid="FIELD_IDENTIFIER.description"><source>This is the backend description for FIELD_IDENTIFIER</source></trans-unit><!-- Collections add another nesting level --><trans-unitid="COLLECTION_IDENTIFIER.FIELD_IDENTIFIER.label"><source>This is the backend label for FIELD_IDENTIFIER in Collection COLLECTION_IDENTIFIER</source></trans-unit><!-- Palette labels and descriptions --><trans-unitid="palettes.PALETTE_IDENTIFIER.label"><source>Label for Palette</source></trans-unit><trans-unitid="palettes.PALETTE_IDENTIFIER.description"><source>Description for Palette</source></trans-unit><!-- Palettes inside Collections --><trans-unitid="COLLECTION_IDENTIFIER.palettes.PALETTE_IDENTIFIER.label"><source>Label for Palette in Collection</source></trans-unit><trans-unitid="COLLECTION_IDENTIFIER1.COLLECTION_IDENTIFIER2.palettes.PALETTE_IDENTIFIER.label"><source>Label for Palette in nested Collection</source></trans-unit><!-- Tab labels --><trans-unitid="tabs.TAB_IDENTIFIER"><source>Label for Tab</source></trans-unit><!-- Tab labels inside Collections --><trans-unitid="COLLECTION_IDENTIFIER.tabs.TAB_IDENTIFIER"><source>Label for Tab in Collection</source></trans-unit><trans-unitid="COLLECTION_IDENTIFIER1.COLLECTION_IDENTIFIER2.tabs.TAB_IDENTIFIER"><source>Label for Tab in nested Collection</source></trans-unit></body></file></xliff>
Copied!
Backend Preview
Backend previews allow you to provide a preview for your frontend in the
backend. Previews are a very important part when creating custom content types
for your editors. They are the only way to help them navigate through the
jungle fo elements in the page module.
A preview can be defined by creating a file templates/backend-preview.html
inside your Content Block. This file is already created for you, if you use the
kickstart command.
Content Elements
By default, TYPO3 comes with a standard preview renderer for Content Elements.
However, it is specialized in rendering the preview of Core Content Elements.
This means only Core fields like
header,
subheader or
bodytext are considered. Therefore, it is advised to provide an own
preview for custom Content Elements.
Changed in version 1.1
Previews for Content Elements now must define the layout
Preview and
any of the sections
Header,
Content or
Footer.
A Content Element preview consists of three parts: The header, the content area
and the footer. By defining the appropriate section it is possible to override
the standard preview rendering of TYPO3. If a section is omitted, the fallback
rendering from TYPO3 will be used instead. You can also include CSS just like in
the frontend with
f:asset.css View Helper.
Page Types
Previews for Page Types are displayed at the top of
the content area and beneath the page title. Unlike Content Elements, you don't
have to define any sections.
<f:comment>Provide the identifier of the child Collection to render a grid preview</f:comment><f:renderpartial="PageLayout/Grid"arguments="{data: data, identifier: 'tabs_item'}"/>
Copied!
Note
In backend context, all hidden relations like Collections or file references
are displayed by default. Thus, the integrator should style those hidden
elements accordingly or simply not render them.
<!-- Hidden relations like Collections --><f:foreach="{data.relations}"as="item"><f:ifcondition="{item.systemProperties.disabled}"><!-- Style or hide --></f:if></f:for><!-- Hidden file references --><f:foreach="{data.images}"as="file"><f:ifcondition="{file.properties.hidden}"><!-- Style or hide --></f:if></f:for>
Basics, also known as partials or mixins, are used to have a pre-defined set
of fields that can be used to better organize your config.yaml file and
to reduce redundancy by sharing them between multiple Content Blocks.
Note
It is important to understand that Basics are a very simple "search & replace"
kind of mechanic. Once included in your Content Block they act like they were
defined there directly. This also means that it is normally required to
re-define the label in every Content Block. Hence it is recommended to reference
labels with the full LLL:EXT path.
Warning
Be cautious when using Basics in multiple Content Blocks with
prefixing enabled. This may lead to the
database schema creating one new column for each included field. It is
recommended to either use fixed prefixes with
prefixType: vendor or
to disable the prefix for each field in the Basic with
prefixField: false.
There are two different ways of using Basics.
Basics as additional fields
The first way of using Basics is to have them added after the
fields
array of your Content Block. This is useful if you want to have a set of fields
that are always available for your Content Block.
This is an example on how to add the classic Fluid Styled Content Appearance
Tab and the additional Links palette.
You can add as many Basics as you need. Note, that all Basics are simply
concatenated onto each other. Be careful, not to create an invalid state by
gluing incompatible Basics together.
Basics as field type
The second way is to use Basics directly in the
fields array. This can
be done by using the according Basic
identifier and the type
Basic.
It's not possible to override Basics once they are included as a field type,
because they can contain multiple fields, palettes or tabs. If a use case
arises, where you need a new "base field", then you probably want to create
a new custom field type. This requires PHP
code, however.
Pre-defined Basics
List of the standard Basics shipped with Content Blocks. Feel free to copy them
into your own project and adjust them as you need.
TYPO3/Header
TYPO3/Appearance
TYPO3/Links
TYPO3/Categories
Define own Basics
You can define your own Basics by placing one or more YAML files into
ContentBlocks/Basics. The name of the YAML file can be chosen freely. It is
also possible to create sub-folders in order to structure your Basics.
The
fields part is exactly the same as in the config.yaml. Here
you can define a Tab, a Palette or simply a set of fields.
The most practical way to use Basics is to use pre-defined tabs as the global
basics option, so they are always added at the end. The field type
Basic is used best as a palette. There you can define a set of fields,
which you always need e.g. various header fields.
Tip
Unlike Content Block names, it is not mandatory to provide a vendor name for
your Basic identifier. However, it is recommended to avoid using too generic
names to avoid conflicts.
Nested Basics
It is also possible to nest Basics. So if Basic A refers to Basic B in the
fields list, then this will be resolved, too. Be careful to not create an
infinite loop by circular or self-references. This will be detected
automatically, if a high nesting level is reached.
Content Elements are a special Content Type in TYPO3. The basic structure is
already defined in the TYPO3 Core. Content Blocks only adds new types to it.
The Appearance tab will then be added after all your custom fields.
About TYPO3 Content Elements
Defining "Content Elements" in TYPO3 is hard and the learning curve is steep.
Despite the possibility to customize TYPO3 to ones needs, most people used the
standard Content Elements shipped with TYPO3 Core. This is, of course,
convenient, but has several drawbacks as soon as customizations are needed. One
override follows the next, until the Core Content Element has more modifications
than the initial implementation. This is where the concept of Content Blocks
stepped in. First of all, it makes creating new types a no-brainer, so that the
temptation to overrides is weakened. Secondly, for the same reason, it makes
standard Content Elements almost obsolete. If you need a specific element, you
can always copy/paste it into your project as your own element. Owning the
elements means there will be no breaking changes to the Fluid templates.
File Types are a special Content Type in TYPO3. They relate to the field type
file. Every time you create a new file reference, the
type of the child record is automatically determined by the file mime type.
There is a fixed amount of types:
text
image
audio
video
application
TYPO3 already provides a basic palette of fields, including an image
manipulation module to handle crop areas. Now, if you happen to need an
additional custom field, you have to completely override the type definition. A
minimal example looks like this:
In this example, we re-define the
image file type. For the most part
everything is identical to the standard definition. We only added another custom
field
example_custom_field right before the crop field. Using this
method, you have full control over the available fields and their position.
Note
This is a global definition, used in your entire TYPO3 installation. Only
use it, if you want to modify the arrangement of fields globally. Also,
don't use this in third party extensions. Better provide a
Basic,
which the extension user should include in his project.
Tip
In case you only want to re-define a file definition in the context of
another element, then use Type Overrides instead.
Note
If you have the option extendedPalette
set to
false, this definition won't be displayed and you will get
the basic palette instead.
Nested Content Elements
It is possible to nest Content Elements within Content Blocks.
By default, TYPO3 would render those nested elements within the TYPO3 Page
Module in the backend and the frontend output. Content Blocks delivers an API
and integration for common setups to prevent this unwanted behaviour.
Note
This will not replace proper grid extensions like EXT:container, as this
solution does not provide drag and drop or creation of new child elements
in the Page Module.
How to create nested Content Elements
In order to have nested Content Elements, we need to make use of the field type
Collection. This field type allows us to have a
relation to another table. In our case
tt_content.
This config creates a field, where you can create new Content Elements within
your root Content Element. With the option
allowedRecordTypes it is possible
to restrict certain Content Types.
Render nested Content Elements in the frontend
There are two ways to render nested Content Elements. The first one is to reuse
the basic rendering definition of the child element. This is already
pre-rendered in the Fluid variable {data._grids.identifier}. This variable
contains all relations which might have a frontend rendering definition defined
in TypoScript. Normally, these are only Content Elements.
<f:foreach="{data._grids.tabs_item}"as="item"iteration="i"><f:comment><!-- {item.data} contains the Content Block data object. --></f:comment><divclass="tab-item"data-uid="{item.data.uid}"><f:comment><!-- {item.content} contains the rendered html. --></f:comment><f:format.raw>{item.content}</f:format.raw></div></f:for>
Copied!
Note
This is the same as triggering the rendering with
f:cObject view
helper:
The second method is to define an alternative rendering within your Fluid
template. This means you can have a default rendering definition for your
Content Element, when used as a root Content Element and an alternative one if
used as a child. This method is a lot more flexible, but requires a little bit
more work.
You can also use global partials for the
second method to have less duplication.
Render nested Content Elements in the backend
Similarly to frontend rendering, it's also possible to render nested content in
the backend. For this Content Blocks provides ready to use Fluid partials which
are able to render backend previews the same way the Core page layout does it.
The partial is called PageLayout/Grid and accepts your current Content Block
data object as well as the identifier of the Collection field, which you want
to render.
The partial renders the grid layout from the Core.
This preview is limited to control buttons like edit, delete and hide. No
support for drag and drop or creation of new child elements is given.
If you forgot to set these options when it would be necessary, the nested elements will
appear twice in the backend. If you are unsure, always enable these options. In worst
case you will have redundant information in the database.
First, shareAcrossFields is needed, if you have two or more Collections with a
sharedforeign_table inside the same Content Block. In this case tt_content.
So for example you have a field
nested_elements_a and
nested_elements_b
inside the same Content Element. In order to distinguish them, a new database column
will be created to track the field name of the corresponding Collection.
name:example/nested-contentfields:-identifier:headeruseExistingField:true-identifier:nested_elements_atype:Collectionforeign_table:tt_content# This table is used twice here.shareAcrossFields:true# That's why shareAcrossFields must be enabled.-identifier:nested_elements_btype:Collectionforeign_table:tt_content# This table is used twice hereshareAcrossFields:true# That's why shareAcrossFields must be enabled.
Copied!
The option shareAcrossTables on the other hand is only necessary, if the same
foreign_table is shared across multiple tables. So for example you use
nested Content Elements in a Content Element and a second time in a custom
Record Type like news.
name:example/nested-contentfields:-identifier:headeruseExistingField:true-identifier:nested_elementstype:Collectionforeign_table:tt_content# This table is also used in newsshareAcrossTables:true# That's why shareAcrossTables must be enabled.
name:example/newstable:my_news_tablefields:-identifier:titletype:Text-identifier:nested_elementstype:Collectionforeign_table:tt_content# This table is also used in nested-contentshareAcrossTables:true# That's why shareAcrossTables must be enabled.
Copied!
Of course, if both cases are true, both options must be enabled.
When to use nested Content Elements vs. container extensions
Many problems can be solved with either solution. However, as a rule of thumb,
as soon as you need dynamic multi-grid components for your page, you are better
off using something like EXT:container. For example "Two-Column container",
"1-1-2 Container" or "Wrapper Container".
On the other hand, if you create a self-contained element, which should hold
child Content Element relations and these child elements are unlikely to be
moved elsewhere, you might be better off using simple nested Content Elements.
An example could be a "Tab module" or a "Content Carousel".
Concept
The nesting is done via one database field
holding a reference to the parent Content Element. The
colPos column will
always be 0, which is also the reason why it would otherwise be rendered by
TYPO3 even though created within another Content Element.
Extensions like EXT:container work completely different as they assign a
specific colPos value to the child elements.
Preventing output in frontend
Output in frontend is prevented by extending
styles.content.get.where
condition. This is done via
postUserFunc, which will extract all
defined parent reference columns. Those are added to the SQL statement in order
to prevent fetching any child elements.
Note
If you do not build upon
styles.content.get or the
PageContentFetchingProcessor ,
you need to integrate the logic yourself. The necessary API providing all
the columns is available via
TYPO3\CMS\ContentBlocks\UserFunction\ContentWhere->extend.
This can be used to apply the same approach to
DatabaseQueryProcessor .
TYPO3 provides an event to alter the SQL query to fetch content for the Page
Module in the backend. Content Blocks adds an event listener to apply the same
logic as for the frontend.
Page Types are a special Content Type in TYPO3. The basic structure is
already defined in the TYPO3 Core. Content Blocks only adds new types to it. A
minimal Page Type looks like this:
This will create a new Page Type entry above the page tree, which you can drag
and drop as usual. Your custom fields will be added after the nav_title field.
SEO fields will be automatically added, if you have the SEO system extension
installed.
About TYPO3 doktypes
Page Types (also known as doktypes) do exist since the beginning of time.
However, it was always hard to create custom ones and there were no extensions
to help with creating them as they exist for Content Elements. Due to this
circumstances you might not be familiar with the concept of custom Page Types.
Basically, you can create a variation which is different from the standard page
type and add special properties to it. These can be used in templates or even
custom plugins. This opens up many possibilities and can enhance user experience
in the backend.
When to use Page Types
Page Types are best suited for cases where you have a set of pages with common
properties like a teaser text, image or date. The best example is a news article
page. These most often have required fields which are always displayed at a
fixed position, like an author at the bottom of the page. These fields should
be included as page properties rather than specifically defined Content
Elements. The article itself however, should be composed of various Content
Elements. This approach also opens up the possibility to use your Page Types in
plugins. See the blog extension
which utilises this concept.
Frontend template
There is no default frontend template for Page Types, as this depends heavily
on your setup. Typically the site package extension of your site defines the
page templates depending on the selected page layout.
Just like for Content Elements, you can define an backend-preview.html file
to create a preview of your Page Type. This can be used to preview custom
properties and to link directly to them. To make them prettier it is advised to
utilise CSS bootstrap classes like card.
Page Types can have special states like disabled or hidden in menu. Depending
on this state, the icon changes or is overlaid with another icon. As for now,
the "hide in menu" and "is site root" states can be supplied via custom icons.
Put an icon with the name icon-hide-in-menu.svg and one with
icon-root.svg inside your assets folder to use them.
Content Blocks comes with a built-in prefixing mechanism for your custom fields.
Whenever you create a new field, the identifier will be prefixed internally
with a combination of the
name. This is important in order to avoid
collisions with other Content Blocks that use the same identifier for their
field. Every new field you create will add a new database column to the table.
If you want to avoid creating new database columns, see how you can
reuse and share fields between different
Content Blocks.
Configure prefixing behavior
By default, prefixing is enabled. In case you don't want prefixing at all, you
can either disable it globally with prefixFields
or on a per field level with prefixField.
The default prefix type is
full. That means the complete
name is
used as a prefix. All dashes are removed and the slash will be converted to an
underscore:
my-vendor/my-element️ => myvendor_myelement
Copied!
An alternative to the full prefix is the
vendor prefix. This option can
be set in prefixType. This does also work on a
per field level. By doing so, only the vendor part of
name is used as
a prefix. This is especially useful if you want all your fields to have the same
prefix. In case you just want to have a static prefix, which differs from your
vendor, you can set a fixed vendor prefix with vendorPrefix.
Examples
# This will prefix all your fields with "myvendor_myelement"name:my-vendor/my-elementprefixFields:trueprefixType:full
Copied!
# This will disable prefixing altogethername:my-vendor/my-elementprefixFields:false
Copied!
# This will prefix all your fields with "myvendor"name:my-vendor/my-elementprefixFields:trueprefixType:vendor
Copied!
# This will prefix all your fields with "tx_foo"name:my-vendor/my-elementprefixFields:trueprefixType:vendorvendorPrefix:tx_foo
Copied!
# This will disable prefixing only for the field "my_field"name:my-vendor/my-elementprefixFields:trueprefixType:fullfields:-identifier:my_fieldtype:TextprefixField:false
Copied!
Record Types
Record Types are generic Content Types in TYPO3. Basically everything, which is
not a Content Element or Page Type. Adding custom records requires you to define
a
table name. A minimal example looks like this:
This example will create a new table
tx_vendor_my_record_type. Usually
Record Types are added in folders, so you first have to create a new folder in
the page tree. After that, you can switch to the List module and click
"Create new record". There you will find your newly created Record Type.
Tip
If you prefix your table with tx_, TYPO3 will assume this record belongs
to an extension and will group it separately by the extension title. This is
either taken from ext_emconf.php (title) or composer.json (description).
Otherwise, the record will be grouped under "System Records".
It is also possible to allow creation of Record Types in normal pages. For that
you have to enable
ignorePageTypeRestriction:
In order to create multiple types for a single Record Type, it is required to
define a
typeField field and a
typeName. The type field will be
created automatically and added as the very first field in your editing
interface as a select dropdown. The different types will be also added
automatically to the list:
Whenever you have multiple types for a table, you run into the problem on where
to define the global capability options like
languageAware,
workspaceAware or
labelField to name a few. Normally it's
recommended to keep this configuration in sync between all Content Blocks. The
reason behind this is, if you remove one type, the other Content Block still
works independently. However, this can cause heavy redundancy. Especially if
you are disabling a lot of options. The solution is to declare one of the types
as the "default" type. This is done simply by giving it the highest
priority. This ensures this Content Block is loaded first and sets the
configuration for all other types.
It is possible to create a new Record Type, which is used as a child table
inside a Collection. This is simply done by defining
foreign_table inside the Collection.
This concept also works for type Select (foreign_table)
and type Relation (allowed). The only difference is
that you reference existing records instead of creating new ones.
Content Blocks allows to extend existing table definitions with custom types.
For this to work, the table in question needs to define type field.
Create a new Record Type and define the
table, which you want to extend.
In addition, you can set a fixed
typeName if you wish. In this example
we extend the News records with a custom type:
Content Blocks will automatically create the necessary system field tabs and
palettes based on the TCA definition. You just have to define your desired
custom fields.
Keep in mind that you probably need to include various required fields like
path_segment, datetime or bodytext for your News record to work properly within
the extension. This is not a complete guide on how to make custom records work
for third-party extbase extensions. Refer to the documentation of those
extensions for additional steps. This replaces every step which includes TCA
overrides.
With this method it is not possible to override the capabilities of the existing
table. This includes options like
languageAware,
workspaceAware
or
labelField to name a few.
Hint
It's not possible to extend existing types with additional fields. This
violates the concept of self-contained Content Blocks. If you need custom
fields, create a custom type instead.
Reuse Existing Fields
It's possible to reuse already existing fields by using the useExistingField
flag. By doing so, you can extend existing fields with your own properties on a
per element level.
Reusing base fields
Base fields are fields, which are defined by extensions in
Configuration/TCA/table_name.php. They serve as a basis and can be reused in
different Content Types. For Content Elements it's highly recommended to reuse
the
header field this way, because it is used for the title on different
places in the backend.
Custom fields are fields, which are defined by extensions in
Configuration/TCA/Overrides/*.php. They extend the basic set of fields. These
can also be reused in Content Blocks, but you have to define the
type
explicitly in contrast to base fields. For this, you have to know the
type mapping from TCA type to Content Blocks type.
Reusing fields between different Content Blocks is only possible, if the option
prefixField(s) is turned off. Inside the same project with same vendor
names you can also set
prefixType to
vendor. As soon as
the
identifier is the same, the field will only be generated once. Be
careful to define the same
type for the field. Settings can be
overridden on a per element basis. Here it is not needed to define
useExistingField.
name:example/block1prefixFields:false# prefixing disabledfields:-identifier:my_custom_field# same identifiertype:Text# same typerequired:true# different settings
Copied!
name:example/block2prefixFields:false# prefixing disabledfields:-identifier:my_custom_field# same identifiertype:Text# same typemax:10# different settings
Copied!
Reusing Select fields
By default, it is not possible to reuse type Select, Radio or Checkbox
fields with different items per element. The items have to be the same for
every element by, for example, putting them into a Basic. It is still possible
to add/remove items with Page TSconfig.
There is one exception to this rule. When used in conjunction with
useExistingField the items can be set per element. For this, the
field has to be defined in base TCA with no items set. This is mostly for
backwards compatibility reasons.
Best practice
It's recommended to use existing fields whenever possible instead of creating
new ones. This also avoids the risk of the "Row size too large"
problem that can arise when a database table becomes too large and difficult to
manage.
It's also important to consider which existing fields are appropriate to reuse
(the extension makes no restrictions here or carries out checks). It is
generally not recommended to reuse fields such as uid, pid or sorting as they
have a specific use and should not be misused due to the potential for negative
side effects. Below we have listed the fields from the table
tt_content
that are eligible for reuse:
header
header_layout
header_position
header_link
subheader
bodytext
date
image
categories
pages
records
For example, if you want to use the existing column
bodytext,
header or
image you can do one of the following:
For page types, you may reuse the media field, which is commonly known from
the "Resources" tab. It is not included by default, but can be used as needed.
The full list:
media
categories
layout
author
author_email
newUntil
lastUpdated
Warning
It is not possible to override the properties below. The reason is they are
used in the SqlSchema generation to provide a proper db type, in
RelationHandler to resolve the records and in DataHandler for language
synchronisation and more. The
type of a field must not be changed in
any case.
type
relationship
dbType
nullable
MM
MM_opposite_field
MM_hasUidField
MM_oppositeUsage
allowed (type: Relation)
foreign_table
foreign_field
foreign_table_field
foreign_match_fields
ds
ds_pointerField
exclude
generatorOptions
behaviour.allowLanguageSynchronization
format (type: Number)
l10n_mode
items
There is an exception for
items when used in conjunction with
useExistingField for backwards compatibility reasons.
Type Overrides
New in version 1.2
Type Overrides are a feature available for the field type Collection and File.
With them it is possible to override a type definition of an existing type when
used in the context of a Collection.
Internally the TCA option overrideChildTca
is used here to override the
showitem string for the specified type.
This feature can be used for different purposes:
Reorder fields of the child record
Remove fields from the child record
Add completely new fields to the child record
Override options like label, description, renderType etc.
For either of those purposes you need to re-define the
fields definition
for the specified type.
Here, the type record1 is overridden in the context of the Collection field
collection_override. A type override is created with the option
overrideType and then with the
typeName as the next key. From
there you define the usual configuration like in
fields.
Lastly, type overrides can be used to re-define file definitions. In this
example the file type
image is overridden. File Types are usually
structured in a palette. This is why there is a type Palette as the first field.
Tip
File Types can also be re-defined on a global level. Refer to
the File Types API for this.
Guides
Here you can find guides on how to extend Content Blocks with additional
functionality.
You need to register the group via PHP API. This will add a new group both for
the "NewContentElementWizard" and the record type selector box. It works the
same for Page Types and Record Types. Adjust the table and typeField
accordingly.
tt_content.vendor_artistslist.20 = EXTBASEPLUGIN
tt_content.vendor_artistslist.20 {
extensionName = MyExtension
pluginName = MyPlugin
# If your controller returns a HTML response, set the template path like this:
view {
templateRootPaths.0 = EXT:site_package/ContentBlocks/ContentElements/artists/templates
}
}
Why not just use configurePlugin / registerPlugin?
TYPO3 has a helper method to quickly create plugins in ExtensionUtility::configurePlugin/registerPlugin
In the background, it will create a completely new Content Element, which just copies the "Header" element.
If you need custom fields or FlexForm configuration, you need to manually override the element.
But if you create the Content Element with Content Blocks and only register the controller actions
for it via the utility, you have the full power of Content Blocks on your side. You can quickly create
FlexForm config and manage labels, icons etc. in your component.
Note
registerControllerActions is an internal method, but it is unlikely to change in version 13. This way of adding plugins is experimental.
Example Controller
This controller simply passes the data from your Content Block to the Fluid template.
Content Blocks generates a lot of boilerplate TCA
(Table Configuration Array) for you. Usually you don't need to write own TCA,
but in some cases, where you want to override the TCA from Content Blocks, you
can do it with own TCA overrides.
How to override Content Blocks TCA
Finding the correct identifier
In order to find out the internal identifier, which is used in TCA you need to
know how prefixing works in Content Blocks. You can also
simply inspect the TCA in the Configuration module of the typo3/cms-lowlevel
extension.
As soon as you create a Collection field,
Content Blocks creates a new custom table. Therefore you need to change the key
to the table's name. Extend the TCA in Configuration/TCA/Overrides/myvendor_mycontentblock_mycollection.php.
For record types you already defined a
tableName, so use this as the key.
In the TypoScript backend module in the submodule "Active TypoScript", formerly
known as "TypoScript Object Browser", the Content Elements created by Content
Blocks can be found as usual in the section tt_content.
The name corresponds to the typeName
defined in config.yaml.
These are very helpful, because you can use the anchor id and spacing classes of
FSC. In order to wrap all your templates with such a layout you have to provide
the FSC layout paths for
lib.contentBlock.
Let's have a look at the contents of this TypoScript object:
The contents of lib.contentBlock
As you can see
lib.contentBlock is a
FLUIDTEMPLATE
TypoScript content object. This means you can override it with your own
layout and partial root paths.
<f:layout/><!-- This removes the <header> partial of FSC --><f:sectionname="Header"/><f:sectionname="Main"><!-- Your content --></f:section>
Copied!
This is of course completely optional and you can just remove this per element
if you need to. The additional empty
<f:section name="Header"/> is
important: This removes the
<header> partial which FSC would add to your
template. If you need this anyway, you can just remove it.
Important
<f:layout/> is the same as
<f:layout name="Default"/>. If you
also have a Layout with the name Default.html defined in Layouts, this
will override the FSC Layout.
Installation without Fluid Styled Content
It is (and was) always possible to run a TYPO3 installation without the optional
extension fluid_styled_content (fsc). With Content Blocks this is even easier
as you can create any Content Element you need really fast. If you need a
specific Content Element definition from fsc, you can simply re-create it.
However, there are some odd specialties which you will encounter when omitting
fsc.
Plugins and contentRenderingTemplates
Plugins from other extensions probably won't work without fluid_styled_content
as the rendering definition
lib.contentElement is defined there
for both CType-based and list_type-based plugins. So you have to define it
yourself e.g. in your sitepackage.
# Basic rendering of list-type plugins. Normally defined in fluid_styled_content.
tt_content.list = FLUIDTEMPLATE
tt_content.list {
template = TEXT
template.value = <f:cObject typoscriptObjectPath="tt_content.list.20.{data.list_type}" table="tt_content" data="{data}"/>
}
# Specific rendering for CType plugins. Normally defined in fluid_styled_content.# This needs to be added for every registered plugin separately.
tt_content.plugin_name = FLUIDTEMPLATE
tt_content.plugin_name {
template = TEXT
template.value = <f:cObject typoscriptObjectPath="tt_content.{data.CType}.20" data="{data}" table="tt_content" />
}
Copied!
The first snippet above defines the default rendering definition for the
record type list. This is a generic type for plugins, which further specify
their type in the field list_type.
The second snippet defines a rendering definition for a specific CType-based
plugin. As these plugins are independent from each other, this needs to be added
for every plugin. Tip: If you don't want to repeat this step over and over again
it is also possible to define your own
lib.contentElement with
the
FLUIDTEMPLATE cObject:
Then, add a template with name Generic.html. Add the cObject ViewHelper from
the snippet above and it will work automatically. This can also be done for
list-type plugin with List.html.
Which method you choose is up to you. The pure TypoScript variant is more
robust as the template one, as changes are to be expected in the Core in this
area.
contentRenderingTemplates
One thing is still missing: The so called contentRenderingTemplates
definition. As the name suggests, it defines where the templates for the content
rendering are defined. Without fsc you probably don't have one. And without this
definition the TypoScript from above won't work, as it will be loaded too late
in the TypoScript parsing process.
EXT:site_package/ext_localconf.php
// Define TypoScript as content rendering template.// This is normally set in Fluid Styled Content.
$GLOBALS['TYPO3_CONF_VARS']['FE']['contentRenderingTemplates'][] = 'sitepackage/Configuration/TypoScript/';
Copied!
Page Types frontend template
Historically, the page layout (also known as backend layout) is used as the
switch for frontend templates and is still considered best practice and was even
added as a TYPO3 Core functionality with the PAGEVIEW
object. This means you have to define an additional page layout for each new
Page Type.
An alternative, more modern approach is to map the frontend template to the
Page Type directly. This makes it possible to have different page layouts
per Page Type, but still render the same template. This can heavily reduce the
amount of frontend templates which need to be created for each slight layout
variation.
In order to make use of this technique, you need to add a
CASE
cObject with the
key set to
doktype. This allows us
to render a different frontend template depending on the Page Type. This setup
expects a Blog.html file inside the Resources/Private/Templates folder
in your extension.
It is not possible to use this technique together with PAGEVIEW
as it inherently depends on the page layout.
Hint
At the time of writing there is no Core solution to have a page layout as
default value for a specific Page Type. This has to be done via DataHandler
hooks. Have a look at this extension
for this.
Shared Partials
Sometimes you want to reuse Partials from your extension in your Content Block
template. This is done by extending the
partialRootPaths of the
default Content Block FLUIDTEMPLATE
definition.
Sometimes it is needed to include partials from another source to be used in the
preview. For this some Page TsConfig is needed. This can be included in the
page.tsconfig file inside your extension, which is automatically loaded. It
is also possible to provide additional layout root paths.
Content Blocks is based on the new Record API
in TYPO3 v13. As soon as a
Record is created
and enriched, it is immutable. This means the standard way of data processing
via custom DataProcessors
will not work anymore, if it was based on manipulating the
$processedData['data'] array.
Note
It is still possible to create a new variable alongside data.
The new recommended way adding custom logic via PHP is to use the new PSR-14
RecordCreationEvent. This event has
the advantage that it is always triggered as soon as a Record is created,
independent of frontend or backend context or even in scenarios where the Record
is part of a nested structure.
Example
The event listener class, using the PHP attribute
#[AsEventListener] for
registration, creates a
Coordinates object based on the field value of
the
coordinates field for the custom
maps content type.
Content Blocks already comes with pre-defined Field Types.
You should utilize them as much as possible. But in case you need special types
which cannot be covered from the basic ones, it is possible to extend these with
your own types.
When to add new field types
You can add own field types whenever you have fields with a distinct set of
configuration options set. These options can be set as default in your type
so you gain a semantic naming for this field. For example type Money could
be the core TCA type
number with
format set to
decimal as
default value.
Another use case is having a custom made TCA
renderType, which is not
covered by existing field types. This could be e.g. TCA type
user with a custom renderType. This way it is
possible to use the renderType as a first-class type in Content Blocks.
Adding a new Field Type
To add a new Field Type it is required to implement the
\TYPO3\CMS\ContentBlocks\FieldType\FieldTypeInterface. Have a look at
the Core implementations to get a feeling on how to implement them. You may
extend
\TYPO3\CMS\ContentBlocks\FieldType\AbstractFieldType for easier
usage. The registration itself happens through the PHP attribute
\TYPO3\CMS\ContentBlocks\FieldType\FieldType, which expects the
name,
tcaType and
searchable arguments.
Note
The registration is based on dependency injection. Make sure your extension
has it enabled in Configuration/Services.yaml.
The incoming
$settings array is the converted YAML definition. Apply your
logic here to instantiate a new type.
getTca
As the name suggests, you generate your TCA config here. This can be done based
on the settings provided when
createFromArray was called. Of course this
can also be a static configuration, if you don't want to provide any settings.
getSql
The SQL definition for your database column. Use
$column as the
column name. Return empty string to fall back to standard definition.
Defined by
\TYPO3\CMS\ContentBlocks\FieldType\AbstractFieldType to return
empty string for automatic detection. Override this, if you need a specific
column definition.
setName/getName
This is the actual type identifier for usage in the YAML
type option.
It is recommended to use UpperCamelCase, but it's not required.
Defined by
\TYPO3\CMS\ContentBlocks\FieldType\AbstractFieldType.
setTcaType/getTcaType
The TCA type, the new Content Blocks type is based on.
Defined by
\TYPO3\CMS\ContentBlocks\FieldType\AbstractFieldType.
setSearchable/isSearchable
Whether the field contents should be searchable in global search.
Defined by
\TYPO3\CMS\ContentBlocks\FieldType\AbstractFieldType.
It is now possible to define a "skeleton" for your Content Blocks. To do this
create a folder called content-blocks-skeleton in your project root. This
folder may contain default templates or assets for one or more Content Types. It
is used as a base when creating new types with the
make:content-block
command. In order to add a skeleton for Content Elements, create a folder called
content-element within that directory. Then, the structure is identical to
your concrete Content Block as you know it. You may place any files there. They
will simply be copied when a new Content Block is created. It is not possible to
define language/labels.xlf or config.yaml this way, as they are dynamically
generated based on your arguments.
content-blocks-skeleton
content-element
assets
icon.svg
templates
backend-preview.html
frontend.html
page-type
record-type
In case you want to name the skeleton folder differently or place it somewhere
else, you can override the default folder by providing the option
--skeleton-path with a relative path to your current working directory.
It is now possible to define default options for the make command via a yaml
config file. By default, the command looks for a file called
content-blocks.yaml in the current working directory. The location and name
can be overridden with the
--config-path option.
This config sets defaults for
vendor,
skeleton-path,
extension and
content-type. These are all possible options right
now.
Now, whenever you run this command, these options will be set by default. This
does not mean, the questions for these options will be skipped, only that they
are the default value, if you just press Enter without any input. They will
be visible in brackets at the very right [default value].
Field Type SelectNumber
A new field type SelectNumber is added. This
new type allows to have a select field with exclusively integer values. The
database column will also have type
int, which saves precious row size.
Backend previews for Content Elements must use the new layout
Preview
now. Content Blocks will fall back to the old behavior, if the layout is omitted
and will log a deprecation-level log entry.
Content Blocks now supports the re-definition of File Types (sys_file_reference).
This new Content Type can be defined inside the ContentBlocks/FileTypes folder:
Content Blocks now features a way to override types in the context of a
Collection with the new field option
overrideType. This does also
work for the Field Type
File.
It is now possible to set specific record types, which should be available for
the type selector of Collection items. The most common use case is probably some
kind of accordion or tab element, which should only have a few allowed Content
Elements as children.
Example
In this example only the Core types text and images are allowed in
allowedRecordTypes inside the Collection field.
If you've used Mask before, you know this feature. The Content Blocks
implementation is based on the FormDataProvider API in contrast to Mask, which
used itemsProcFunc.
Link EditRecord ViewHelper
Content Blocks introduces its own
cb:link.editRecord ViewHelper. In
contrast to the Core
be:link.editRecord ViewHelper it has support for
Page Layout anchor links by default. This means when you click on a custom edit
link and close the editing interface again, you will automatically jump to the
referring Content Element.
Note
This new ViewHelper is only useful in the Page Layout context for Content
Element previews. For all other cases using the Core
be:link.editRecord is still valid.
Example 1: Content Element
Here the bodytext of the Content Element is linked.
If you have a custom preview for your Collection, you can link the items
individually, making it easier for editors to edit one specific item quickly.
The important part is to add the id attribute following the schema:
element-{table}-{uid}. In this example the text of an accordion item is linked
this way.
If you already used the
be:link.editRecord ViewHelper, just make a quick
search and replace: <be:link.editRecord -> <cb:link.editRecord and
</be:link.editRecord> -> </cb:link.editRecord>. The old parameters uid and
table still work as a fallback. After that, you can also remove the namespace
import in case you didn't use any other ViewHelper in the be namespace.
Importing the cb namespace it optional as it is registered globally.
Re-definition of Core Page Types
It is now possible to re-define the Core Pages Types of TYPO3:
Standard (1)
Link to External URL (3)
Shortcut (4)
Backend User Section (6)
Mount Point (7)
This is especially useful if you need custom fields for Core Page Types, but
it's not possible to use a custom Page Type like for Shortcuts or External
Links. This feature should not be used in third-party extensions, as this would
make it impossible for users to define the Page Type themselves.
The content-blocks.yaml file has been extended to support arbitrary default
values for the generated config.yaml file from the make:content-block command.
Example
To use this new feature, create a content-blocks.yaml file in the root directory
of your project. Then, add a
config key followed by a Content Type
identifier. In this case, we set default values for Content Elements, so we use
content-element. You can also set default values for page-type,
record-type or file-type. Values defined in here override the generated
configuration by the command.
The Content Blocks Data Processor is now able to resolve the Page Record for
page templates based on
PAGEVIEW.
Example
This will add the variable
data to your page template which contains the
resolved page record. This also works for Page Types not defined by Content
Blocks like the default Page Type "1".
Core Types are now also transformed to Record Objects by the Content Blocks Data
Processor. This unifies the experience for users who have both Core defined and
Content Blocks defined types in their installation.
Note
Before Content Blocks 1.3 the Content Blocks Data Processor returned the
raw array for Types not defined by Content Blocks. Check your templates
whether they can be simplified now.
Publish Assets Command
The command
content-blocks:assets:publish publishes your public Content
Block assets into the Resources/Public folder of the host extension. Normally,
this is performed automatically every time Content Blocks is compiled. In some
deployment scenarios this command could be performed in the CI pipeline to
publish assets without the requirement for a database connection.
Migrations
Before Content Blocks, there were many more third-party solutions which enabled
easier creation of Content Elements. This collection of guides should show how
to migrate these extensions to Content Blocks.
With the release of Content Blocks 1.0.0
some things changed under the hood, which require migration in your Fluid
templates. Also, the folder structure changed as well as usage of ViewHelpers.
Ensure the extension with the old Content Block structure is loaded in the
system before running this wizard.
Warning
On case-insensitive file systems like Windows or MacOS have, the renaming
of folder Assets to assets won't be registered in git. This needs to
be commited on a case-sensitive file system e.g. inside your ddev container.
New AssetPathViewHelper
We replaced the custom AssetViewHelpers with a new
AssetPathViewHelper. Now you can use the Core
AssetViewHelpers and only use the custom ViewHelpers to build the path to your
asset.
<!-- Before --><cb:asset.cssidentifier="cbAccordionCssBackend"file="EditorPreview.css"/><!-- After --><f:asset.cssidentifier="cbAccordionCssBackend"href="{cb:assetPath()}/EditorPreview.css"/>
Copied!
New LanguagePathViewHelper
We replaced the custom TranslateViewHelper with a new
LanguagePathViewHelper that is used to build
the translation key.
<!-- Before --><cb:translatekey="readmore"/><!-- After --><f:translatekey="{cb:languagePath()}:readmore"/>
Copied!
Record object
Content Blocks now uses the
\TYPO3\CMS\Core\Domain\Record under the hood.
This has changed how some record attributes are accessed.
Content Blocks now uses the
\TYPO3\CMS\Frontend\DataProcessing\RecordTransformationProcessor
under the hood. This has changed how some fields are transformed.
Link
The type Link field will now resolve to an object of
type
\TYPO3\CMS\Core\LinkHandling\TypolinkParameter . Checks for existence
need to be adjusted to check for the
url property instead.
<!-- Before --><f:ifcondition="{data.link_field}"><!-- --></f:if><!-- After --><f:ifcondition="{data.link_field.url}"><!-- --></f:if>
Copied!
Folder
The type Folder field will now resolve to a list of
\TYPO3\CMS\Core\Resource\Folder objects.
<!-- Before --><f:foreach="{data.folder}"as="folder"><f:foreach="{folder}"as="image"><f:imageimage="{item}" /></f:for></f:for><!-- After --><f:foreach="{data.folder}"as="folder"><f:foreach="{folder.files}"as="image"><f:imageimage="{item}" /></f:for></f:for>
Copied!
FlexForm
New: Sub-fields of type FlexForm are now resolved as well.
Groups
The property
group now works for both the NewContentElementWizard and
for the record selector in the edit view. With this, the way to register groups
has changed.
Note
The group
common was renamed to
default.
Before:
EXT:my_package/Configuration/page.tsconfig
mod.wizards.newContentElement.wizardItems {
my_group {
header = LLL:EXT:my_package/Resources/Private/Language/Backend.xlf:content_group.my_group
before = common
}
}
The assets folder of your Content Block is now symlinked to the extension's
Resources/Public/ContentBlocks/* folder.
Mask
The popular Mask extension is one of the most used extensions in TER. With over
one million downloads on packagist it served their users well. Content Blocks
is heavily inspired by Mask's architecture. In fact, it could be called the
successor of Mask. The combination of Mask and the concept of a Content Block
is the result of this extension. Due to this, the migration is quite simple and
doesn't even require database migrations.
Note
This guide assumes you are on the latest Mask version.
The migration creates an exact equivalent of your Mask Elements as Content
Blocks. As already mentioned, no database migrations are necessary. The elements
will be re-defined in place. The command gathers all assets like icons and
templates and puts them into the appropriate folders. The config.yaml file is
built based on the mask.json file(s). Templates are simply copied, so they
potentially need manual migration.
Install the extension via composer. If you are on classic mode, do the
equivalent in the extension manager.
composer req nhovratov/mask-to-content-blocks
Copied!
If you haven't yet installed Content Blocks itself, install it as well:
composer req friendsoftypo3/content-blocks
Copied!
Then run the migration command. This migration will create the Content Blocks
into the same extension, where your Mask elements are currently loaded.
Removing Mask is necessary, or else the Content Elements are defined twice:
by Mask and by Content Blocks. The Mask definition will "win".
It is still recommended you read the manual migration guide to understand, what
the automatic command is doing and what manual steps you have to do afterwards.
Manual migration
Install Content Blocks in parallel to Mask and follow the steps below.
Recreation of a Mask Element
Example Mask slider element
First, let's create a new Content Block for the Mask Element you want to
migrate. For this, run the command
make:content-block and create a
Content Element with a vendor and name of your choice. The name can be the same
as the Mask name, but doesn't have to.
Next, you need to override the
typeName and disable prefixing. In this
example our Mask element has the name slider, so
typeName must be set
to mask_slider. This is the CType, which Mask generates in the background. If
you don't set this option, Content Blocks would create a unique identifier from
vendor and name, which you don't want in this case. We disable prefixing, so we
can adopt the existing Mask fields.
Our Mask element has a repeating slides field. This is a Collection in terms of
Content Blocks. To adopt this field, we have to set the identifier prefixed with
tx_mask. Also,
foreign_field has to be set explicitly to
parentid, as this is the column name, which Mask uses for its parent
reference field.
All other fields can be re-defined in Content Blocks as usual. Don't forget
to prefix all fields with tx_mask, even inside Collections.
This is the easy part. Simply copy the Mask template into templates/frontend.html and
you are done *. That said, if you didn't use any partials or layouts. If so, move
these to the according templates/partials and templates/layouts folder in your
Content Block.
Note
* This is not the whole truth. In some rare cases you need to adapt the
template when migrating to Content Blocks >= 1.0. Most probably usages of
the type Link field need adaptions:
<!-- Before --><f:ifcondition="{data.tx_mask_link_field}"><!-- --></f:if><!-- After --><f:ifcondition="{data.tx_mask_link_field.url}"><!-- --></f:if>
Copied!
Icon
In case you've used a custom SVG icon for the Mask Element, you can rename it
to icon.svg and move it to the assets folder. For FontAwesome icons you
can download the SVG version from the official website.
Labels
Mask doesn't have a translation feature. Labels can be copied from the backend
module view, and pasted directly into the labels.xlf file.
Tip
Use the command
content-blocks:language:generate vendor/name to automatically
generate the labels.xlf file with automatic keys prefilled.
Finish
That's mostly it. Now you can purge your Mask element in the backend module view
and everything should work as before. Repeat the migration for every Mask
Element and you can uninstall the Mask extension in the end and say farewell.
Note
In the backend preview of your element you will see
Element "xxx" is missing in Mask definition.. This message will vanish as
soon as you uninstall the Mask extension.
Limitations
There are some very special Mask features, which didn't make the cut to Content
Blocks (for good reason).
Page templates fields
Mask has this quirky feature of assigning additional page fields based on TYPO3
backend layouts. There is no such feature in Content Blocks. Use custom page
types instead and migrate pages with special backend layouts to the new doktype.
Nested Content
Technically, nested content means the table tt_content has a relation to itself.
This can be achieved with Content Blocks as well with the
help of Collections. However, the approach is
slightly different. Mask sets the fixed value 999 for the colPos field
of child elements. All elements with this colPos value are simply hidden from
the backend with a hook. Naturally, they won't appear in the frontend, as this
colPos is usually not rendered. In contrast, Content Blocks uses the existing
parent reference field to systematically determine the status of a child field.
This method is far more superior as it doesn't force a random colPos value.
You do the migration as described with Collections above. In addition, you have
to add some more config.
The allowed CTypes should be added in
allowedRecordTypes.
Disable Content Elements
With Mask, you could disable certain Mask Elements so they won't appear in the
Content Element Wizard. This is not possible with Content Blocks. All defined
Content Blocks are always available. The migration is to delete those unused
elements, or restrict the usage through backend user permissions.
Content Blocks is partly integrated into TYPO3 v13. Since version 1.0 it
is based on the new, underlying API from the Core. Most importantly the
new Record Transformation API, which was previously implemented in
Content Blocks for v12.
Content Blocks is supposed to be the intersection where frontend and
backend developers meet. YAML is currently the best suited format for
both parties. It allows comments and auto-completion for IDEs with JSON
schemas.
This is not possible. The philosophy behind Content Blocks are reusable
components, not extendable components. If there is an existing Content
Block you want to adapt to your custom needs, simply copy it as a whole.
There is no drawback to it. Normally, you don't even want future changes
to a Content Block from external parties. By owning the Content Block
your code can't break with an update.
If you really need to override a Content Block, you can always fall back
to traditional methods like TCA Overrides.
The Content Blocks API does not support PHP files at all. If you want to add
some PHP logic to your Content Blocks, you have to use the old common
ways.
No, it's not possible. The main rendering engine for TYPO3 is Fluid.
If you want to use something else, you are on your own.
Content Blocks generates TYPO3 core code under the hood (TCA, TypoScript
and so on), so you have to prepare your headless setup like you have to
do with core content elements.
Content Blocks uses core's asset collector, so the assets are not merged
together. Since they get registered only if the Content Block is used on
the page, each file is included separately.
This is not a specific Content Blocks question. You can add your assets
to the build process like you do with any other assets. For example you
can add your Content Block CSS to your main CSS file, and remove the CSS
registration in the Content Block.
Yes, the Content Types Team is working on a GUI for Content Blocks. The
first steps are already done, and we are looking forward to the first
beta phase in 2025.
In some areas in the TYPO3 backend fields created by Content Blocks are
displayed with their identifier instead of the localized label. E.g. in the
backend user permissions view or the list view / export view. The reason is that
you can't define fields centrally in Content Blocks. Multiple Content Blocks can
reuse the same field and define alternative labels. If you really need proper
labels in these areas, we recommend to use TCA overrides in order to define a
default label.
Here is an overview of the required and optional languages / places for defining
new Content Types the Core way (without Content Blocks). See also:
Create a custom content element type.
Note that Content Blocks uses these technologies under the hood and you should
still learn these to understand how TYPO3 works internally.
SQL
When you need a custom field or a custom table, first of all you have to define
a SQL schema. This requires knowledge of
MySQL syntax and which column types are suitable for the field. Content
Blocks already knows which types are the best for the job and adds them via a
PSR-14 Event. For experienced integrators, it is still possible to override
the default SQL definitions.
TCA
The Table Configuration Array is a TYPO3 specific PHP
structure to define the behavior of SQL columns inside the TYPO3 backend. It is
quite powerful and has plenty of options. In order to not limit the capabilities
of Content Blocks all options and field types are still available. You will
recognize them in the YAML definition. There are some improvements, which
required slight naming changes. Content Blocks greatly reduces the amount of
code, which needs to be defined, by automatically generating redundant
information. E.g. by adding new types to the type selector or placing custom
fields to a designated destination. Also icons are registered automatically,
if they are placed in a fixed folder.
XML / FlexForm
If one wants to make use of TYPO3 FlexForms, it is
required to define an XML file and register it in TCA. Content Blocks
streamlines this by using the same syntax for both normal fields and FlexForm
fields. There is no need to write TCA inside of XML anymore. Everything is
handled in the single YAML definition.
XML / XLF
Translatable labels are implemented by using XLF files
in TYPO3. In Content Blocks you still have to write those. However, all fields
get a fixed key, which consists of the Content Block name and the field
identifier. You only need to remember this standardized schema and they will
automatically be used for the backend labels. There is a fallback to the field
identifier, if no entry for the field is defined, so that you never encounter
fields without a label.
TSConfig
TSConfig is special variant of TypoScript, which
allows to modify the backend behavior. It is required to define some TSConfig in
order to register Content Elements in the Content Element Wizard. Content Blocks
already holds all necessary information for this and adds default TSConfig. If
not otherwise defined, new Content Elements are placed in the "common" tab.
TypoScript
TypoScript is the glue between the backend and the
frontend. For Content Elements specifically it is required to define a
rendering definition, if you intend to display them in the frontend. Content
Blocks automatically adds default TypoScript, which registers a Fluid
template for the Content Element. In addition, all relations are resolved
automatically, so you don't have to define DataProcessors for common use
cases like file references or collections.
Fluid
Fluid is the templating engine for TYPO3
and is both used in the backend and the frontend. Content Blocks registers a
frontend.html and a backend-preview.html file, which can immediately be used
for styling your Content Element. Layouts and partials can be used as usual.
TCA type mapping
Content Blocks redefines all Core TCA types Most of
them are the same, but have a capital letter as the first character. Some are
slightly different. Here is an overview of the mapping. This is also useful to
know, if you want to reuse custom fields.
TCA
ContentBlocks
category
Category
check
Checkbox
inline
Collection
color
Color
datetime
DateTime
email
Email
file
File
flex
FlexForm
folder
Folder
language
Language
link
Link
number
Number
passthrough
Pass
password
Password
radio
Radio
group
Relation
select
Select
slug
Slug
input
Text
text
Textarea
uuid
Uuid
json
Json
History
The beginning
The content blocks concept is a outcome of the structured content initiative.
Since there is a consensus in the community that we appreciate nearly endless possibilities
to create a new content element in TYPO3, but the creation of a simple content element is
still a very complex task.
There were different ideas and solutions for that issue in TER. The download count
proves that there is a need for a solution. So the community aimed for a solution shipped
by the core.
So in 2019 the structured content initiative reached out to the community to find a
solution for this aim. The initiative analyzed the existing solutions, extensions in
the TER like EXT:mask, EXT:dce or EXT:flux and summarized the pros and cons of each solution.
After that, there were different ideas and solutions developed and discussed in the community.
With the feedback of the community, the structured content initiative came up with
a concept for a new solution called "content blocks", the smallest chunk of information
which is needed to create a new content element.
The content blocks concept
The content blocks concept is a new way to create content elements in TYPO3.
The outcome of the research and the discussions in the community is:
A content block is a definition in your {extDir}/ContentBlocks/ folder, which will be determined and loaded automatically
A content block is not a TYPO3 Extension but a sub-resource, so there is no PHP, TypoScript, SQL nor TSconfig inside
The configuration for a new content element is stored in a YAML file, the config.yaml
Content blocks are made for frontend developers / TYPO3 beginners as main target group
As a common best practice in TYPO3, labels and every other language related stuff is stored in a XLF file. This will be registered and processed by convention
The entered name (vendor/name) in the config.yaml file defines the identifier of a content block
GUI to create / kickstart a new content block
If there are breaking changes, support e.g via UpgradeWizards to migrate easily to the new version
Better UX for editors by shipping a template (backend-preview.html) for the backend preview
Usage of the AssetCollector: JavaScript and CSS in backend and frontend
The content blocks are encapsulated, in a own folder
You can move a content block from one project to another project and it will work out of the box
Content blocks are a standalone solution, so you can use it without Fluid Styled Content or other bootstrap extensions
What it does
Basically, this extension registers the new content type with the entered vendor/name in the corresponding config.yaml
file in TYPO3 and the newContentElementWizard, and translates the YAML-file into TCA and SQL.
It registers the labels.xlf and sets the labels and descriptions by the field identifiers,
registers the icon and adds the necessary TypoScript.
So it is a abstraction layer to ease up and speed up the work of integrators and frontend developers.
But in the end, it outputs the same definitions as a normal TYPO3 content element, which can be
overwritten by the integrator.
The first proof of concept
The first proof of concept was created by the structured content initiative and is called
contentblocks_reg_api. This extension introduces the new API to TYPO3 v10 and v11.
In this first proof of concept, the content blocks are stored in a FlexForm structure.
But even if this was just a proof of concept, the community was not happy with storing data as FlexForm.
BesideS the API, the POC delivers also a simple GUI to kickstart a new content block.
The collections in this state are stored in one single table.
The field types at this point were oriented on the Symfony types.
Learnings from the contentblocks_reg_api
Learnings are:
The community is not happy with the FlexForm solution
The GUI is essential for the usage
Overriding of TCA is essential to add custom functions
Copy and paste is useful and easy
Writing the YAML file is not so hard as expected, but a GUI for editing would be beneficial
The identifiers in the labels.xlf are not so easy to work with, would be better to have a GUI for that
The GUI / kickstarter in big projects should not be available in production environment
The data storage research
The first proof of concept was stored the structure and data in FlexForm. But despite the hint,
that this is just for getting a first impaction, this leads to massive contrary feedback from
the community. Meanwhile the structured content initiative did a research and discussed the
different possibilities to store the data.
The possible solutions were:
FlexForm
JSON / JSON Blob
Database columns
EAV (Entity Attribute Value)
The result of the research was, that the best solution is to store the data in the database.
Summary of the decision
FlexForm:
Store data in FlexForm is good for a quick structure, it delivers the possibilities of sections
which allows to store a kind of inline data or repeatable elements without the need of a custom
table. But the main problem is, that the data is stored in a XML structure, which is not easy to
work with. It doesn't provide all of the TCA features and there were some challenges with translations.
Furthermore, the community replayed that they are not happy with the FlexForm solution of the
proof of concept.
JSON / JSON Blob:
The JSON solution is a good solution for storing the data, but there is a big issue with the
that: There is no DataProcessor for JSON in TYPO3 at the moment. So this has to be done by hand
and would be a complete new technology for TYPO3. This technology afterwards would have to be
maintained and supported by the TYPO3 core team. This would cost a lot of more resources which
aren't available at the moment.
Database columns and custom tables:
The database columns will work out of the box, but if there are too many columns, this leads to
an error. But it is the most common way to store data in TYPO3 and it is the most stable solution
at the moment. To reduce the amount of columns, there is a possibility to reuse a column. So the
decision was to use the database columns technique.
EAV (Entity Attribute Value):
The EAV solution is like the JSON solution not implemented in TYPO3 at the moment. So this would
be a lot of work to implement this and due to lack of resources, this is not possible at the moment.
Why YAML?
At the time this solution was met, the Form Framework and the routing was introduced a few years ago.
Despite the fact that Symfony was turning away from YAML in this time, we stuck to this solution.
Reasons for YAML:
YAML is a human readable format
YAML is a very common format in the TYPO3 community
The core provides a YAML parser
YAML can import other YAML files, so complex structures can be split into multiple files
YAML is a simple configuration format, without any logical stuff to learn
Why not PHP?
PHP would deliver a lot of possibilities, but it didn't fit to the vision of the content blocks:
PHP is not a frontend technique
In general, content blocks should be easy to understand and so there should not be any PHP code inside
PHP is not a configuration format
Why not JSON?
In fact, JSON is a good solution, but there are some reasons why we decided against JSON:
JSON is a less used format in TYPO3
JSON is not extendable or can import other files
JSON does not support comments
Fusion with EXT:Mask - Nikita Hovratov joined the team
In 2022, Nikita Hovratov from EXT:mask joined the team. He is the main developer of the mask extension
and has a lot of experience with the TYPO3 core and the issues which are addressed by the content blocks.
We decided to join forces and bring together the best of both worlds.
Developing a Core system extension
As we started to develop the system extension, we had to better adapt to core standards.
The content blocks extension is developed completely from scratch in a new way.
Types are changed:
We decided to adapt the types to the new TCA types in TYPO3 v12, so the amount of fields
to learn is reduced. This is because the TCA now supports much easier to use types.
Separate the GUI from the core extension:
While developing the system extension, there where several discussions to separate the GUI from
the core extensions. The reasons for this are:
Mostly, you don't want to have the GUI in the production environment
The GUI should not stuck to the release scheduling of the core, so we can add features faster
There is the vision, that the GUI should be a website and/or a backend module
Platform for sharing content blocks, where you can create and download content blocks
Sitemap
Reference to the headline
Copy and freely share the link
This link target has no permanent anchor assigned.The link below can be used, but is prone to change if the page gets moved.