After TYPO3 v14.0, only new functionality with a solid migration path
can be added on top, with aiming for as little as possible breaking changes
after the initial v14.0 release on the way to LTS.
Extbase queries now support SQL function expressions in ORDER BY clauses
through a new fluent API. This enables sorting results by computed values
using functions like CONCAT, TRIM, and COALESCE.
New methods have been added to
\TYPO3\CMS\Extbase\Persistence\QueryInterface :
orderBy() - Sets a single ordering, replacing any existing orderings
addOrderBy() - Adds an ordering to existing orderings
Developers can now use SQL functions in Extbase query orderings without
resorting to raw SQL statements. This enables more flexible sorting logic
while maintaining the abstraction and security benefits of the Extbase
persistence layer.
Password generation in the backend is now driven by password policies. Each
password policy can define a generator section with a class implementing
\TYPO3\CMS\Core\PasswordPolicy\Generator\PasswordGeneratorInterface .
The passwordGenerator field control references a policy by name via the
passwordPolicy option. The dice icon button next to the field generates
a password using the configured generator.
The field control can be added to any password field via TCA configuration,
making it available for extension developers as well.
All password policies are registered under
$GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies'] .
TYPO3 ships with three pre-configured policies:
default — Used for backend and frontend users
installTool — Used for install tool passwords
secretToken — Used for secret token fields (e.g. webhooks, reactions)
Each policy contains both a generator and validators section. The
generator is responsible for creating passwords, while validators enforce
password requirements. They are configured independently within the same policy.
Example
The TYPO3 core ships a PasswordGenerator implementation which is
configured like this:
Password generation for backend and frontend users is now configurable through
password policies. The Install Tool command
vendor/bin/typo3 install:password:set also respects the configured
policy.
Feature: #78412 - Provide static tsconfig includes for be_users & be_groups
The tables be_users and be_groups are extended by an additional field which
allows to select static Tsconfig defined by extensions. Following the syntax for
the field tsconfig_includes in the table pages there are the following
methods available:
For Backend users:
<?php// in Extension/Configuration/TCA/Overrides/be_users.php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerUserTSConfigFile(
'extensionKey',
'Configuration/Tsconfig/Static/example1.tsconfig',
'Example 1'
);
Copied!
For Backend usergroups:
<?php// in Extension/Configuration/TCA/Overrides/be_groups.php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerUserGroupTSConfigFile(
'extensionKey',
'Configuration/Tsconfig/Static/example2.tsconfig',
'Example 2'
);
Copied!
Impact
The new fields can be used to define User Tsconfig code for specific users and
usergroups which can be provided by extensions.
By using this approach instead of writing TSconfig directly to the TSconfig database field of
be_users or be_groups reduces the amount of configuration saved in the database. This has
many advantages in following scenarios:
Running a TYPO3 instance with automated deployment and GIT version control makes it easily
possible to create/modify such an includable TSconfig snippet via a file change. It also helps
keeping configuration streamlined for multiple environments like staging or production. Once
the file is included you have the same user/group configuration without manually changing that
in the database for each affected user or group.
Extension authors can ship predefined User TSconfig files which can be included by the TYPO3
backend user. That also applies to local (site) packages or your agency's base package.
Possible breaking changes in major upgrades can be automatically upgraded with tools like
TYPO3 Fractor (enhancement for TYPO3 Rector). If a breaking change occurs within User TSconfig,
such a tool can automatically upgrade the configuration ensuring that you don't forget to
search for them in the depths of the database. This kind of approach could reduce the amount
of recurring manual work. Particularly affected by this are large TYPO3 instances with a big
amount of be_users or be_groups records.
Searching within existing User TSconfig which is stored in the database otherwise is possible
in your IDE now. All your TSconfig is present in your codebase. This can also improve your
daily productivity and greatly simplifies major upgrades. You can also go further and disable/
hide the TSconfig database field in projects to prevent saving User TSconfig in the database
for users/groups at all.
Feature: #87435 - Make new content element wizard items sort order configurable
It is now possible to influence the order of content elements within wizard tabs
in the new content element wizard by setting before and after values in Page TSconfig.
Previously, only the order of tabs could be configured, but not the order of individual
content elements within a tab. This feature extends the existing ordering mechanism
to individual content elements, following the same pattern as tab ordering.
This eliminates the need for workarounds like creating custom wizard groups to
reorder elements.
Example
mod.wizards.newContentElement.wizardItems {
default.elements {
textmedia {
after = header
}
mask_article_card {
after = textmedia
}
# Multiple elements can be specified (comma-separated)
mask_article_list {
after = header,textmedia
}
# Or use before
header {
before = textmedia
}
}
}
Copied!
Impact
Integrators and developers can now control the exact order of content elements
within each wizard tab using Page TSconfig, without needing to create custom wizard
groups. The ordering mechanism follows the same syntax and behavior as the existing
tab ordering feature (forge#71876), ensuring consistency and familiarity.
The feature works alongside the existing tab ordering: tabs can be ordered using
before and after at the group level, and content elements within each tab
can be ordered using before and after at the element level.
If no before or after configuration is specified for elements, they will retain
their default order (typically the order defined in TCA).
Feature: #88470 - Custom message in form email finisher
The email finishers (EmailToSender and EmailToReceiver) of the TYPO3 form
framework now support an optional custom message field. This allows editors
to add a personalized text to the email sent by the form, either before or
after the submitted form values.
The message field supports rich text editing using the form-content RTE
preset, which provides formatting options like bold, italic, links, and lists.
A special placeholder {formValues} can be used within the message to control
where the submitted form data table is rendered. If the placeholder is omitted,
only the custom message is shown and the form values table is hidden.
The message field is available in:
The form editor backend module
The form finisher override settings
Impact
Editors can now configure a custom message for email finishers directly in the
form editor or via finisher overrides in the form plugin. This provides more
flexibility in crafting email notifications without the need for custom Fluid
templates.
Feature: #89951 - Cleanup command for form file upload folders
A new CLI command
form:cleanup:uploads has been introduced
to clean up old file upload folders created by the TYPO3 Form Framework.
When users upload files via
FileUpload or
ImageUpload form
elements, the files are stored in form_<hash> sub-folders inside
the configured upload directory. Over time these folders accumulate — both
from completed and incomplete form submissions.
Since uploaded files are not moved upon form submission, there is no way to
distinguish between folders from completed and abandoned submissions. The
command identifies form upload folders by their naming pattern (form_
followed by exactly 40 hex characters) and their modification time. Folders
older than a configurable retention period (default: 2 weeks) can be removed.
You must specify at least one upload folder to scan. Since each form element
can configure a different upload folder via the
saveToFileMount
property (e.g.
1:/user_upload/,
2:/custom_uploads/),
pass all relevant folders as arguments.
Usage
# Dry-run: list form upload folders older than 2 weeks (default)
bin/typo3 form:cleanup:uploads 1:/user_upload/ --dry-run
# Delete folders older than 48 hours
bin/typo3 form:cleanup:uploads 1:/user_upload/ --retention-period=48
# Scan multiple upload folders
bin/typo3 form:cleanup:uploads 1:/user_upload/ 2:/custom_uploads/
# Force deletion without confirmation (useful for scheduler tasks)
bin/typo3 form:cleanup:uploads 1:/user_upload/ --force
# Verbose output shows details about each folder
bin/typo3 form:cleanup:uploads 1:/user_upload/ --dry-run -v
Copied!
Arguments
upload-folder
Combined folder identifier(s) to scan (required). Multiple folders can be
specified as separate arguments.
Options
--retention-period /
-r
Minimum age in hours before a folder is considered for removal (default: 336,
i.e. 2 weeks).
--dry-run
Only list expired folders without deleting them.
--force /
-f
Skip the interactive confirmation prompt. Automatically set when running
with
--no-interaction (e.g. in the TYPO3 Scheduler).
Scheduler integration
The command is automatically available as a Scheduler task since it is
registered via the
#[AsCommand] attribute. Configure it to run
periodically (e.g. once per day or once per week) to keep the upload folders
clean.
Impact
The new command provides a safe and configurable way to reclaim disk space
from accumulated form upload folders. The conservative default retention period
of 2 weeks ensures that files belonging to forms still being actively
worked on are not accidentally removed.
Feature: #91924 - Add form element selection buttons to property grid
A new TCA option
isViewable is introduced for page types ("doktype")
to configure whether a specific page type ("doktype") can be linked to in
the page browser and in frontend TypoLink generation.
// Disable linking for custom page type
$GLOBALS['TCA']['pages']['types']['116']['isViewable'] = false;
Copied!
By default, all page types are viewable unless explicitly set to
false.
TYPO3 core now marks the following page types as non-viewable in TCA:
Spacer (doktype 199)
SysFolder (doktype 254)
The existing TSconfig option
TCEMAIN.preview.disableButtonForDokType
is also respected when determining viewability in the backend page browser. If a
page type is disabled for preview via TSconfig, it will also be non-viewable.
Impact
The viewability of pages can now be configured in TCA, following the same
pattern as the
allowedRecordTypes option introduced in TYPO3 v14.1.
This provides a centralized way to control which page types can be linked.
Extensions with custom page types that should not be viewable can now
configure this directly in TCA:
When a Content-Security-Policy violation report needs to be persisted, the
\TYPO3\CMS\Core\Security\ContentSecurityPolicy\Event\BeforePersistingReportEvent
can be used to provide an alternative report or to prevent a particular report
from being persisted at all.
A new
\TYPO3\CMS\Core\Pagination\QueryBuilderPaginator is introduced to
enable pagination of
\TYPO3\CMS\Core\Database\Query\QueryBuilder
instances directly.
The paginator implements the existing
\TYPO3\CMS\Core\Pagination\PaginatorInterface and integrates seamlessly
with the existing
\TYPO3\CMS\Core\Pagination\SimplePagination and
\TYPO3\CMS\Core\Pagination\SlidingWindowPagination classes.
The paginated items are fetched only once per page request by storing the result
internally, avoiding double execution of the database statement.
The total item count is determined robustly using a common table expression
(CTE) wrapping the passed
QueryBuilder instance. This approach correctly
handles advanced queries involving UNION, nested CTEs, windowing functions,
or grouping.
Note
The
QueryBuilderPaginator does not handle language overlays.
Applying overlays on the result set can lead to unexpected item count
differences between pages when some records are hidden after overlay
processing. Use
\TYPO3\CMS\Core\Pagination\QueryResultPaginator or
\TYPO3\CMS\Core\Pagination\ArrayPaginator when language overlay
handling is required.
The paginator also takes full control over LIMIT and OFFSET
and does not respect any existing limit/offset constraints on the passed
QueryBuilder instance.
Impact
A new
QueryBuilderPaginator is available to paginate
QueryBuilder result sets using the standard TYPO3 pagination API.
useTYPO3\CMS\Core\Pagination\QueryBuilderPaginator;
useTYPO3\CMS\Core\Pagination\SimplePagination;
$paginator = new QueryBuilderPaginator(
queryBuilder: $queryBuilder,
currentPageNumber: $currentPage,
itemsPerPage: 10,
);
$pagination = new SimplePagination($paginator);
// Retrieve the items for the current page
$items = $paginator->getPaginatedItems();
Copied!
Feature: #102430 - Flush cache tags for file and folder operations
This feature is guarded with toggle "frontend.cache.autoTagging" and
experimental for now: The core flushes cache tags for all kind of records
automatically when they are created, changed or deleted. For files and
folders this is not the case. This feature adds cache tag handling for file
or folder operations when they are created, changed or deleted. Also the
file metadata changes are handled correctly now. This leads to a better
editor experience when the cache tags are used correctly.
Impact
Integrators and extension developers can now add
sys_file_${uid} and
sys_file_metadata_${uid} as cache tags and they are flushed correctly
from the TYPO3 core when an editor interacts with them in the filelist module.
Feature: #102790 - Line wrapping option for code editor
A new TCA appearance option
lineWrapping has been added for the
codeEditor render type. When enabled, long lines are wrapped within
the editor instead of requiring horizontal scrolling.
TYPO3 now supports ICU MessageFormat for translations, enabling proper handling
of plural forms, gender-based selections, and other locale-aware formatting
directly in language labels.
ICU MessageFormat is an internationalization standard that allows messages to
contain placeholders that can vary based on parameters like quantity, gender,
or other conditions. This is particularly useful for proper pluralization in
languages with complex plural rules.
The format is automatically detected when using named arguments (associative
arrays) in translation calls. If the message contains ICU patterns like
{count, plural, ...} or {name}, and named arguments are provided,
the ICU MessageFormatter will be used automatically.
Language file format
ICU MessageFormat strings are stored as regular translation strings in XLIFF
files:
<?xml version="1.0" encoding="UTF-8"?><xliffversion="1.2"xmlns="urn:oasis:names:tc:xliff:document:1.2"><filesource-language="en"datatype="plaintext"original="locallang.xlf"><body><!-- Simple plural form --><trans-unitid="file_count"><source>{count, plural, one {# file} other {# files}}</source></trans-unit><!-- Plural with zero case --><trans-unitid="item_count"><source>{count, plural, =0 {no items} one {# item} other {# items}}</source></trans-unit><!-- Combined placeholder and plural --><trans-unitid="greeting"><source>Hello {name}, you have {count, plural, one {# message} other {# messages}}.</source></trans-unit><!-- Gender selection --><trans-unitid="profile_update"><source>{gender, select, male {He} female {She} other {They}} updated the profile.</source></trans-unit><!-- Simple named placeholder --><trans-unitid="welcome"><source>Welcome, {name}!</source></trans-unit></body></file></xliff>
Copied!
PHP usage
Use named arguments (associative array) to trigger ICU MessageFormat processing:
Using ICU MessageFormat with LanguageService
useTYPO3\CMS\Core\Localization\LanguageServiceFactory;
$languageService = GeneralUtility::makeInstance(LanguageServiceFactory::class)
->createFromUserPreferences($backendUser);
// ICU plural forms - use named arguments
$label = $languageService->translate(
'file_count',
'my_extension.messages',
['count' => 5]
);
// Result: "5 files"// Combined placeholder and plural
$label = $languageService->translate(
'greeting',
'my_extension.messages',
['name' => 'John', 'count' => 3]
);
// Result: "Hello John, you have 3 messages."// sprintf-style still works with positional arguments
$label = $languageService->translate(
'downloaded_times', // Label: "Downloaded %d times"'my_extension.messages',
[42] // Positional arguments use sprintf
);
// Result: "Downloaded 42 times"
Copied!
Using ICU MessageFormat with LocalizationUtility
useTYPO3\CMS\Extbase\Utility\LocalizationUtility;
// With named arguments for ICU format
$label = LocalizationUtility::translate(
'file_count',
'MyExtension',
['count' => 1]
);
// Result: "1 file"
Copied!
Fluid usage
In Fluid templates, use named arguments in the arguments attribute:
<!-- ICU plural forms with named arguments --><f:translatekey="file_count"arguments="{count: numberOfFiles}" /><!-- Combined placeholder and plural --><f:translatekey="greeting"arguments="{name: userName, count: messageCount}" /><!-- Gender selection --><f:translatekey="profile_update"arguments="{gender: userGender}" /><!-- sprintf-style with positional arguments still works --><f:translatekey="downloaded_times"arguments="{0: downloadCount}" />
Copied!
ICU MessageFormat syntax reference
Plural forms:
{variable, plural,
=0 {zero case}
one {singular case}
other {plural case}
}
Copied!
Select (gender/choice):
{variable, select,
male {He}
female {She}
other {They}
}
Copied!
Number formatting:
{count, number} - Basic number
{price, number, currency} - Currency format
Copied!
The # symbol in plural patterns is replaced with the actual number.
Impact
This feature provides a standards-based approach to pluralization that:
Uses the well-tested ICU library (via PHP's intl extension)
With Feature: #103504 - New ContentObject PAGEVIEW the new
PAGEVIEW cObject
has been introduced for the Frontend Rendering. It's a powerful alternative
to the
FLUIDTEMPLATE cObjct and allows to render a full page
with less configuration.
The elements are attached to the new
ContentArea object, which beside
the elements itself also contains all the column related information and
configuration. This is quite useful for the frontend rendering, because it
might be important for an element to know in which context it should be
rendered. With this information, an element can e.g. decide to not render
the
Header partial if it is included in the sidebar content area.
The
ContentArea objects are added to the view using either the variable
name defined via
contentAs or falls back to
content. The
content elements can then be accessed via the
records property.
The
ContentArea's' contain all the backend layout related configuration,
such as the content restrictions, which
allows further validation, e.g. if the given content types are actually valid.
{content.main.records} can therefore be used to get all content elements
from the
main content area.
main is the identifier, as
defined in the page layout and
content is the default variable name.
Important
The
ContentArea objects are attached in the
ContentAreaCollection
which implements the PSR-11
ContainerInterface to allow access to
the areas via
get(). To optimize performance, the
ContentArea
objects itself are instantiated only when accessed (lazy loading).
By accessing the
ContentArea with
{content.main} the following
information is available (as defined in the page layout):
identifier - The column identifier
colPos - The defined colPos
name - The (speaking) name, which might be a locallang key
allowedContentTypes - The defined allowedContentTypes
disallowedContentTypes - The defined disallowedContentTypes
slideMode - The defined
ContentSlideMode, defaults to
ContentSlideMode::None
configuration - The whole content area related configuration
records - The content elements as
Record objects
The following example is enough to render content elements of a page with a
single column:
mod.web_layout.BackendLayouts {
default {
title = Default
config {
backend_layout {
colCount = 1
rowCount = 1
rows {
1 {
columns {
1 {
name = Main Content Area
colPos = 0
identifier = main
}
}
}
}
}
}
}
}
With the introduction of the f:render.contentArea
and f:render.record ViewHelpers, manually
iterating over content elements is no longer necessary. All elements of a
content area can be rendered with a single ViewHelper call:
<!-- Tag syntax --><f:render.contentAreacontentArea="{content.main}"/><!-- Inline syntax -->
{content.main -> f:render.contentArea()}
Copied!
To render a single record, the
f:render.record ViewHelper can be used:
<!-- Tag syntax --><f:render.recordrecord="{content.main.records.0}"/><!-- Inline syntax -->
{content.main.records.0 -> f:render.record()}
Copied!
Note
Introducing
ContentArea also improves the
AfterContentHasBeenFetchedEvent - used to manipulate the resolved
content elements of each area - by having additional context at hand.
Impact
It's now possible to access all content elements of a page, grouped by their
corresponding column, while having the column related information and
configuration at hand. Next to less configuration effort, different renderings
for the same element, depending on the context, are easily possible.
Example
A content element template using the
Default layout and rendering
the
Header partial only in case it has not been added to the
sidebar column.
A new TypoScript setting
plugin.tx_indexedsearch.settings.pagination_type
has been introduced to select the pagination implementation used by
EXT:indexed_search.
Available values:
simple: uses
\TYPO3\CMS\Core\Pagination\SimplePagination
and renders all result pages.
slidingWindow: uses
\TYPO3\CMS\Core\Pagination\SlidingWindowPagination
and limits shown page links according to
plugin.tx_indexedsearch.settings.page_links.
The default remains
simple to preserve existing behavior.
Integrators can switch to
slidingWindow to make
page_links effective for indexed_search result browsing.
Impact
Integrators can now switch between core pagination implementations via
TypoScript without custom PHP code.
Advanced, fully custom pagination logic can still be implemented via
\TYPO3\CMS\IndexedSearch\Event\ModifySearchResultSetsEvent.
Feature: #105708 - Multiple file upload for EXT:form elements
The TYPO3 Form Framework now supports multiple file uploads for the
FileUpload and
ImageUpload form elements. This allows users
to select and upload multiple files at once using a single form field.
The implementation follows the same security patterns as Extbase's file upload
handling, using HMAC-signed deletion requests to ensure secure file removal.
Configuration
To enable multiple file uploads for a form element, set the
multiple
property to
true in your form definition:
The
multiple option is also available in the Form Editor backend module
as a checkbox in the element's inspector panel.
The
allowRemoval property enables users to remove previously uploaded
files before submitting the form. When enabled, a "Remove" checkbox is displayed
next to each uploaded file.
File count validation
The existing
Count validator can now be used with
FileUpload and
ImageUpload elements to limit the number of uploaded files:
The file input field renders with the HTML5
multiple attribute
Previously uploaded files are displayed in a list with individual
remove checkboxes
Users can select multiple files in the browser's file picker dialog
On the summary page, multiple files are displayed as a list
File deletion
The implementation uses HMAC-signed deletion requests similar to Extbase's
file handling. Each uploaded file displays a checkbox that, when checked,
marks the file for removal on form submission. The deletion data is signed
with an HMAC to prevent manipulation.
A new ViewHelper
<formvh:form.uploadDeleteCheckbox> is available
for custom templates:
Adapting custom finishers for multiple file uploads
When
multiple is enabled on a
FileUpload element, the value
returned by
$formRuntime[$element->getIdentifier()] is an
ObjectStorage<FileReference> instead of a single
FileReference. Custom finishers that process file uploads need to be adapted
to handle both cases (single and multiple uploads).
The following example shows the pattern used in the core
EmailFinisher and
DeleteUploadsFinisher:
useTYPO3\CMS\Core\Resource\FileInterface;
useTYPO3\CMS\Extbase\Domain\Model\FileReference;
useTYPO3\CMS\Extbase\Persistence\ObjectStorage;
useTYPO3\CMS\Form\Domain\Finishers\AbstractFinisher;
useTYPO3\CMS\Form\Domain\Model\FormElements\FileUpload;
classMyFinisherextendsAbstractFinisher{
protectedfunctionexecuteInternal(): void{
$formRuntime = $this->finisherContext->getFormRuntime();
foreach ($formRuntime->getFormDefinition()->getRenderablesRecursively() as $element) {
if (!$element instanceof FileUpload) {
continue;
}
$file = $formRuntime[$element->getIdentifier()];
// Single file upload: value is a FileReferenceif ($file instanceof FileReference) {
$this->processFile($file->getOriginalResource());
}
// Multiple file upload: value is an ObjectStorage of FileReferencesif ($file instanceof ObjectStorage) {
foreach ($file as $singleFile) {
if ($singleFile instanceof FileReference) {
$this->processFile($singleFile->getOriginalResource());
}
}
}
}
}
privatefunctionprocessFile(FileInterface $file): void{
// Your custom logic, e.g. move, copy, attach, etc.
}
}
Copied!
Per-element validation with ObjectStorageElementValidatorInterface
When a form field's value is an
ObjectStorage (e.g. a multi-file upload),
the
ProcessingRule must decide how to call each registered validator:
Collection-level validators (default) receive the entire
ObjectStorage. Use this for validators that check the collection as a
whole, such as
CountValidator (minimum/maximum number of items).
Element-level validators receive each item individually. Use this for
validators that inspect a single item, such as
MimeTypeValidator or
FileSizeValidator.
To mark a validator as element-level, implement the marker interface
ObjectStorageElementValidatorInterface:
useTYPO3\CMS\Extbase\Validation\Validator\AbstractValidator;
useTYPO3\CMS\Form\Mvc\Validation\ObjectStorageElementValidatorInterface;
finalclassMyFileValidatorextendsAbstractValidatorimplementsObjectStorageElementValidatorInterface{
publicfunctionisValid(mixed $value): void{
// $value is a single element from the ObjectStorage,// e.g. a FileReference — NOT the whole collection.
}
}
Copied!
For single-value fields (non-ObjectStorage), the interface has no effect —
validators are always called with the field value directly.
Impact
Form integrators can now create forms that accept multiple file uploads
without custom extensions
The
FileUpload and
ImageUpload elements support the new
multiple property
All existing finishers (EmailFinisher, SaveToDatabaseFinisher,
DeleteUploadsFinisher) automatically support multiple file uploads
Email templates display multiple files as a list of filenames
The summary page displays multiple images as a gallery and multiple
files as a filename list
Feature: #105742 - Synchronized manipulation of all crop variants
The image manipulation wizard allows images to be cropped using multiple crop variants.
When there were many variants, each one had to be edited individually, and all
changes had to be made manually multiple times.
This became particularly tedious when the editor wanted to apply precisely the
same crop values across all variants.
This feature introduces a new checkbox that allows the editor to crop all variants simultaneously.
The prerequisite for enabling this checkbox is that all crop variants must be
defined with identical aspect ratios and other identical configuration
(apart from its title).
A new sub-option of the cropVariants TCA/TCEFORM option array called
excludeFromSync allows developers to exclude specific crop variants from being
affected by synchronized cropping.
This is useful, for example, when a special crop variant for a list view is
added to the standard set of crop variants, and its differing configuration
should still allow the other crop variants to be synchronizable.
Example
The following code is the definition of the standard crop variants for a
bootstrap-based template.
All crop variants are defined identically (except the title) to enable the
synchronized cropping feature.
But for tx_news, a new crop variant for the listview is added, and
excludeFromSync = 1 is used to specifically allow one exemption of the
synchronize-feature.
TCEFORM.sys_file_reference.crop.config.cropVariants {
xxl {
title = Very Large Desktop
selectedRatio = NaN
allowedAspectRatios {
# [...] array of defined aspect ratios (identical!)
}
}
xl {
title = Large Desktop
selectedRatio = NaN
allowedAspectRatios {
# [...] array of defined aspect ratios (identical!)
}
}
# [...]
}
# Override for news extension
TCEFORM.tx_news_domain_model_news.fal_media.config.overrideChildTca.columns.crop.config.cropVariants {
listview {
title = List view
selectedRatio = default
excludeFromSync = 1
allowedAspectRatios {
# [...] array of a custom aspect ratio definition# (or identical aspect ratios, but not taken into account# for synchronized cropping)
}
}
}
Copied!
Impact
The editor is now able to apply changes to the image aspect ratio and the image
cutting to several matching crop variants at once inside the image manipulation
cropping GUI.
Exemptions for specific cropVariants can be set.
Feature: #105827 - Search in backend page tree and live search can find pages by their frontend URI
The backend page tree search functionality has been enhanced to allow entering
a full URI like https://mysite.example.com/de/any/subtree/page/, which will
show the matching page in the result tree.
Multiple URIs can be separated by the comma separator (,), just like multiple
page IDs can be entered.
Additionally, the Live Search is also enhanced to perform the same search by
a single URI to lookup its page. This is achieved with the new PSR-14 event
ModifyConstraintsForLiveSearchEvent (see Feature: #105827 - New PSR-14 ModifyConstraintsForLiveSearchEvent).
The live search will present both the default language page derived from the URI,
as well as the actual translated page as a result.
Configuration
Search by frontend URI is enabled by default and can be controlled in two ways,
similar to search by translation:
User TSconfig
Administrators can control the availability of translation search via User TSconfig:
# Disable searching by frontend URI for specific users/groups
options.pageTree.searchByFrontendUri = 0
Copied!
User Preference
Individual backend users can toggle this setting using the page tree toolbar menu.
The preference is stored in the backend user's configuration, allowing each user
to customize their search behavior.
Impact
Editors can now easily locate a backend page when only having the frontend URI
available. Permissions to edit/see the page are evaluated. Invalid or non-matching
URIs are ignored.
Feature: #105827 - New PSR-14 ModifyConstraintsForLiveSearchEvent
A new PSR-14 event
\TYPO3\CMS\Backend\Search\Event\ModifyConstraintsForLiveSearchEvent
has been added to TYPO3 Core. This event is fired in the
\TYPO3\CMS\Backend\Search\LiveSearch\LiveSearch class
and allows extensions to modify the
CompositeExpression constraints
gathered in an array, before execution. This allows to add or remove additional constraints to the main query
constraints that are combined in an logical OR conjunction, and could not be accessed
with the existing event
\TYPO3\CMS\Backend\Search\Event\ModifyQueryForLiveSearchEvent .
The event features the following methods:
getConstraints(): Returns the current array of query constraints (composite expression).
addConstraint(): Adds a single constraint.
addConstraints(): Adds multiple new constraints in one go.
getTableName(): Returns the table, for which the query will be executed (e.g. "pages"
or "tt_content").
getSearchDemand(): Returns the search demand that is getting searched for
Hint
Note that constraints are only intended to be added to the event, to not
negatively impact security-related mandatory constraints added by Core or extensions.
So there is no way to remove a constraint after it has been added.
Example
The corresponding event listener class:
<?phpnamespaceVendor\MyPackage\Backend\EventListener;
useTYPO3\CMS\Backend\Search\Event\ModifyConstraintsForLiveSearchEvent;
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Core\Database\ConnectionPool;
final readonly classPageRecordProviderEnhancedSearch{
publicfunction__construct(private ConnectionPool $connectionPool){}
#[AsEventListener('my-package/livesearch-enhanced')]publicfunction__invoke(ModifyConstraintsForLiveSearchEvent $event): void{
if ($event->getTableName() !== 'pages') {
return;
}
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('pages');
// Add a constraint so that pages marked with "show_in_all_results=1"// will always be shown.
$constraints[] = $queryBuilder->expr()->eq(
'show_in_all_results',
1,
);
$event->addConstraints(...$constraints);
}
}
Copied!
The Core itself makes uses of this event to allow searching for frontend URIs
inside the backend tree.
Impact
It is now possible to use a new PSR-14 event for adding constraints to the Live
Search query, which are OR-combined.
Feature: #106153 - Improve DebugExceptionHandler with copy functionality
The debugging exception error handler (which can be set for backend and
frontend error reporting) provides a large stack trace with details of
an error.
This is often vital when reporting bugs in TYPO3 or debugging custom
code.
The output has now been improved:
Each stack trace segment's filename and line where the error occurred
now has a "copy path" button. Clicking on it will copy the whole
path, filename and line number to the browser's clipboard.
The bottom of the page shows two buttons: One to toggle the output
above to hide or reveals the file's contents. And another to copy
the whole stack trace in plain text format, so that it can be forwarded
in error reports.
A brief section explains what a 'stack trace' is, and a jump functionality
to get from the top to the export section is available.
Hint
The "copy to clipboard" functionality is based on JavaScript. Some
browsers like Firefox only allow access to the clipboard when
accessing the site via https. When copying fails, the condensed
output that would have been written to the clipboard is instead revealed
in a box below, so it can be manually copied.
Errors and their stack traces can now be much more easily copied and forwarded
for support questions, without the need to dump a HTML file or even to make
screenshots.
File names and the lines of an occurred error can easily be copied to be inserted
into the IDE to jump directly to the related code.
Feature: #106261 - Align command line arguments of Message consumer with Symfony original
This change aligns the command line arguments of the TYPO3 Console
messenger:consume command with the original Symfony Messenger implementation.
The following new options have been added:
--limit /
-l: Limit the number of received messages
--failure-limit /
-f: The number of failed messages the worker can consume
--memory-limit /
-m: The memory limit the worker can consume
--time-limit /
-t: The time limit in seconds the worker can handle new messages
--bus /
-b: Name of the bus to which received messages should be dispatched
--all: Consume messages from all receivers
--keepalive: Whether to use the transport's keepalive mechanism if implemented
Scheduler Integration
The command can be configured as a scheduler task in TYPO3, enabling automated
consumption of messages from the configured transports. This is particularly useful
for processing asynchronous messages in the background.
This integration helps projects adopt asynchronous message handling by providing
a reliable way to process messages without manual intervention. Messages can be
dispatched asynchronously during regular request handling and consumed in the
background by the scheduler task, improving application performance and user experience.
Important
The
messenger:consume command blocks other scheduler tasks from executing
while it is running. It is therefore strongly recommended to set the
--time-limit option to a value lower than the scheduler's cron interval.
For example, if the scheduler runs every 5 minutes (300 seconds), set the time
limit to 240 seconds (4 minutes) to ensure the task completes before the next
scheduler run and allows other tasks to execute.
<trans-unitid="settings.my.enumSetting.enum.optionA"><source>Option A (localized)</source></trans-unit><trans-unitid="settings.my.enumSetting.enum.optionB"><source>Option B (localized)</source></trans-unit>
Copied!
Map-style enum declaration in settings.definitions.yaml
settings:my.enumSetting:type:stringdefault:optionAenum:optionA:'LLL:EXT:my_extension/Configuration/Sets/MySet/labels.xlf:settings.custom.optionA'# Explicit LLL referenceoptionB:'Literal Option B'# Literal labeloptionC:# Key-only map-style entry, falls back to enum value "optionC"optionD:''# Empty label stays empty
Copied!
Referenced label in labels.xlf
<trans-unitid="settings.custom.optionA"><source>Option A (localized)</source></trans-unit>
Copied!
If you want to work with automatically derived keys in the set
labels.xlf (for example
settings.<settingKey>.enum.<enumValue>), omit enum labels in YAML and
use list-style enum declarations.
Impact
Integrators can localize enum options consistently using the same
resolution behavior as other setting labels.
Feature: #106681 - Support relative date formats in DateRange validator
The
DateRange validator of the form extension now supports relative date
expressions in addition to absolute dates in Y-m-d format.
This allows form integrators to define dynamic date constraints that are evaluated
at runtime, such as ensuring a date of birth is at least 18 years in the past or
that a selected date cannot be in the future.
The following relative expressions are supported (matching PHP's
strtotime() syntax):
Named dates:
today,
now,
yesterday,
tomorrow
Relative offsets:
-18 years,
+1 month,
-2 weeks,
+30 days
These expressions can be used in the
options.minimum and
options.maximum properties of the
DateRange validator.
Example
Ensure a date of birth is at least 18 years in the past:
type:Dateidentifier:date-of-birthlabel:'Date of birth'validators:-identifier:DateRangeoptions:maximum:'-18 years'
The form editor in the TYPO3 backend has been updated to accept these relative
expressions in the date range fields. The HTML
min and
max
attributes on the rendered
<input type="date"> element are automatically
resolved to absolute Y-m-d dates at rendering time.
Impact
Form integrators can now use relative date expressions in the
DateRange
validator configuration. Existing form definitions using absolute dates continue
to work without changes.
Feature: #106828 - Add User TSconfig to define default Live Search action
To use preview for a custom record, a valid preview configuration
(TCEMAIN.preview) must exist for the table.
Impact
Live Search default actions can now be configured via User TSconfig. Integrators
can define global or per-table behavior for search results, improving backend
workflows.
The default behavior for pages has been changed to layout
to improve the user workflow.
Feature: #107003 - Add event to change record data in list view
A new PSR-14 event
\TYPO3\CMS\Backend\RecordList\Event\AfterRecordListRowPreparedEvent
has been added. This event is fired in the
\TYPO3\CMS\Backend\RecordList\DatabaseRecordList and allows extensions
to change the data for rendering a single record in the list view.
The event allows to adjust the following properties:
data: The fields of the row as array. These are the available fields:
_SELECTOR_: The checkbox element
icon: The icon
__label: Special field that contains the header
_CONTROL_: The action buttons of the row
_LOCALIZATION_: The current language
_LOCALIZATION_b: The translated language
rowDescription: The row description
header: The header (This field is only used if __label is not set. Use __label instead)
uid: The record uid (readonly)
tagAttributes: The html tag attributes of the row. These attributes are available:
class
data-table
title
The corresponding event listener class:
useTYPO3\CMS\Backend\RecordList\Event\AfterRecordListRowPreparedEvent;
useTYPO3\CMS\Core\Attribute\AsEventListener;
#[AsEventListener('my-package/backend/my-listener-name')]classMyEventListener{
publicfunction__invoke(AfterRecordListRowPreparedEvent $event): void{
$data = $event->getData();
$tagAttributes = $event->getTagAttributes();
// do magic here
$event->setData($data);
$event->setTagAttributes($tagAttributes);
}
}
Copied!
Impact
The new PSR-14 event can be used to for example modify
the title link in the record list.
Feature: #107058 - Simplify registration of a Custom Form Element
The registration of custom form elements in the TYPO3 Form Framework has been
significantly simplified. Previously, registering a custom form element required
subscribing to the JavaScript event
view/stage/abstract/render/template/perform
to render the element in the Form Editor's stage area.
With this improvement, custom form elements can now be registered without any
custom JavaScript code. The Form Editor automatically uses a generic Web Component
to render form elements in the stage area.
To use this simplified registration method, simply omit the
formEditorPartials
configuration in your form element's YAML definition. The Form Editor will then
automatically render the element using the built-in
<typo3-form-form-element-stage-item>
Web Component, which provides:
Element type and identifier display
Element label with required indicator
Validators visualization
Support for select options (SingleSelect, MultiSelect, RadioButton, Checkbox)
Support for allowed MIME types (FileUpload, ImageUpload)
Element toolbar
Hidden state visualization
The generic rendering automatically extracts and displays relevant information
from your form element's configuration without requiring any custom template or
JavaScript code.
Impact
Extension developers can now register custom form elements with minimal
configuration. By omitting the
formEditorPartials configuration, the
Form Editor will automatically render the element using a generic Web Component,
eliminating the need for:
Custom Fluid templates in Resources/Private/Backend/Partials/FormEditor/Stage/
Custom JavaScript code subscribing to
view/stage/abstract/render/template/perform
Manual element rendering logic
This significantly reduces the complexity and maintenance burden when creating
custom form elements that don't require special visualization in the Form Editor.
For custom form elements that require specialized rendering or custom interactions
in the stage area, the
formEditorPartials configuration can still be used
to provide custom Fluid templates, which will work as before.
For a complete step-by-step tutorial on creating custom form elements, see
:ref:`Creating a Custom Form Element typo3/cms-form:howtos-custom-form-element>.
Feature: #107802 - Support username and password in Redis session backend
Since Redis 6.0, it is possible to authenticate against Redis using both a username and
a password. Prior to this version, authentication was only possible with a password. With
this patch, you can now configure the TYPO3 Redis session backend as follows:
The "password" configuration option of the Redis session backend is now typed as a
array|string. Setting this configuration option with an array is deprecated and
will be removed in 15.0.
A new authorization mechanism has been introduced for Extbase controller actions
using PHP attributes. Extension authors can now implement declarative access
control logic directly on action methods using the
#[Authorize] attribute.
The
#[Authorize] attribute supports multiple authorization strategies:
Built-in checks
- Require frontend user login via
requireLogin
- Require specific frontend user groups via
requireGroups
Custom authorization logic
- Dedicated authorization class (recommended for complex logic)
- Public controller method (for simple checks)
Multiple
#[Authorize] attributes can be stacked on a single action.
All authorization checks must pass for access to be granted. If any check fails,
a
PropagateResponseException is thrown with an HTTP 403 response, which
immediately stops the Extbase dispatching process.
The
requireGroups parameter accepts an array of frontend user group
identifiers. Groups can be specified either by their UID (recommended) or by
their title. If multiple groups are specified, the user must be a member of
at least one of the groups (OR logic).
namespaceMyVendor\MyExtension\Controller;
usePsr\Http\Message\ResponseInterface;
useTYPO3\CMS\Extbase\Attribute\Authorize;
useTYPO3\CMS\Extbase\Mvc\Controller\ActionController;
classMyControllerextendsActionController{
// Recommended: Use group UIDs#[Authorize(requireGroups: [1, 2])]publicfunctionadminListAction(): ResponseInterface{
// Only accessible to users in groups 1 or 2return$this->htmlResponse();
}
// Alternative: Use group titles (not recommended)#[Authorize(requireGroups: ['administrators', 'editors'])]publicfunctioneditorListAction(): ResponseInterface{
return$this->htmlResponse();
}
// Mixed: UIDs and titles can be combined (not recommended)#[Authorize(requireGroups: [1, 'editors'])]publicfunctionmixedListAction(): ResponseInterface{
return$this->htmlResponse();
}
}
Copied!
Note
It is strongly recommended to use group UIDs instead of group titles.
Group titles can be changed by editors, which would break the authorization
logic. Group UIDs are stable and should be preferred.
Custom authorization class
For complex authorization logic, create a dedicated authorization class.
This class supports dependency injection and can be reused across controllers.
namespaceMyVendor\MyExtension\Controller;
useMyVendor\MyExtension\Authorization\MyObjectAuthorization;
useMyVendor\MyExtension\Domain\Model\MyObject;
usePsr\Http\Message\ResponseInterface;
useTYPO3\CMS\Extbase\Attribute\Authorize;
useTYPO3\CMS\Extbase\Mvc\Controller\ActionController;
classMyControllerextendsActionController{
#[Authorize(requireLogin: true)]#[Authorize(requireGroups: [1, 2])]#[Authorize(callback: [MyObjectAuthorization::class, 'checkOwnership'])]publicfunctioneditAction(MyObject $myObject): ResponseInterface{
// Only accessible to logged-in users in groups 1 or 2 who own the objectreturn$this->htmlResponse();
}
}
Copied!
Authorization checks can also be combined within a single attribute:
By default, the authorization check will throw a
\TYPO3\CMS\Core\Http\PropagateResponseException with a HTTP 403 response.
This response can be handled by the TYPO3 page error handler configured in
site settings.
The PSR-14 event
BeforeActionAuthorizationDeniedEvent can be used to
provide a custom PSR-7 response, which will be returned by Extbase.
namespaceMyVendor\MyExtension\EventListener;
usePsr\Http\Message\ResponseFactoryInterface;
usePsr\Http\Message\StreamFactoryInterface;
useTYPO3\CMS\Extbase\Authorization\AuthorizationFailureReason;
useTYPO3\CMS\Extbase\Event\Mvc\BeforeActionAuthorizationDeniedEvent;
finalclassCustomAuthorizationResponseListener{
publicfunction__construct(
private readonly ResponseFactoryInterface $responseFactory,
private readonly StreamFactoryInterface $streamFactory
){}
publicfunction__invoke(BeforeActionAuthorizationDeniedEvent $event): void{
// Customize response based on failure reason
$message = match ($event->getFailureReason()) {
AuthorizationFailureReason::NOT_LOGGED_IN => 'Please log in to access this page',
AuthorizationFailureReason::MISSING_GROUP => 'You do not have permission to access this page',
AuthorizationFailureReason::CALLBACK_DENIED => 'Access to this resource is denied',
};
$response = $this->responseFactory->createResponse()
->withHeader('Content-Type', 'text/html; charset=utf-8')
->withStatus(403)
->withBody($this->streamFactory->createStream($message));
$event->setResponse($response);
}
}
Copied!
Security Considerations
Warning
When using the
BeforeActionAuthorizationDeniedEvent event:
Do not perform state changes or modify domain objects in the event listener.
The authorization check happens before the action is executed, and any state
changes could lead to inconsistent data.
Do not use Extbase persistence (e.g., repository operations, persist calls)
in the event listener, as this may result in unintended side effects.
Custom PSR-7 responses should only be used for uncached Extbase actions.
For cached actions, the custom response may be cached and served to all users,
regardless of their authorization status. Always ensure proper cache configuration
when customizing authorization responses.
Impact
Extension authors can now implement secure, declarative authorization checks
for Extbase controller actions using the
#[Authorize] attribute.
Feature: #107887 - New "Latest backend logins" widget
A new dashboard widget Latest backend logins has been introduced
to display the most recent backend user logins directly in the TYPO3 Dashboard.
This allows administrators to quickly monitor user activity and track recent
backend access patterns without navigating through the system log.
The widget provides a configurable interface where administrators can set the
number of logins to display, offering flexibility in monitoring scope.
Each login entry shows:
The backend user avatar and name
The corresponding login time
Key benefits:
Displays recent backend user logins with user details and timestamps
Provides direct access to login monitoring without navigating system logs
Offers configurable display limits for different monitoring needs
Enhances security monitoring with quick access to login patterns
Impact
This feature improves administrative oversight by providing immediate visibility
into recent backend user activity directly within the dashboard interface.
Feature: #107906 - Recently Opened Documents Widget
A new Recently Opened Documents Dashboard Widget has been introduced to display
documents that are currently open or were recently accessed in the TYPO3 backend.
This allows editors and administrators to quickly return to their work and access
frequently edited content without navigating through the page tree or search.
The widget provides a configurable interface where users can set the number of
documents to display, offering flexibility based on their workflow needs.
Each document entry shows the record icon and title for
easy identification and quick access.
Key benefits:
Displays recently opened documents with icons and titles
Provides quick access to ongoing work without searching
Offers configurable display limits for different workflow needs
Shows document type icons for visual identification
Improves editing efficiency by reducing navigation time
Displays documents in reverse chronological order (most recent first)
The widget retrieves documents from the FormEngine session data, ensuring that
only currently open or recently accessed documents are displayed. Deleted records
are automatically filtered out to maintain data accuracy.
Impact
This feature improves editorial efficiency by providing immediate access to
recently opened documents directly within the dashboard interface, reducing
the time spent navigating through the backend to resume work.
Feature: #107940 - Introduce report about content type usage
A new Content statistics module has been introduced in the TYPO3
backend under guilabel:Reports`. This module provides data about the usage of
content elements in the TYPO3 site.
Overview
The overview displays all available content element types along with the number
of times each type is used.
For each element, all associated fields are listed, including key details such as:
The field type
Whether it is marked as required
Whether it is configurable as excludable via user group permissions
Detail view
The detail view for each content element type lists all corresponding records
that are not marked as deleted.
Impact
The new report offers a convenient way to analyze and optimize content structures
within a TYPO3 installation.
It helps administrators and developers to:
Identify unused content element types
Understand which fields are utilized by specific content element types
Gain insights into the overall configuration and diversity of content elements
Feature: #108557 - TCA option allowedRecordTypes for Page Types
// Allow any record on that Page Type.
$GLOBALS['TCA']['pages']['types']['116']['allowedRecordTypes'] = ['*'];
// Only allow specific tables on that Page Type.
$GLOBALS['TCA']['pages']['types']['116']['allowedRecordTypes'] = ['tt_content', 'my_custom_record'];
Copied!
The array can contain a list of table names or a single entry with an asterisk *
to allow all types.
Per default only the tables pages, sys_category, sys_file_reference and
sys_file_collection are allowed, if not overridden with this option.
The defaults are extended, if TCA tables enable the option
ctrl.security.ignorePageTypeRestriction. Again, this won't be considered if
allowedRecordTypes is set. They need to be configured there as well.
Impact
The allowed record types for pages can now be configured in TCA. This
centralizes the configuration for Page Types and further slims down
the need for ext_tables.php, which was utilized before.
The page module's content element preview functionality has been
enhanced to provide editors with better visual representations of content
elements directly in the backend.
Sanitized HTML rendering for content
Content elements with HTML in the bodytext field (such as text, text & images)
now display sanitized HTML in the page module preview instead of plain text.
A new
PreviewSanitizerBuilder has been introduced that creates a sanitizer
specifically designed for backend previews. This sanitizer:
Removes clickable links (unwraps
<a> tags while preserving their content)
Removes heading tags (:html<h1> through
<h6>) while preserving their content
Allows safe HTML formatting (bold, italic, lists, etc.)
Enhanced bullet list preview
Content elements of type "bullet list" now render as actual HTML lists in the preview.
Harmonized menu element rendering
The preview rendering for menu content elements and "insert records" elements
has been harmonized to match the presentation used in the record selector wizard.
This provides a consistent experience across different parts of the backend.
Impact
The mentioned enhancement improve the editorial experience in the TYPO3
backend by providing clearer, more informative content previews. Editors can now:
See formatted HTML content as it will appear to users
Quickly identify bullet list structure and content
Feature: #108581 - Record type specific label configuration
Previously, the TCA label configuration (
ctrl['label'],
ctrl['label_alt'],
and
ctrl['label_alt_force']) applied globally to all record types within a table.
This meant that all content elements in
tt_content, regardless of their
CType, displayed the same field(s) as their label in the backend.
It is now possible to define a type-specific label configuration directly in the
TCA
types section. These settings override the global
ctrl label
configuration for the respective record type:
label - Primary field used for the record title
label_alt - Alternative field(s) used when label is empty (or as additional fields)
label_alt_force - Force display of alternative fields alongside the primary label
This is especially useful for tables like
tt_content where different content
element types may benefit from showing different fields. For example, an "Image"
content element could display the image caption, while a "Text" element shows the
header field.
All types use
header as the primary label field (from
ctrl['label']).
The article type additionally displays the
teaser field if
header is empty.
The event type displays
header together with
event_date and
location
(since
label_alt_force is enabled).
When adding a new record type to an existing table, the label configuration can be
provided as the 3rd argument
$additionalTypeInformation of
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addRecordType.
Tables with multiple record types can now define more specific and descriptive
labels for each type in the backend user interface. This improves usability and
clarity for editors by making it immediately obvious which type of record is
being displayed.
This feature is especially useful for:
Content element tables such as
tt_content with different
CType
values
Tables with distinct record types serving different purposes
Plugin records with varying functionality per type
Any table where the record type changes the record's purpose or meaning
All places in TYPO3 that use
\TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle()
automatically benefit from this feature without any code changes. This includes
record lists, page trees, history views, workspaces, and similar backend modules.
Functionality like FormEngine records cannot profit yet from this option,
as FormEngine does not support Schema API yet.
Feature: #108648 - Option to modify src attribute for Vimeo/YouTube
The new configuration option srcAttribute for the YouTubeRenderer
and VimeoRenderer can be used to adjust the previously hard-coded src
attribute in the generated iframe HTML code. This can be helpful if
iframes shouldn't be loaded immediately because of privacy concerns,
in which case an alternative such as data-src can be used in the
initial HTML markup.
A new QR code button has been added next to the "View" button in various backend
modules. Clicking the button opens a modal displaying a scannable QR code for
the frontend preview URI.
The button is available in the following locations:
Page module (Layout view and Language Comparison view)
List module
Preview module
Workspaces module
When working in a workspace, the QR code contains a special preview URI that
works without backend authentication. This makes it easy to share workspace
previews with colleagues or clients, or to quickly check a draft version on a
mobile device by simply scanning the code.
The QR code can be downloaded as PNG or SVG directly from the modal.
Impact
Editors benefit from a streamlined workflow when sharing page previews or
testing pages on mobile devices. The workspace-aware preview URIs eliminate the
need to be logged in when scanning the QR code, making it particularly useful
for review processes involving external stakeholders.
Feature: #108726 - Add PSR-14 Events ModifyRenderedContentAreaEvent and ModifyRenderedRecordEvent
With the
\TYPO3\CMS\Fluid\Event\ModifyRenderedContentAreaEvent , developers can
intercept the rendering of content areas in Fluid templates to modify the output.
This depends on content areas being rendered with the new
<f:render.contentArea>
ViewHelper in Fluid templates, see
Introduce Fluid f:render.contentArea ViewHelper.
With the
\TYPO3\CMS\Fluid\Event\ModifyRenderedRecordEvent , developers can
intercept the rendering of individual records in Fluid templates to modify the output.
This depends on records being rendered with the new
<f:render.contentArea> or
<f:render.record>
ViewHelpers in Fluid templates, see
Introduce Fluid f:render.record ViewHelper.
Note that any alterations will be output as-is and will not be escaped. If you
process insecure content within an event listener, be sure to escape it properly,
e.g. by applying
htmlspecialchars() to it.
Instead of using the
<f:cObject> and
<f:for> ViewHelpers to render content areas,
the new
<f:render.contentArea> ViewHelper can be used.
It allows rendering content areas while enabling other extensions to modify the output via PSR-14 EventListeners.
This is especially useful for adding debugging wrappers or additional HTML structure
around content areas.
By default, the ViewHelper renders the content area as-is, but EventListeners
can listen to the
\TYPO3\CMS\Fluid\Event\ModifyRenderedContentAreaEvent and modify the output.
You need to use the PAGEVIEW config like this:
.. code-block:: typoscript
Instead of using the
<f:cObject> ViewHelper to render database records,
the new
<f:render.record> ViewHelper can be used.
It allows rendering records while enabling other extensions to modify the output via PSR-14 EventListeners.
This is especially useful for adding debugging wrappers or additional HTML structure
around content elements.
By default, the ViewHelper renders the record as-is, but EventListeners
can listen to the
\TYPO3\CMS\Fluid\Event\ModifyRenderedRecordEvent and modify the output.
Usage with the record-transformation data processor:
dataProcessing {
10 = record-transformation
}
Copied!
MyContentElement.fluid.html
<f:render.recordrecord="{record}"/>
or
{record -> f:render.record()}
Copied!
You can not only render tt_content records but any database record by defining the rendering in Typoscript.
# Example Typoscript configuration for rendering custom records
sys_category = FLUIDTEMPLATE
sys_category {
file = EXT:my_extension/Resources/Private/Templates/Category.html
layoutRootPaths.10 = EXT:my_extension/Resources/Private/Layouts/
partialRootPaths.10 = EXT:my_extension/Resources/Private/Partials/
dataProcessing.1421884800 = record-transformation
}
# Example Typoscript configuration for special record types
tx_myextension_domain_model_product = COA
tx_myextension_domain_model_product.default = FLUIDTEMPLATE
tx_myextension_domain_model_product.default {
templateName >
templateName.ifEmpty.cObject = TEXT
templateName.ifEmpty.cObject {
field = record_type
required = 1
case = uppercamelcase
}
# for record_type = 'mainProduct' the template file my_extension/Resources/Private/Templates/Product/MainProduct.html will be used
layoutRootPaths.10 = EXT:my_extension/Resources/Private/Layouts/
partialRootPaths.10 = EXT:my_extension/Resources/Private/Partials/
templateRootPaths.10 = EXT:my_extension/Resources/Private/Templates/Product/
dataProcessing.1421884800 = record-transformation
}
Copied!
Impact
Theme creators are encouraged to use the
<f:render.record> ViewHelper
to allow other extensions to modify the output via EventListeners.
Feature: #108763 - Console command to analyse Fluid templates
The fluid:analyse console command is introduced, which analyses
Fluid templates in the current project for correct Fluid syntax and alerts
about deprecations that are emitted during template parsing.
Usage:
vendor/bin/typo3 fluid:analyse
Copied!
Example output:
[DEPRECATION] packages/myext/Resources/Private/Templates/Test.fluid.html: <my:obsolete> has been deprecated in X and will be removed in Y.
[ERROR] packages/myext/Resources/Private/Templates/Test2.fluid.html: Variable identifiers cannot start with a "_": _temp
Copied!
In its initial implementation, the command automatically finds all Fluid
templates within the current project based on the *.fluid.* file extension (See
Feature: #108166 - Fluid file extension and template resolving)
and analyses them. By default, TYPO3's system extensions are skipped, this can
be adjusted by specifying the —-include-system-extensions CLI option.
The following errors and deprecations are currently supported:
Fluid syntax errors (e. g. invalid nesting of ViewHelper tags)
Usage of invalid ViewHelpers or ViewHelper namespaces
If exceptions are caught during the parsing process of at least one template,
the console command has a return status of 1 (error), otherwise it returns 0
(success). This means that deprecations are not interpreted as errors.
This should make it possible to use the command in CI workflows of most projects,
since deprecated functionality used by third-party templates won't make the
pipeline fail.
Verbose output allows to get feedback of analyzed templates
and the number of errors/deprecations (or success).
Deprecating ViewHelpers
The fluid:analyse console command can catch deprecations of whole ViewHelpers
if the deprecation is emitted during parse time of a template. This is possible
by implementing the
\ViewHelperNodeInitializedEventInterface :
ObsoleteViewHelper.php
useTYPO3Fluid\Fluid\Core\Parser\ParsingState;
useTYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
useTYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
useTYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperNodeInitializedEventInterface;
/**
* @deprecated since X, will be removed in Y.
*/finalclassObsoleteViewHelperextendsAbstractViewHelperimplementsViewHelperNodeInitializedEventInterface{
// ...publicstaticfunctionnodeInitializedEvent(ViewHelperNode $node, array $arguments, ParsingState $parsingState): void{
trigger_error(
'<my:obsolete> has been deprecated in X and will be removed in Y.',
E_USER_DEPRECATED,
);
}
}
Copied!
Deprecating ViewHelper arguments
The
\ViewHelperNodeInitializedEventInterface
can also be used to deprecate a ViewHelper's argument. The deprecation will only
be triggered if the argument is actually used in a template.
SomeViewHelper.php
useTYPO3Fluid\Fluid\Core\Parser\ParsingState;
useTYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
useTYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
useTYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperNodeInitializedEventInterface;
finalclassSomeViewHelperextendsAbstractViewHelperimplementsViewHelperNodeInitializedEventInterface{
publicfunctioninitializeArguments(): void{
// @deprecated since X, will be removed in Y.$this->registerArgument('obsoleteArgument', 'string', 'Original description. Deprecated since X, will be removed in Y');
}
publicstaticfunctionnodeInitializedEvent(ViewHelperNode $node, array $arguments, ParsingState $parsingState): void{
if (array_key_exists('obsoleteArgument', $arguments)) {
trigger_error(
'ViewHelper argument "obsoleteArgument" in <my:some> is deprecated since X and will be removed in Y.',
E_USER_DEPRECATED,
);
}
}
}
Copied!
Impact
The new fluid:analyse console command can be used to check basic validity of Fluid
templates in a project and can discover deprecated functionality used in template files.
Feature: #108776 - Allow to set user interface language when using CLI to create user
The TYPO3 backend bookmark system has been comprehensively overhauled,
introducing a centralized architecture that replaces the legacy "shortcut"
implementation.
Bookmark Groups
Bookmarks can be organized into three types of groups.
System groups are defined via UserTSconfig using
options.bookmarkGroups and are available to all users. Global
groups contain bookmarks visible to all backend users, though only
administrators can add bookmarks to these groups. User groups are custom groups
created by individual users for personal organization, stored in a new database
table
sys_be_shortcuts_group.
Five default bookmark groups are provided out of the box: Pages, Records,
Files, Tools, and Miscellaneous. Previously these groups were hardcoded in PHP,
but they are now defined via UserTSconfig in EXT:backend, making them fully
customizable. The functionality remains the same, but administrators now have
complete control over which groups are available.
The
options.bookmarkGroups setting should only be modified on a
global scope and not on a per-user basis, as inconsistent group configurations
between users can lead to unexpected behavior:
EXT:my_ext/Configuration/user.tsconfig
# Remove a specific default group (e.g., Files)
options.bookmarkGroups.3 >
# Remove all default groups
options.bookmarkGroups >
# Add a custom group with a static label
options.bookmarkGroups.10 = My Custom Group
# Add a custom group with a translatable label using domain syntax
options.bookmarkGroups.11 = my_extension.messages:bookmark_group.custom
# Disable bookmarks entirely
options.enableBookmarks = 0
Copied!
Group labels support the TYPO3 translation domain syntax, allowing extensions
to provide translated group names. The format is
extension_key.messages:translation_key, which resolves to the default
language file at EXT:extension_key/Resources/Private/Language/locallang.xlf.
As before, group ID -100 has special behavior as a superglobal group. Bookmarks
assigned to this group are visible to all backend users, but only administrators
can add or modify bookmarks in this group. This allows administrators to provide
a shared set of bookmarks across the entire TYPO3 installation.
Bookmark Manager
A new modal-based Bookmark Manager provides a centralized interface
for managing all bookmarks. The manager supports drag and drop reordering to
reorganize bookmarks within and across groups. Bulk operations allow selecting
multiple bookmarks to move or delete at once. Users can create, edit, and
delete custom bookmark groups through the group management interface, and
rename bookmarks directly through inline editing.
The Bookmark Manager can be accessed via the bookmark icon in the toolbar
dropdown menu.
Impact
The bookmark toolbar item now opens a dropdown with quick access to recent
bookmarks and a link to the full Bookmark Manager. Users can create custom
bookmark groups for better organization of their saved pages, records, and
modules. Administrators can configure global bookmarks visible to all users.
The Bookmarks dashboard widget has been updated to support the new bookmark
system with group filtering and limit options. The legacy "shortcut"
terminology has been replaced with "bookmark" throughout the backend
interface and codebase.
Feature: #108799 - LocalizationRepository methods for fetching record translations
TYPO3 historically has helper methods for localizations in various places.
This patch centralizes localization-related functionality by marking
\TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository
as public (non-internal) and adding new methods as modern, DI-friendly
alternatives to the static BackendUtility methods.
getRecordTranslation()
Fetches a single translated version of a record for a specific language.
Fetches all translations of a record. This method can also be used to count
translations by using
count() on the result, replacing the need for
BackendUtility::translationCount().
Returns an array of translated RawRecord objects indexed by language ID.
getPageTranslations()
Fetches all page translations for a given page.
publicfunctiongetPageTranslations(
int $pageUid,
array $limitToLanguageIds = [],
int $workspaceId = 0,
bool $includeDeletedRecords = false,
): array
Copied!
Returns an array of page translation records as RawRecord objects
indexed by language ID.
Impact
Extension developers working with record translations in the TYPO3 Backend now
have access to modern, injectable repository methods that follow current
TYPO3 coding practices.
The legacy static methods
BackendUtility::getRecordLocalization(),
BackendUtility::getExistingPageTranslations(), and
BackendUtility::translationCount() remain available for backward
compatibility until migrated completely.
Feature: #108817 - Introduce web component-based form editor tree
The Form Editor's tree component has been completely modernized, migrating from
the legacy jQuery-based implementation to a modern Web Components architecture
using Lit and the TYPO3 backend tree infrastructure.
Enhanced User Experience
The new tree component provides a significantly improved user experience with
modern interaction patterns and visual feedback:
Intuitive Drag & Drop
Form elements can now be reorganized using a smooth drag and drop interface
with intelligent validation rules. The tree automatically prevents invalid
operations, such as dragging the root form element, moving pages outside
their designated level, or dropping elements into non-composite types.
Smart Element Organization
Only composite elements like Grid Containers and Fieldsets can receive
child elements, while simple form fields remain non-droppable. Pages must
always stay at the top level, ensuring proper form structure. The tree
automatically distinguishes between reordering siblings and changing parent
elements, providing precise control over form organization.
Visual Feedback
Clear visual indicators show valid drop zones during drag operations.
Selected elements are highlighted with proper styling. The tree provides
immediate feedback for all interactions, making form building more
intuitive.
Persistent Navigation
The tree automatically remembers expanded and collapsed states. After drag
and drop operations, the tree maintains your current view and selection,
preventing disorienting resets. Navigation feels natural and responsive.
Integrated Search
A built-in search toolbar allows quick filtering of form elements by name.
The search works client-side for instant results, making it easy to locate
specific elements in complex forms.
Collapse All Functionality
The toolbar includes a convenient button to collapse all expanded nodes at
once, helping to get a quick overview of your form structure or reset the
view to a clean state.
Technical Implementation
The new implementation leverages the proven TYPO3 backend tree infrastructure.
Impact
Form editors will immediately notice the improved responsiveness and modern
feel of the tree component. Drag and drop operations are smoother and more
predictable. The search functionality makes working with large forms
significantly easier. The tree maintains its state during operations, reducing
friction and improving workflow efficiency.
The new Web Component-based architecture ensures better maintainability and
extensibility for future enhancements. The component integrates seamlessly with
the existing Form Editor without requiring changes to form definitions or
configurations.
Feature: #108819 - RecordFieldPreviewProcessor for custom PreviewRenderers
A new service
\TYPO3\CMS\Backend\Preview\RecordFieldPreviewProcessor
has been introduced to provide common field rendering helpers for custom
content element preview renderers.
Previously, these helper methods were only available in
StandardContentPreviewRenderer, which required custom preview
renderers to extend that class to access them.
Instead, this service uses the pattern of Composition over Inheritance.
The new service provides the following methods:
prepareFieldWithLabel()
Renders a field value with its TCA label prepended in bold.
Extension developers implementing custom preview renderers can now inject
RecordFieldPreviewProcessor to access common field rendering helpers
without extending
StandardContentPreviewRenderer.
A new backend module Link Management > Short URLs has been introduced.
It enables editors to create and manage short URLs that redirect visitors to a
configurable target. Short URLs are stored as sys_redirect records with the
dedicated record type short_url, providing a streamlined editing form that
hides redirect-specific fields irrelevant to short URL use cases.
Creating Short URLs
Short URLs can be created in two ways:
Manual entry: Editors type a custom path (e.g. /promo) into the
source path field.
Auto-generation: Clicking the Generate Short URL button
produces a random 8-character path (e.g. /aBcDeFgH). The generated path
is guaranteed to be unique through server-side collision checking.
Uniqueness enforcement
Short URL paths must be unique per source host. Duplicate detection happens at
two levels:
Client-side validation: While editing, the source path and source host
fields are validated against existing records. If a conflict is detected,
both fields are highlighted with an error state together with a notification.
Server-side enforcement: On save, DataHandler rejects duplicate short
URLs and displays a flash message, ensuring data integrity even if
client-side validation is bypassed.
Immutability
Once a short URL record has been saved, the source path and source host fields
become read-only. This ensures that published short URLs remain stable and
previously shared links continue to work. The redirect target can still be
changed at any time.
Clipboard support
The full short URL (including protocol and host) can be copied to the clipboard
from both the list overview and the record edit view.
Impact
Editors benefit from a dedicated interface for managing short URLs without
needing to understand redirect configuration details. The module provides a
central location for creating, reviewing, and maintaining short URLs with
built-in safeguards against duplicates and accidental modifications.
Feature: #108832 - Introduce UserSettings object for backend user profile settings
A new
UserSettings object provides structured access to backend user
profile settings defined via
$GLOBALS['TYPO3_USER_SETTINGS'].
UserSettings Object
The
UserSettings object can be retrieved via the backend user:
$userSettings = $GLOBALS['BE_USER']->getUserSettings();
// Check if a setting existsif ($userSettings->has('colorScheme')) {
$scheme = $userSettings->get('colorScheme');
}
// Get all settings as array
$allSettings = $userSettings->toArray();
// Typed access via dedicated methods
$emailOnLogin = $userSettings->isEmailMeAtLoginEnabled();
$showUploadFields = $userSettings->isUploadFieldsInTopOfEBEnabled();
Copied!
The class implements
\Psr\Container\ContainerInterface with
has()
and
get() methods. The
get() method throws
UserSettingsNotFoundException if the setting does not exist.
New JSON Storage with Backward Compatibility
Profile settings are now stored in a new
be_users.user_settings JSON
field, providing a structured and queryable format. For backward compatibility,
the existing serialized
uc blob continues to be written alongside:
// Writing still uses the uc mechanism
$GLOBALS['BE_USER']->uc['colorScheme'] = 'dark';
$GLOBALS['BE_USER']->writeUC();
// Both uc (serialized) and user_settings (JSON) are updated
Copied!
An upgrade wizard "Migrate user profile settings to JSON format" migrates
existing settings from the
uc blob to the new
user_settings field.
Impact
Backend user profile settings can now be accessed via the
UserSettings
object, providing type safety and IDE support. The new JSON storage format
improves data accessibility while maintaining full backward compatibility
through dual-write to both storage formats.
Feature: #108842 - Add Badge for Slide Mode in Layout Module
This feature introduces a visual badge in the Layout Module to indicate when
the Slide Mode is active. The badge serves as a clear indicator for editors,
enhancing the user experience by providing immediate feedback on the current
mode of operation.
For each slide mode there is a corresponding badge, with corresponding description.
For slideMode None, no badge is shown.
For slideMode Slide, a badge with the text "Slide" is shown, only if there are currently no content elements in the current page.
For slideMode Collect, a badge with the text "Collect" is shown.
For slideMode CollectReverse, a badge with the text "CollectReverse" is shown.
Feature: #108843 - User settings configuration migrated to TCA
The backend user profile settings configuration, previously stored in
$GLOBALS['TYPO3_USER_SETTINGS'], is now available in TCA at
$GLOBALS['TCA']['be_users']['columns']['user_settings'] .
This allows the user settings to benefit from TCA-based tooling and provides
a consistent API that extensions already use for other configurations.
A new method
ExtensionManagementUtility::addUserSetting() has been
introduced to simplify adding custom fields to the user profile settings.
Impact
Extensions can add custom fields to the backend user profile settings using
the new
addUserSetting() method in Configuration/TCA/Overrides/be_users.php:
The
user_settings TCA column has the following structure:
columns
Array of field configurations, each containing:
label
The field label (LLL reference or string)
config
Standard TCA config array (type, renderType, items, etc.)
table (optional)
Set to
'be_users' if the field is stored in a be_users table column
showitem
Comma-separated list of fields to display, supports
--div--; for tabs
Available field types
input - Text input field
number - Number input field
email - Email input field
password - Password input field
check with
renderType => 'checkboxToggle' - Checkbox/toggle
select with
renderType => 'selectSingle' - Select dropdown
language - Language selector
Backward compatibility
For backward compatibility, the legacy
$GLOBALS['TYPO3_USER_SETTINGS'] array
is still supported. Third-party additions are automatically migrated to TCA after
all ext_tables.php files have been loaded. However, this approach is
deprecated and extensions should migrate to the new TCA-based API.
Feature: #108846 - Console command to inspect global ViewHelper namespaces
The new console command fluid:namespaces has been introduced which lists
all available global ViewHelper namespaces in the current project. This can
be used to verify the current configuration. With the --json option, it is
also possible to access that information in a machine-readable way.
A new
<f:render.text> ViewHelper has been added. It provides a consistent
approach for outputting field values in templates where the field is part of a record.
The ViewHelper follows the same conventions as other rendering-related ViewHelpers
and can be used wherever a text-based database field should be displayed in the
frontend.
The ViewHelper is record-aware: it receives the full record and the field name, and
renders the field according to the field's TCA configuration. This includes handling
of both plain text and rich text fields.
The input can be a
\TYPO3\CMS\Core\Domain\RecordInterface ,
\TYPO3\CMS\Frontend\Page\PageInformation , or
\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface .
This allows to input a Record, a ContentBlockData object, a PageInformation object, or an Extbase Model.
PageInformation and Extbase Models are internally converted to a RecordInterface.
Usage
Usage with the record-transformation data processor:
dataProcessing {
10 = record-transformation
}
Copied!
Based on the field's TCA configuration from the provided record, the ViewHelper
chooses the appropriate processing of the field (plain text, multiline text or rich text)
without further configuration in the template.
MyContentElement.fluid.html
<f:render.textrecord="{record}"field="title" />
or
<f:render.textfield="title">{record}</f:render.text>
or
{f:render.text(record: record, field: 'title')}
or
{record -> f:render.text(field: 'title')}
Copied!
Usage with an Extbase model (property name differs from database field name):
The
field argument always refers to the database/TCA column name of the
underlying record, even if your Extbase model maps that column to a differently
named property.
Note that Extbase models need to contain all columns that should be rendered
and the record type column (if configured in TCA) for this to work correctly.
For example, an Extbase model that represents tt_content must map both bodytext
and ctype to be able to use
<f:render.text record="{contentModel}" field="bodytext" />.
Blog/Templates/Post/Show.html
<f:render.textrecord="{post}"field="short_description" /><!-- Example: Post->shortDescription maps to DB field "short_description"; use field="short_description" here. -->
Copied!
Previously, you needed to choose different processing for plain text and rich text
fields; you can now use the same ViewHelper for all types of fields.
For reference, similar results could previously be achieved using:
MyContentElement.fluid.html
{record.title}
Copied!
or multiline text:
MyContentElement.fluid.html
<f:format.nl2br>{record.description}</f:format.nl2br>
or
{record.description -> f:format.nl2br()}
Copied!
or, for rich text:
MyContentElement.fluid.html
<f:format.html>{record.bodytext}</f:format.html>
or
{record.bodytext -> f:format.html()}
Copied!
Migration
Extensions that previously accessed field values directly via
{record.title}
can continue to do so. However, using
<f:render.text> is recommended because
it renders the field in the context of the record and applies processing based on the
field configuration.
When migrating from formatting ViewHelpers like
<f:format.nl2br> or
<f:format.html> to
<f:render.text>, the main difference is that the
new ViewHelper is aware of the record it belongs to and renders the field based on
the record's TCA schema.
Impact
Theme creators are encouraged to use the
<f:render.text> ViewHelper for
rendering text-based fields (plain and rich text), as it provides a standardized,
record-aware approach that can be built upon in future versions.
Since the ViewHelper takes both the record and the field name as arguments, the
rendering process has access to the complete record context. This makes the
ViewHelper more flexible compared to directly accessing the field value.
Feature: #108904 - Add generic error action for custom HTTP status codes in ErrorController
The
\TYPO3\CMS\Frontend\Controller\ErrorController has been enhanced with
a new method
customErrorAction(), which allows flexible error handling
for custom HTTP status codes.
The new method can be used with TYPO3 site page error handling, allowing site
administrators to configure dedicated error handling (e.g., rendering a Fluid
template) for a given status code.
Example for usage in an Extbase action:
$response = GeneralUtility::makeInstance(ErrorController::class)->customErrorAction(
$this->request,
429,
'Rate limit exceeded.',
'You have exceeded the rate limit.'
);
thrownew PropagateResponseException($response, 1771065101);
Copied!
Impact
It is now possible to trigger custom error pages with specific HTTP status
codes and messages from within TYPO3 or extensions, while still respecting
the site's configured error handling.
Feature: #108941 - Provide language labels as virtual JavaScript modules
JavaScript modules can now import language labels as code.
The labels are exposed as a object that offers a get() method
and allows to substitute placeholders according to the ICU message format.
// Import labels from language domain "core.bookmarks"import { html } from'lit';
import labels from'~labels/core.bookmarks';
// Use label
html`<p>{labels.get('groupType.global')}</p>`// Retrieve label and use ICU Message Format placeholders// Example label: <source>File "{filename}" deleted</source>
html`<p>{labels.get('file.deleted', { filename: 'my-file.txt' })}</p>`// Render a label containing pseudo xml-tags// Example label: "File <bold>{filename}</bold> deleted"
html`<p>{labels.get('file.deleted', {
filename: 'my-file.txt'
// Callback function that renders the contents of `<bold>`
bold: chunks => html`<strong>${chunks}</strong>`,
})}</p>`
Copied!
This avoids the need for controllers to inject arbitrary labels into
global TYPO3.lang configuration, which impeded writing generic
web components. (Often hindered simple adopting by a plain import)
Virtual JavaScript modules (schema ~label/{language.domain})
are created that resolve the labels for the specified language domain,
that is provided after the prefix ~label/. Technically this mapping is
implemented using an importmap path prefix, which instructs to the
JavaScript engine to append the specified suffix to the mapped prefix.
The labels are allowed to be cached client side with a far future
cache timeout, similar to static resources. We therefore generate
version and locale-specific URLs, to ensure labels can be cached by
the user agent, without requiring explicit cache invalidation.
Impact
Extension developers can now use labels in JavaScript components, without
requiring to preload labels globally or per module, reducing the risk for
missing labels and simplifying developer workflows.
Several hacks like pushing labels to the top frame,
loading labels globally or adding labels to component attributes
have been used previously and will be replaced by this infrastructure.
Feature: #108966 - Rich text editor support in TYPO3 form editor
The TYPO3 form editor now supports rich text editing for textarea fields using
CKEditor 5. Form elements can be configured to use any available RTE preset,
providing a consistent editing experience across the entire TYPO3 backend.
The implementation includes a new RichTextConfigurationService that resolves
CKEditor configuration from global TYPO3 RTE presets and prepares it for use
in the form editor context. External plugins like the TYPO3 link browser are
automatically configured.
Impact
Form integrators can now enable rich text editing for any textarea field in
the form editor by configuring it in the form YAML configuration.
The following form elements and finishers now support rich text editing out of
the box:
StaticText element - Formatted text in forms
Checkbox element - Labels with links for privacy policies, etc.
form-content - Extended formatting for content fields (includes lists)
default - Standard TYPO3 RTE with all features
minimal - Minimal feature set
New Form RTE Presets
Two new RTE presets specifically designed for the form extension are now available:
form-label
Essential formatting options for labels and short text fields.
Includes: bold, italic, link
form-content
Extended formatting options for content fields like StaticText.
Includes: bold, italic, link, bulleted lists, numbered lists
Configuration Options
The following options are available for textarea editors in the form editor:
enableRichtext
Data type
boolean
Default
false
Description
Enable rich text editing for this textarea field.
richtextConfiguration
Data type
string
Default
'form-label'
Description
Name of the RTE preset to use. The preset must be registered in
$GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets'] .
Common presets: form-label, form-content, default, minimal, full
Custom Sanitizer Configuration
The form extension uses a multi-layer sanitization approach for security:
Backend: Content is sanitized using the htmlSanitize.build setting from
the RTE preset's processing configuration
Frontend: Content is sanitized again using the default sanitizer via
the f:sanitize.html() ViewHelper
To use a custom sanitizer in the backend, configure it in your RTE preset:
The frontend templates use f:sanitize.html() with the default sanitizer for
defense-in-depth security. To customize the frontend sanitization, integrators
have two options:
Option 1: Override Fluid templates
Override the form element templates and specify a custom sanitizer build:
The Extbase class configuration (persistence mapping) is now exposed in the backend
Configuration module (System > Configuration). The module is available when
the system extension lowlevel is installed.
The displayed configuration reflects the configured mapping that Extbase uses at runtime:
it is built by collecting and merging all Configuration/Extbase/Persistence/Classes.php
definitions from active packages.
This is a read-only usability improvement: developers and integrators can inspect and verify
the resolved Extbase persistence class mapping (including extension overrides) directly in the
backend, without dumping configuration arrays or manually checking each
Configuration/Extbase/Persistence/Classes.php file.
Feature: #108982 - Introduce rate limiting for extbase actions
Extbase now supports rate limiting for controller actions using the new PHP
attribute
\TYPO3\CMS\Extbase\Attribute\RateLimit . This feature allows
developers to restrict the number of requests a user can make to a specific
action within a given time frame.
Note
Rate limiting only works for uncached Extbase actions. For cached actions,
the TYPO3 frontend cache might return the response before the Extbase
controller is invoked, thus bypassing the rate limiting logic.
The rate limiting is based on the client's IP address and uses Symfony's
RateLimiter component with a caching framework storage.
The
#[RateLimit] attribute supports the following properties:
limit: The maximum number of requests allowed (default: 5).
interval: The time window for the limit (e.g., '15 minutes', '1 hour') (default: '15 minutes').
policy: The rate limiting policy to use (e.g., 'sliding_window', 'fixed_window') (default: 'sliding_window').
message: An optional translation key for the error message shown when the limit is reached.
message: An optional, localizable translation key (not a hard-coded string) for the error message shown when the
limit is reached like messages.rate_limit_message (the translation domain like my_extension will be automatically
used, and must not be part of the key here), or LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:rate_limit_message.
When a rate limit is exceeded, Extbase returns by default a response with
HTTP status code 429 (Too Many Requests).
Usage
To apply a rate limit to an Extbase action, add the
#[RateLimit]
attribute to the action method:
usePsr\Http\Message\ResponseInterface;
useTYPO3\CMS\Extbase\Attribute\RateLimit;
useTYPO3\CMS\Extbase\Mvc\Controller\ActionController;
classMyControllerextendsActionController{
#[RateLimit(limit: 3, interval: '1 minute', message: 'message.ratelimitexceeded')]publicfunctioncreateAction(): ResponseInterface{
// Business logic for creating an entityreturn$this->redirect('index');
}
}
Copied!
PSR-14 Event: BeforeActionRateLimitResponseEvent
The new PSR-14 event
\TYPO3\CMS\Extbase\Event\Mvc\BeforeActionRateLimitResponseEvent
is dispatched when a rate limit is triggered, but before the response is
returned. This allows extension developers to modify the response or perform
additional actions, such as logging, throwing a custom exception or enqueuing a
flash message.
The following example implementation shows, how to throw a custom error (which
is handled by a possible site error handler) if the rate limit is reached.
Developers can now protect sensitive Extbase actions (e.g., form submissions,
login attempts, or heavy API endpoints) from abuse, spam, or brute-force
attacks with minimal effort.
Feature: #108992 - New PSR-14 event for workspace dependency resolution
A new PSR-14 event
\TYPO3\CMS\Workspaces\Event\IsReferenceConsideredForDependencyEvent
has been added. It is dispatched per sys_refindex row when the workspace
dependency resolver evaluates which references constitute structural
dependencies during publish, stage, discard, or display operations.
Listeners decide whether a given reference should be treated as a workspace
dependency. References are opt-in: the default is "not a dependency", and
listeners must explicitly mark relevant references.
The event provides the following methods:
getTableName(): The table owning the field (refindex tablename).
getRecordId(): The record owning the field (refindex recuid).
getFieldName(): The TCA field name (refindex field).
getReferenceTable(): The referenced table (refindex ref_table).
getReferenceId(): The referenced record id (refindex ref_uid).
getAction(): The
\TYPO3\CMS\Workspaces\Dependency\DependencyCollectionAction enum
value (Publish, StageChange, Discard, or Display).
getWorkspaceId(): The current workspace id.
isDependency() /
setDependency(): Read or change whether this
reference is a structural dependency.
TYPO3 Core registers a listener that marks type=inline, type=file
(with foreign_field), and type=flex fields as dependencies.
A new enum
\TYPO3\CMS\Workspaces\Dependency\DependencyCollectionAction
has been added to represent the action context.
Example
A third-party extension that stores parent-child relationships in a custom
field can register a listener to include those references as workspace
dependencies:
Extensions can now register custom parent-child relationships as workspace
dependencies via this PSR-14 event. This ensures that structurally dependent
records are published, staged, or discarded together, preventing orphaned
records in workspaces.
The internal pseudo-event mechanism (EventCallback,
ElementEntityProcessor) used previously has been removed. This is an
internal change that does not affect public API.
Feature: #109018 - PSR-14 event to modify indexed_search result sets
A new PSR-14 event
\TYPO3\CMS\IndexedSearch\Event\AfterSearchResultSetsAreGeneratedEvent
has been introduced to modify complete search result sets in
\TYPO3\CMS\IndexedSearch\Controller\SearchController .
The event is dispatched in
searchAction() after all result sets have
been built. Event listeners can manipulate complete result sets, including
pagination, rows, section data, and category metadata.
The event provides the following methods:
getResultSets(): Returns all result sets of the current search.
setResultSets(array $resultSets): Replaces the result sets.
getSearchData(): Returns the search configuration array.
getSearchWords(): Returns the array of search words.
getView(): Returns the view instance.
getRequest(): Returns the current server request.
Example
The following example replaces every result set pagination with
SlidingWindowPagination:
This event allows modifying complete search result sets in a single listener
call. It enables custom pagination strategies as well as advanced search
result transformations.
Feature: #109080 - Unified RateLimiterFactory with admin overrides
TYPO3's
\TYPO3\CMS\Core\RateLimiter\RateLimiterFactory has been
refactored to serve as the single entry point for all rate limiting across the
system. A new
\TYPO3\CMS\Core\RateLimiter\RateLimiterFactoryInterface
extends Symfony's
RateLimiterFactoryInterface with additional convenience
methods for request-based and login rate limiting.
Previously, the backend and frontend password recovery features, as well as the
Extbase rate limiting, each created Symfony rate limiter factories directly,
bypassing TYPO3's factory. All consumers now use the central TYPO3 factory,
which enables a unified admin override mechanism.
Extension developers should type-hint against
\TYPO3\CMS\Core\RateLimiter\RateLimiterFactoryInterface when injecting
the factory.
Admin overrides via TYPO3_CONF_VARS
A new configuration option
$GLOBALS['TYPO3_CONF_VARS']['SYS']['rateLimiter']
allows administrators to override any rate limiter's settings by its ID. Each key
is a limiter ID, and each value is an array of settings to override:
The limiter ID for the action is:
extbase-vendor-myextension-controller-mycontroller-dosomethingaction
General-purpose rate limiting
Extension developers can now use the factory for custom rate limiting needs.
The
createRequestBasedLimiter() method is the recommended entry point for
request-scoped rate limiting. It automatically extracts the client's remote IP
from the PSR-7 request and uses it as the limiter key:
Pre-configured named services can also be defined in Services.yaml,
which are then injectable with the
create() method from the
RateLimiterFactoryInterface:
All rate limiting in TYPO3 now flows through a single factory that supports
admin-level overrides. Administrators can tune or restrict rate limits for any
component — login, password recovery, Extbase actions, or custom extensions —
without modifying code, using
$GLOBALS['TYPO3_CONF_VARS']['SYS']['rateLimiter'] .
The login rate limiter now uses human-readable IDs (
login-be,
login-fe) instead of SHA1 hashes. Existing cached rate limit state
from previous versions will expire naturally.
Feature: #109087 - Introduce BeforeBackendPageRenderEvent for BackendController
A new PSR-14 event
\TYPO3\CMS\Backend\Controller\Event\BeforeBackendPageRenderEvent
has been introduced. It is dispatched in
\TYPO3\CMS\Backend\Controller\BackendController
before the main backend page is rendered and provides access to:
$view (
\TYPO3\CMS\Core\View\ViewInterface ) – assign additional template
variables to the backend top frame view
$javaScriptRenderer (
\TYPO3\CMS\Core\Page\JavaScriptRenderer ) – add
custom JavaScript modules to the backend top frame
$pageRenderer (
\TYPO3\CMS\Core\Page\PageRenderer ) – add further assets
such as CSS files (marked
@internal)
It is now possible to add custom JavaScript modules and other assets to the
TYPO3 backend top frame using the new PSR-14 event
\TYPO3\CMS\Backend\Controller\Event\BeforeBackendPageRenderEvent .
Feature: #109126 - Introduce date editor for ext:form
A new web component
<typo3-form--date-editor>
has been introduced for the form editor backend. It replaces the plain text
input for the
DateRange validator minimum/maximum fields and the
defaultValue field of the
Date form element.
Previously, editors had to manually type date values or relative expressions
like
-18 years into a plain text field. The new structured editor
provides a user-friendly UI with five modes:
No value – Clears the constraint (empty value)
Today – Sets the value to
today
Absolute date – Native HTML5 date picker producing Y-m-d values
Relative date – Structured input with direction (past/future), amount
and unit (days, weeks, months, years) producing expressions like
-18 years or
+1 month
Custom relative expression – Free-text input for arbitrary relative date
expressions that go beyond the structured input, such as compound expressions
like
+1 month +3 days. The input is validated against the configured
relative date pattern.
Impact
The form editor backend now provides a structured, user-friendly editor for
date constraints and default values on
Date form elements. Editors no
longer need to know the PHP relative date syntax — they can simply select
a mode, direction, amount and unit from dropdown fields. For advanced use cases,
the custom mode allows typing arbitrary relative date expressions with
real-time validation. Existing form definitions are not affected and continue
to work without changes.
Feature: #109130 - Context-aware editing in the page module
The page module now features a context panel for editing page properties
and content elements. Clicking an edit button opens a slide-in panel next to
the page layout. The editing form is displayed inside the panel while the
page layout remains visible in the background.
The panel supports all FormEngine fields in an improved UI. The panel header
displays the record title along with a Save and Close button. An
expand button allows switching to the full record editing form in the
content area at any time. After saving, the panel stays open for further
edits.
User settings
The context panel is enabled by default. It can be disabled per user in
User Settings via the
Use quick editing for records in the page module option. When
disabled, edit buttons navigate directly to the full record editing form as
before. The setting takes effect immediately.
Impact
Editors can now edit records in the page module without leaving the page layout
context. The full editing form remains accessible for more complex editing tasks.
Deprecation: #69190 - Deprecate Random Password generator for frontend and backend users
The passwordRules option of the passwordGenerator field control has been
deprecated. Password generation is now configured through password policies
registered in
$GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies'] .
Each password policy can define a generator section with a class implementing
\TYPO3\CMS\Core\PasswordPolicy\Generator\PasswordGeneratorInterface . The
field control references a policy by name via the new passwordPolicy option
instead of defining rules inline.
Impact
Using the passwordRules option in TCA field control configuration will
trigger a PHP deprecation warning. Support for passwordRules will be
removed in TYPO3 v15.
Affected installations
Installations that use the passwordGenerator field control with the
passwordRules option in custom TCA configurations, for example on password
fields or secret token fields.
Migration
Replace the passwordRules option with a passwordPolicy reference.
For backend and frontend user password fields, the field control is now
provided by the core automatically. If your TCA override only added the
passwordGenerator field control with default rules, you can remove
it entirely. The core uses the password policy configured in
$GLOBALS['TYPO3_CONF_VARS']['BE']['passwordPolicy'] and
$GLOBALS['TYPO3_CONF_VARS']['FE']['passwordPolicy'] respectively.
The configuration option
fieldExplanationText has been deprecated
in favor of
description. The new name better reflects its purpose
and is easier to understand.
This affects form element type definitions in
prototypes.*.formElementsDefinition.*.formEditor configurations,
including editors, validators, and finishers in any extension.
Impact
Using
fieldExplanationText will trigger a PHP deprecation warning.
The migration service will automatically convert
fieldExplanationText
to
description when form configurations are loaded, ensuring
backward compatibility.
Support for
fieldExplanationText will be removed in TYPO3 v15.0.
Affected installations
Any installation using extensions that provide custom form element type
definitions with the configuration option
fieldExplanationText
in their form prototype YAML files (e.g., Configuration/Form/*.yaml
or Configuration/Yaml/FormSetup.yaml).
Migration
Rename any occurrence of
fieldExplanationText to
description
in your form element type definition YAML files (typically located in
Configuration/Yaml/FormElements/*.yaml).
Example migration:
# Before (deprecated)formEditor:editors:200:identifier:placeholderlabel:PlaceholderfieldExplanationText:Entertheplaceholdertext# AfterformEditor:editors:200:identifier:placeholderlabel:Placeholderdescription:Entertheplaceholdertext
Since Redis 6.0, it is possible to authenticate against Redis using both a username and
a password. Prior to this version, authentication was only possible with a password. With
this patch, you can now configure the TYPO3 Redis session backend as follows:
The "password" configuration option of the Redis session backend is now typed as a
array|string. Setting this configuration option with an array is deprecated
and will be removed in 15.0.
Affected installations
All installations using Redis session backend and using the password configuration
option to pass an array with username and password to it.
Migration
Use the configuration options username and password.
Calling any of the above mentioned methods will trigger a deprecation-level log
entry and will result in a fatal PHP error in TYPO3 v15.0.
Affected installations
All installations using the
PageDoktypeRegistry to configure Page Types
using the
add() method. Or, in some rare cases, using the
methods
addAllowedRecordTypes() or
doesDoktypeOnlyAllowSpecifiedRecordTypes.
Migration
A new TCA option is introduced to configure allowed record types for pages:
The array can contain a list of table names or a single entry with an asterisk *
to allow all types. If no second argument was provided to the
add method,
then the specific configuration can be omitted, as it will fall back to the
default allowed records.
Also note that Page Types are registered through TCA types. The former usage of
PageDoktypeRegistry was only useful to define allowed record types
different to the default.
The option
allowedRecordType is only evaluated within the "pages" table.
Deprecation: #108568 - BackendUserAuthentication::recordEditAccessInternals() and $errorMsg
The method
\TYPO3\CMS\Core\Authentication\BackendUserAuthentication::recordEditAccessInternals()
and the property
\TYPO3\CMS\Core\Authentication\BackendUserAuthentication::$errorMsg
have been deprecated.
These methods and properties represented an anti-pattern where the method returned
a boolean value but communicated error details through a class property, making
the API difficult to use and test.
A new method
checkRecordEditAccess() has been introduced that returns an
\TYPO3\CMS\Core\Authentication\AccessCheckResult value object containing
both the access decision and any error message.
Impact
Calling the deprecated method
recordEditAccessInternals() or accessing
the deprecated property
$errorMsg will trigger a deprecation-level log
entry and will stop working in TYPO3 v15.0.
The extension scanner reports usages as a strong match.
Affected installations
Instances or extensions that directly call
recordEditAccessInternals()
or access the
$errorMsg property are affected.
Migration
Replace calls to
recordEditAccessInternals() with
checkRecordEditAccess().
The new method returns an
AccessCheckResult object with two public properties:
isAllowed - boolean indicating if access is granted
errorMessage - string containing the error message (empty if access is allowed)
Before
useTYPO3\CMS\Core\Authentication\BackendUserAuthentication;
$backendUser = $this->getBackendUser();
if ($backendUser->recordEditAccessInternals($table, $record)) {
// Access granted
} else {
// Access denied, error message is in $backendUser->errorMsg
$errorMessage = $backendUser->errorMsg;
}
The following methods in
\TYPO3\CMS\Backend\Utility\BackendUtility have
been deprecated:
getTCEFORM_TSconfig()
getTSCpidCached()
getTSCpid()
A new method
BackendUtility::getRealPageId() has been introduced that
returns the real page ID for a given record. Unlike the previous methods that
returned arrays with multiple values or used internal caching, this method
provides a cleaner API that returns either the page ID as an integer or
null if the page cannot be determined.
Impact
Calling any of the deprecated methods will trigger a deprecation-level log
entry. The methods will be removed in TYPO3 v15.0.
The extension scanner reports usages as a strong match.
Affected installations
Instances or extensions that directly call any of the deprecated methods are
affected.
Migration
getTCEFORM_TSconfig()
This method has been moved to
FormEngineUtility. If you need TSconfig
for TCEFORM, it is recommended to rely on FormEngine data providers instead.
getTSCpidCached() and getTSCpid()
These methods returned an array with two values: the TSconfig PID and the
real PID. The new
getRealPageId() method returns only the real page ID.
Before
// getTSCpidCached returned [$tscPid, $realPid]
[$tscPid, $realPid] = BackendUtility::getTSCpidCached($table, $uid, $pid);
// getTSCpid returned the same structure
[$tscPid, $realPid] = BackendUtility::getTSCpid($table, $uid, $pid);
Copied!
After
// getRealPageId() returns int|null
$pageId = BackendUtility::getRealPageId($table, $uid, $pid);
// If you need to ensure an integer (null becomes 0)
$pageId = (int)BackendUtility::getRealPageId($table, $uid, $pid);
Calling any of the deprecated methods will trigger a deprecation-level log
entry. The methods will be removed in TYPO3 v15.0 and result in a fatal
PHP error.
The extension scanner reports usages as a strong match.
Affected installations
Instances or extensions that directly call any of the deprecated methods are
affected.
Migration
Inject
LocalizationRepository and use the new methods. The new methods
return
RawRecord objects instead of plain arrays.
getRecordLocalization()
// Before
$translations = BackendUtility::getRecordLocalization($table, $uid, $languageId);
if (is_array($translations) && !empty($translations)) {
$translation = $translations[0];
}
// After
$translation = $this->localizationRepository->getRecordTranslation($table, $uid, $languageId);
if ($translation !== null) {
// $translation is a RawRecord object
$translatedUid = $translation->getUid();
}
Copied!
getExistingPageTranslations()
// Before
$pageTranslations = BackendUtility::getExistingPageTranslations($pageUid);
// After// Returns an array of RawRecord objects indexed by language ID
$pageTranslations = $this->localizationRepository->getPageTranslations($pageUid);
Copied!
translationCount()
// Before
$message = BackendUtility::translationCount($table, $uid . ':' . $pid, 'Found %s translation(s)');
// or just counting
$count = (int)BackendUtility::translationCount($table, $uid . ':' . $pid);
// After
$translations = $this->localizationRepository->getRecordTranslations($table, $uid);
$count = count($translations);
$message = sprintf('Found %s translation(s)', $count);
The method
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addFieldsToUserSettings()
has been deprecated in favor of the new
addUserSetting() method.
The legacy method required two separate steps to add a field to user settings:
first adding the field configuration to the columns array, then calling
addFieldsToUserSettings() to add it to the showitem list. The new method
combines both steps into a single call and uses TCA as the storage location.
Impact
Calling the deprecated method will trigger a deprecation-level log entry.
The method will be removed in TYPO3 v15.0.
The extension scanner reports usages as a strong match.
Affected installations
Instances or extensions that use
ExtensionManagementUtility::addFieldsToUserSettings()
or directly modify
$GLOBALS['TYPO3_USER_SETTINGS'] to add custom fields to the
backend user profile settings are affected.
Migration
Replace the two-step approach with the new
addUserSetting() method.
Note that the new method uses TCA-style configuration and should be called from
Configuration/TCA/Overrides/be_users.php instead of ext_tables.php.
Extension developers can now use labels in JavaScript components, without
requiring to preload labels globally or per module, reducing the risk for
missing labels and simplifying developer workflows.
Affected installations
The deprecated method has been added in 14.1, that means only installations that
used
addInlineLanguageDomain() in 14.1 are affected.
Migration
The method call to
PageRenderer::addInlineLanguageDomain() can
be removed and the JavaScript part adds an module import that
imports from the
'~label/' prefix.
import { html } from'lit';
// Import labels from language domain "core.bookmarks"import labels from'~labels/core.bookmarks';
// Use label
html`<p>{labels.get('groupType.global')}</p>`
Copied!
Deprecation: #109027 - Move language:update command and events to EXT:core
The language:update CLI command and the related
\TYPO3\CMS\Install\Service\LanguagePackService have been moved from
EXT:install to EXT:core, allowing installations to update language packs
without requiring EXT:install to be installed.
Since TYPO3 v13, it is possible to run TYPO3 without EXT:install in composer
based installations. However, the language:update command still required
EXT:install, which made this advantage impractical for deployments that need
to update language packs.
The following classes have been moved and their old class names deprecated:
\TYPO3\CMS\Install\Command\LanguagePackCommand is now
\TYPO3\CMS\Core\Command\UpdateLanguagePackCommand
\TYPO3\CMS\Install\Service\Event\ModifyLanguagePackRemoteBaseUrlEvent
is now
\TYPO3\CMS\Core\Localization\Event\ModifyLanguagePackRemoteBaseUrlEvent
\TYPO3\CMS\Install\Service\Event\ModifyLanguagePacksEvent is now
\TYPO3\CMS\Core\Localization\Event\ModifyLanguagePacksEvent
The old class names are registered as aliases via
ClassAliasMap and
continue to work in TYPO3 v14. Event listeners registered for the deprecated
event class names are still called when the new event is dispatched, with a
deprecation notice triggered at runtime.
Impact
Using the old class names will trigger a deprecation notice. The extension
scanner will report usages of the deprecated class names.
The old class names will be removed in TYPO3 v15.
Affected installations
Extensions that use one or more of the deprecated class names listed above.
Migration
Replace the old class names with the new ones in
use statements:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\EventListener;
use TYPO3\CMS\Core\Attribute\AsEventListener;
-use TYPO3\CMS\Install\Service\Event\ModifyLanguagePackRemoteBaseUrlEvent;+use TYPO3\CMS\Core\Localization\Event\ModifyLanguagePackRemoteBaseUrlEvent;
final class MyOtherEventListener
{
#[AsEventListener(
identifier: 'my-extension/modify-language-pack-remote-base-url',
)]
public function __invoke(
ModifyLanguagePackRemoteBaseUrlEvent $event,
): void {
// ...
}
}
Copied!
Note
Extensions supporting both TYPO3 v13 and v14 do not need to change
anything. The old class names continue to work in both versions.
Simply update the
use statements when dropping TYPO3 v13 support.
Deprecation: #109029 - FormEngine doSave hidden field
The
<input type="hidden" name="doSave"> field in FormEngine was a
legacy mechanism where JavaScript set the field value to
1 in order to
signal PHP that the submitted form data should be processed as a save operation.
This indirection is no longer needed. TYPO3 now uses native submit button values
such as
_savedok directly, which are sufficient to determine whether a
save should be performed. The field is no longer evaluated internally.
For backwards compatibility the
doSave field is still appended to the
form on programmatic saves in TYPO3 v14, but this behaviour is deprecated and
will be removed in TYPO3 v15.
Impact
Third-party code reading
$request->getParsedBody()['doSave'] to detect
whether a save operation was triggered will stop working in TYPO3 v15.
Affected Installations
Installations with custom backend modules or extensions that inspect the
doSave POST field to determine whether incoming form data should be
persisted.
Migration
Replace any check against the
doSave POST field with a check against
the native submit action fields that FormEngine sends as part of its regular
form submission.
Before:
$parsedBody = $request->getParsedBody();
$doSave = (bool)($parsedBody['doSave'] ?? false);
if ($doSave) {
// process data
}
Copied!
After:
$parsedBody = $request->getParsedBody();
$isSaveAction = !empty($parsedBody['_savedok'])
|| !empty($parsedBody['_saveandclosedok'])
|| !empty($parsedBody['_savedokview'])
|| !empty($parsedBody['_savedoknew']);
if ($isSaveAction) {
// process data
}
The additionalHiddenFields result array key in FormEngine was a legacy
mechanism that stored hidden
<input> HTML strings separately from the
main html key. This indirection is no longer needed — elements can simply
add their hidden fields to the html key directly.
The following have been deprecated:
The additionalHiddenFields key in FormEngine result arrays
FormResult::$hiddenFieldsHtml
FormResultCollection::getHiddenFieldsHtml()
Impact
Third-party FormEngine elements that add entries to
php:$resultArray['additionalHiddenFields'] will trigger a PHP
E_USER_DEPRECATED level error when their result is merged via
AbstractNode::mergeChildReturnIntoExistingResult().
Affected installations
Installations with custom FormEngine elements or containers that populate the
additionalHiddenFields result array key.
Migration
Move hidden field HTML from additionalHiddenFields into the html key
directly.
This affects how whitespace (spaces, tabs, newlines) in translation strings
are handled.
Without
xml:space="preserve" (default behavior):
Multiple consecutive whitespace characters (spaces, tabs, newlines) are
collapsed into a single space, and leading/trailing whitespace is trimmed.
Example XLIFF source:
<trans-unitid="my.label"><source>This is a
multi-line
string.</source></trans-unit>
Copied!
Before: The string contained literal newlines and indentation.
After: The string becomes
"This is a multi-line string."
With
xml:space="preserve":
Whitespace is kept exactly as written in the XLIFF file.
<trans-unitid="my.label"xml:space="preserve"><source>This is a
multi-line
string.</source></trans-unit>
Copied!
The string remains
"This is a\n multi-line\n string."
Impact
Translation strings that previously contained unintended whitespace (from
formatting in the XLIFF file) will now display correctly without extra
spaces or line breaks.
If you intentionally need preserved whitespace in a translation string,
add the
xml:space="preserve" attribute to the
<trans-unit>
element (XLIFF 1.2) or
<segment> element (XLIFF 2.0).
This change affects both XLIFF 1.2 and XLIFF 2.0/2.1 formats.
Important: #93765 - Extbase identity map now language-aware
The Extbase persistence session's identity map now includes language context
when caching domain objects. Previously, the identity map used identifiers
based only on the record's UID and localized UID, which could cause incorrect
translations to be returned when the same object was accessed with different
LanguageAspect configurations within the same request.
The identity map identifier now includes the language context,
specifically the
contentId,
overlayType, and
fallbackChain
properties of the
LanguageAspect.
Impact
This change ensures that objects loaded with different language configurations
are cached separately in the identity map. For example, if an object is first
loaded with
OVERLAYS_ON and then queried again with
OVERLAYS_MIXED,
the system will correctly return different cached objects for each context.
The change is transparent for most use cases. However, objects retrieved with
different language settings are now distinct instances. Code relying on
object identity (e.g., using === comparison) between objects loaded with
different language settings will need adjustment.
Important: #102906 - Prevent Extbase errorAction from writing session data
Previously, any validation error handled implicitly by the Extbase
ActionController::errorAction() would persist the resulting
FlashMessage items to the user session. In cases where no session
existed, a new session would be generated and a session cookie sent to the
client. This behavior could lead to automated crawlers generating a large
number of unnecessary sessions.
When the
errorAction() is invoked (for example, due to validation
errors), flash messages are no longer persisted to the session but are
instead transferred with the corresponding
ForwardResponse.
The implementation introduces two new public methods in
ForwardResponse:
withFlashMessages(FlashMessage ...$flashMessages) - Adds flash
messages to the forward response
getFlashMessages() - Retrieves flash messages from the forward
response
Flash messages are transferred through
ExtbaseRequestParameters when
forwarding requests and are restored from
ExtbaseRequestParameters in
ActionController::initializeStateFromExtbaseRequestParameters().
Hint
Custom code that has overridden the internal methods
ActionController::processRequest() or
ActionController::forwardToReferringRequest() may need to be
adjusted to benefit from this change. Ensure that your custom
implementations properly handle flash messages via
ForwardResponse
when forwarding requests from the error action.
When configuring a TCA select field with renderType => 'selectSingle'
and adding an item with 'value' => null, the generated database column is
now nullable regardless of whether the other item values are integers or strings.
Previously, the following configuration with integer item values would incorrectly
generate a
VARCHAR(255) column:
The workspace selector has been moved from the top toolbar to the backend
sidebar. The selector now displays the full name of the currently active
workspace and provides a dropdown to switch between available workspaces.
A colored top bar indicator is shown whenever a workspace is active, giving
editors a clear visual cue about which workspace they are working in.
Color per workspace
Administrators can now assign a color to each workspace via the
Color field in the workspace record. The selected color is used
for the top bar indicator and the workspace selector in the sidebar, making
it easier to visually distinguish workspaces at a glance.
The following colors are available: red, orange, yellow, lime, green, teal,
blue, indigo, purple, and magenta. The default color for new workspaces is
orange.
Description as tooltip
The Description field of a workspace record is now displayed as
a tooltip on both the workspace selector dropdown items and the top bar
indicator. This allows administrators to provide additional context about
the purpose of a workspace, which editors can see by hovering over the
workspace name.
Workspace Live indicator option
A user setting Enable workspace Live indicator is available in
User Settings > Personalization. When enabled (the default), the
top bar indicator is shown while working in the Live workspace. Users who find
the indicator distracting can disable it. The setting takes effect immediately
without requiring a page reload. The indicator remains visible when any
non-Live workspace is active, regardless of this setting.
Impact
Editors will see the workspace selector in the sidebar instead of the top
toolbar. The workspace indicator bar at the top of the backend now reflects
the configured color of the active workspace.
Administrators are encouraged to assign meaningful colors and descriptions to
their workspaces to improve the editing experience for their teams.
Important: #108557 - Drop PageDoktypeRegistry onlyAllowedTables option
It is possible to limit allowed tables for Page Types ("doktype"). However, up
until now the default behavior when switching types was to ignore possible
violations to these rules. The behavior could be changed on a per doktype
level like this:
This would make the Page Type 116 strict, when switching the Page Type
(aka doktype).
This option is now obsolete, as this functionality is always enabled.
Switching Page Types is no longer possible if it violates the configured
allowed tables, making the system more consistent.
Some remarks: This option was rarely used and often misunderstood. The option
to configure allowed tables was called allowedTables. It was not clear what
onlyAllowedTables should even mean without looking into the documentation.
On a rational point of view it does not make sense to configure restrictions
for Page Types, when they are ignored per default for the action of switching
types. Allowing to violate the rules makes them useless in the first place. So
either remove those restrictions altogether or make them always work like it
is done now.
Important: #108783 - Backend user language default changed to "en"
The backend user language field (be_users.lang) historically used default
as the value for English. This has been changed to use the standard ISO 639-1
language code en instead.
The language key default is still accepted for backwards compatibility with
custom code, but is no longer selectable in the backend user interface.
An upgrade wizard "Migrate backend user language from 'default' to 'en'" is
available to migrate existing backend user records.
Impact
New backend users will have en as their default language instead of
default.
Existing backend users with lang=default should run the upgrade wizard
to migrate to lang=en.
In general: Code that uses default as a language key (e.g. custom
instances of
LanguageService) will continue to work as
default is still mapped to en internally.
Important: #108796 - Internal shortcut classes renamed to bookmark
As part of the centralized bookmark management feature, several internal classes
related to backend shortcuts have been renamed to use "bookmark" terminology.
These classes were marked as
@internal and are not part of the public
TYPO3 API. However, some extensions might have used them despite being internal.
The following classes have been renamed or replaced:
Renamed to
\TYPO3\CMS\Backend\Backend\ToolbarItems\BookmarkToolbarItem .
The JavaScript module
@typo3/backend/toolbar/shortcut-menu has been removed
and replaced by the new bookmark management modules in
@typo3/backend/bookmark/.
Important: #109147 - Move linkvalidator module to Link Management group
The linkvalidator module (Check Links) has been moved from the
Status / Info module group (content_status) to the
Link Management module group (link_management), alongside
Redirects and QR Codes.
Since the "Link Management" parent module does not provide a page tree
navigation component, the linkvalidator module now brings its own
navigationComponent to retain the page tree.
Additionaly, the module identifier is changed from web_linkvalidator to
linkvalidator_checklinks.
Impact
The linkvalidator module now appears under Link Management in the
backend module menu instead of Status / Info.
An upgrade wizard ensures that backend users and groups with web_linkvalidator
permissions are migrated to linkvalidator_checklinks and automatically receive
access to the link_management parent module.
After TYPO3 v14.0, only new functionality with a solid migration path
can be added on top, with aiming for as little as possible breaking changes
after the initial v14.0 release on the way to LTS.
The TYPO3 Install Tool password can now utilize validators as defined
via the
$GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies']['installTool']['validators']
array. By default, this re-uses the default validator
\TYPO3\CMS\Core\PasswordPolicy\Validator\CorePasswordValidator with the configuration:
This will require 8 characters minimum (as it was before) and now also require
at least one upper-case, one lower-case, one digit and one special character.
The validator is utilized in both scenarios when setting the Install Tool password
via CLI (bin/typo3 install:password:set) as well as the Install Tool GUI via
Admin Tools > Settings > Change Install Tool Password in the TYPO3 backend.
If a password is auto-generated via the mentioned CLI command, by default it uses
8 characters. The password-length for adapted validator configurations can then be
specified with the new --password-length=XXX argument.
Impact
Maintainers now need to set secure Install Tool passwords and can configure
validation rules.
Existing Install Tool passwords are not affected, making this a non-breaking feature.
However, these should be revisited by maintainers and maybe set to a more secure
password.
To disable password policies (not recommended!), the configuration option
$GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies']['installTool']['validators']
can be set to an empty array (or null).
Hint
Please note, you can only override this configuration directive through the
additional.php configuration file, otherwise an empty array or null value
will trigger merging the DefaultConfiguration over the defined settings,
and re-adding default validators to your setup.
A new Link Management > QR Codes backend module has been introduced,
grouped alongside the existing Link Management > Redirects module.
The QR Codes module provides editors with an efficient way to generate reusable
QR codes for various purposes, such as printing them on promotional materials,
booth displays, or marketing collateral.
Each generated QR code contains a permanent, unique URL that never changes,
ensuring printed materials remain valid indefinitely. While the QR code URL
itself stays constant, the destination it redirects to can be updated at any
time, providing flexibility to adapt campaigns or redirect visitors to current
content without requiring reprints.
The module includes a convenient button to generate QR codes on demand, offering
multiple download options including different formats (PNG, SVG) and customizable
sizes to suit various use cases and printing requirements.
Impact
The new QR Code module enables users to create scannable QR codes that redirect
to any specified URL. This feature is particularly valuable for marketing campaigns,
events, and printed materials where maintaining flexibility in the destination URL
is essential while preserving the QR code itself.
Site sets can now define route enhancers in a dedicated route-enhancers.yaml
file. This allows extensions to provide route enhancers as part of their site set
configuration, which are automatically merged into the site configuration when
the set is used as a dependency.
The route enhancers from site sets are applied as presets. This means that
site-level route enhancer configuration takes precedence and can override
set-defined enhancers.
Usage
Create a route-enhancers.yaml file in your site set directory alongside
the config.yaml:
imports:-{resource:'route-enhancers/*.yaml'}routeEnhancers:# Additional enhancers can be defined here
Copied!
Merging behavior
Route enhancers from site sets are merged in dependency order. When a site
uses multiple sets, enhancers from earlier dependencies are loaded first,
and later sets can override them.
Site-level route enhancer configuration always takes precedence over
set-defined enhancers. This allows sites to customize or override
preset configurations from sets.
Scalar values from the site configuration override set-defined values,
while new keys are appended.
Impact
Extensions can now ship route enhancers as part of their site sets, providing
a streamlined way to configure routing for extension functionality. This is
particularly useful for extensions that require specific URL patterns, such
as sitemap extensions or API endpoints.
Invalid route enhancer configurations are handled gracefully: sets with
invalid route-enhancers.yaml files are skipped and logged, similar
to other set validation errors.
Feature: #107837 - Sitemap route enhancers provided via site set
The SEO extension now ships its sitemap route enhancers as part of
the typo3/seo-sitemap site set. When this set is used as a dependency,
the route enhancers for XML sitemaps are automatically configured.
This enables clean URLs for sitemaps out of the box:
/sitemap.xml - Main sitemap index
/sitemap-type/pages - Pages sitemap
Previously, these route enhancers had to be manually configured in each
site's config.yaml.
Impact
Sites using the typo3/seo-sitemap set no longer need to manually
configure sitemap route enhancers. The clean URLs are available
automatically.
Sites can still override or extend the route enhancers in their
site configuration if needed.
Feature: #107961 - Search translated pages in page tree
The page tree filter has been extended with the ability to search for pages
through their translated content. This enhancement makes it significantly easier
to find pages in multilingual TYPO3 installations, particularly when editors
work primarily with translated content.
The page tree filter now supports two translation search methods:
Search by translated page title: When a search phrase matches a translated page
title or nav_title, the corresponding default language page will be found and
displayed in the page tree.
Search by translation UID: When searching for a numeric page UID that belongs
to a translated page, the parent default language page will be found and displayed.
Both search methods work seamlessly alongside the existing search capabilities
(searching by page title, nav_title, or default language UID).
Configuration
Translation search is enabled by default and can be controlled in two ways:
User TSconfig
Administrators can control the availability of translation search via User TSconfig:
# Disable searching in translated pages for specific users/groups
options.pageTree.searchInTranslatedPages = 0
Copied!
User Preference
Individual backend users can toggle this setting using the page tree toolbar menu.
The preference is stored in the backend user's configuration, allowing each user
to customize their search behavior.
Visual Feedback
When a page is found through a translation match, a colored label is automatically
added to provide clear visual feedback:
Single translation match
Displays "Found in translation: [Language Name]"
Example: When searching for "Produkte", a page found via its German translation
shows "Found in translation: German"
Multiple translation matches
Displays "Found in multiple translations"
Example: When searching for "Home", a page with matching French and German
translations shows "Found in multiple translations"
Direct matches
Pages matching the search phrase directly (L=0) show "Search result"
Example: When searching for "Products", the English page titled "Products"
shows "Search result"
Combined matches
When a page matches both directly and through a translation, both labels
are displayed.
Example: Searching for "Home" finds a page titled "Home" with a German
translation "Startseite Home" - the page shows both labels.
Note
Search term highlighting works only for direct matches. For performance
reasons, it is activated only if the search yields fewer than 100 results,
and it requires the user to enter at least two characters or a number.
Flexibility for developers
Developers can still use the PSR-14 event
\TYPO3\CMS\Backend\Controller\Event\AfterPageTreeItemsPreparedEvent
to add custom labels or modify the prepared tree items before they are rendered.
Impact
Editors working in multilingual TYPO3 installations can now efficiently search
for pages using translated titles or translation UIDs. The visual labels provide
immediate feedback about how search results were matched, improving the user
experience when navigating complex page trees.
The feature respects user permissions (language restrictions from user groups)
and workspace context, ensuring that only accessible translations are searched.
Feature: #108344 - Allow number of decimals in
stdWrap.bytes function
The TypoScript function
stdWrap.bytes now accepts an additional
configuration parameter
decimals. It allows to explicitly define
the number of decimals in the resulting number representation. By default, the
number of decimals is derived from the formatted size.
In addition, the consumed PHP function
TYPO3\CMS\Core\Utility\GeneralUtility::formatSize is extended as well.
The additional parameter
$decimals is added and defaults to
null,
which results in the same behavior as for the TypoScript function
stdWrap.bytes.
Example
lib.fileSize = TEXT
lib.fileSize {
value = 123456
bytes = 1
bytes.decimals = 1
}
Copied!
Impact
By allowing to configure the number of decimals in
stdWrap.bytes,
integrators can now better adapt the output of the formatted size returned by
the TypoScript function. This was previously not possible by default and needed
some workarounds in TypoScript.
The TCA configuration config option type=datetime can now specify
the format=datetimesec format to offer a date/time picker for entering
a date (day, month, year) with a specific time (hour, minute, second).
Previously, only a datepicker for hour and minute was available,
even though the utilized component (Flatpickr) supports entering seconds.
This format can either be specified for dbType=datetime (native SQL datetime
columns based on a timestamp that always includes seconds) or
for the integer-based storage without a dbType option (UNIX timestamp).
<?phpreturn [
// ...'columns' => [
'meteor_impact' => [
'label' => 'Time of estimated impact',
'config' => [
'type' => 'datetime',
'format' => 'datetimesec',
'dbType' => 'datetime', // can also be omitted for integer-based storage'nullable' => true, // can also be false
],
],
],
// ...
];
Copied!
Hint
The format datetimesec is only allowed for:
empty
['config']['dbType'] (defaults to integer-based storage type)
['config']['dbType'] = 'datetime' (native date and time storage type)
Other types (date, time, timesec) do not support both components
and would yield unconsistent data.
Impact
Editors and integrators can now specify dates including seconds
in database record fields, if the fields are configured with the
new TCA config format datetimesec.
This can be set for any TCA field that already internally receives a
UNIX timestamp value.
-- For the editor who has everything, but seconds.
The class
\TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem is
the central entity to generate various previews of content elements.
Developers can either use the event
\TYPO3\CMS\Backend\View\Event\PageContentPreviewRenderingEvent
to generate a preview or implementing
\TYPO3\CMS\Backend\Preview\PreviewRendererInterface .
The new PSR-14 event
\TYPO3\CMS\Backend\View\Event\AfterPageContentPreviewRenderedEvent
can now be used to enrich the output generated by one of those.
Previously, the date-time picker used the selected locale of a backend user.
However, certain locale configurations might rather be a user preference.
For example, users may prefer an english backend but want to use a weekday
start on "Monday" instead of "Sunday" due to their cultural habits.
Thus, the "first day of a week" has been decoupled from the locale selection
and can be configured on a per-user setting. By default, it still inherits
the locale's default setting, if not changed (for example, english=sunday
and german=monday).
Inside the user settings and tab panel Backend appearance, a new dropdown
First day of week in calendar popups appears.
The setting is stored in both the persisted
be_users.uc preference blob,
as well as on the JavaScript-persistence side.
Impact
Editors can now choose the first day of a week as a user preference, independent
from locale selection.
Fluid 4.3 introduced the concept of components to Fluid (see
Components). Since then, it
was already possible to use components in TYPO3 projects by creating a custom
ComponentCollection class that essentially connects a folder of template files
to a Fluid ViewHelper namespace. Using that class it was also possible to use an
alternative folder structure for a component collection and to allow passing
arbitrary arguments to components within that collection.
Now it is possible to define component collections purely with configuration.
For the most common use cases, it is no longer necessary to create a custom
PHP class, which makes it much easier for integrators to setup components in
TYPO3 projects.
Registering component collections
The new extension-level configuration file
Configuration/Fluid/ComponentCollections.php is introduced, which allows
extensions to register one or multiple new component collections. It is also possible
to extend existing collections registered by other extensions (such as adding template
paths to override components defined by another extension).
By default, component collections use a folder structure that requires a
separate folder per component. This is handy if you want to put other
files right next to your component template, such as the matching CSS
or JS file, or even a custom language file. Using the example above,
<my:organism.header.navigation /> would point to
EXT:my_extension/Resources/Private/Components/Organism/Header/Navigation/Navigation.fluid.html.
If not otherwise specified, components use a strict API, meaning that all
arguments that are passed to a component need to be defined with
<f:argument> in the component template.
Both defaults can be adjusted per collection by providing configuration options:
templateNamePattern allows you to use a different folder structure, available
variables are {path} and {name}. For
<my:organism.header.navigation>,
{path} would be Organism/Header and {name} would be Navigation.
setting additionalArgumentsAllowed to true allows passing undefined arguments
to components.
Using this example
<my:organism.header.navigation /> would point to
EXT:my_extension/Resources/Private/Components/Organism/Header/Navigation.fluid.html
(note the missing Navigation folder).
The example also demonstrates that components can (and should) use other components, in this
case
<my:atom.icon>.
Depending on the use case, it might also make sense to pass the output of one component
to another component via a slot:
You can learn more about components in
Defining Components. Note
that this is part of the documentation of Fluid Standalone, which means that it doesn't mention
TYPO3 specifics.
Migration and co-existence with class-based collections
Configuration-based and class-based component collections can be used side by side.
For more advanced use cases, it might still be best to ship a custom class to define
a component collection. However, most use cases can easily be migrated to the
configuration-based approach, since they usually just consist of boilerplate code
around the configuration options.
Since the new approach is not available in TYPO3 13, it is possible to ship both
variants to provide backwards-compatibility: If a specific component collection is
defined both via class and via configuration, in TYPO3 13 the class will be used,
while in TYPO3 14 the configuration will be used and the class will be ignored completely.
Extending component collections from other extensions
It is possible to extend the configuration of other extensions using the
introduced configuration file. This allows integrators to merge their own set of
components into an existing component collection:
For template paths, the familiar rule applies: They will be sorted by their
keys and will be processed in reverse order. In this example, if my_extension
defines a component that already exists in vendor_extension, it will override
the original component in vendor_extension.
Impact
Fluid component collections no longer need to be defined by creating a custom
class, but can now be registered purely by configuration. Existing class-based
collections will continue to work. If a collection namespace is registered both
by a class and by configuration, the configuration overrules the class and any
custom code in the class is ignored.
Feature: #108508 - PSR-14 events for Fluid components
Three PSR-14 events have been added to influence the processing and rendering
of Fluid components that are registered using the new configuration file
(see Fluid components integration).
ModifyComponentDefinitionEvent
The
ModifyComponentDefinitionEvent can be
used to modify the definition of a component before it's written to the cache.
Component definitions must not have any dependencies on runtime information, as
they might be used for static analysis or IDE auto-completion. Due
to the component definitions cache, this is already enforced, as the registered
events are only executed once and not on every request.
The
ProvideStaticVariablesToComponentEvent can
be used to inject additional static variables into component templates. As with the
ModifyComponentDefinitionEvent , these variables
must not have any dependencies on runtime information, as they might be used for
static analysis or IDE auto-completion. The
RenderComponentEvent can be used to add variables
with runtime dependencies.
Valid use cases for this event might be:
providing static (!) design tokens (colors, icons, ...) to all components in a collection
generating prefix strings based on the component's name
<?phpdeclare(strict_types=1);
namespaceMyVendor\MyExtension\EventListener;
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Fluid\Event\ProvideStaticVariablesToComponentEvent;
#[AsEventListener]final readonly classProvideStaticVariablesToComponentListener{
publicfunction__invoke(ProvideStaticVariablesToComponentEvent $event): void{
// Provide design tokens to all components in a collectionif ($event->getComponentCollection()->getNamespace() === 'MyVendor\\MyExtension\\Components') {
$event->setStaticVariables([
...$event->getStaticVariables(),
'designTokens' => [
'color1' => '#abcdef',
'color2' => '#123456',
],
]);
}
}
}
Copied!
RenderComponentEvent
The
RenderComponentEvent can be used to alter or
replace the rendering of Fluid components. There are three possible use cases:
fully take over the rendering of components by filling the
$renderedContent with
$event->setRenderedContent(). The first event that does this skips all following
event listeners.
provide additional arguments (= variables in the component template) or slots to
the component with
$event->setArguments()/
$event->setSlots().
execute additional code that doesn't influence the component rendering directly, e. g.
adding certain frontend assets to the page automatically.
The extension-level configuration file Configuration/Fluid/Namespaces.php
is introduced, which enables a structured way to register and extend global
Fluid namespaces. This replaces the old configuration in
$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces'] , see
deprecation.
TYPO3 reads and merges Configuration/Fluid/Namespaces.php files from all
loaded extensions in the usual loading order, which can be manipulated by
declaring dependencies in composer.json and possibly ext_emconf.php. If an
extension registers a namespace that has already been registered by another
extension, these namespaces will be merged by Fluid. This allows extensions
to override ViewHelpers of another extension selectively.
Namespaces are processed in reverse order, which means that
<myext:demo /> would first check for
EXT:my_extension2/Classes/ViewHelpers/DemoViewHelper.php, and would
fall back to EXT:my_extension1/Classes/ViewHelpers/DemoViewHelper.php.
PSR-14 event to modify namespaces
The new
ModifyNamespacesEvent is
introduced, which allows modification of the whole namespaces array
before it is being passed to Fluid. This allows for example to:
completely redefine an existing namespace (instead of extending it)
Note that namespaces might still be imported locally from within a
template file, which is unaffected by this event.
Backwards-compatible namespaces in extensions
There are several ways to provide backwards-compatible global namespaces
in extensions, depending on the concrete use case:
preferred: Define namespace in TYPO3_CONF_VARS (with version check) and
in new Namespaces.php. This means that in TYPO3 v14 installations the new
Namespaces.php can already be used to extend the namespace.
alternative: Define namespace both in TYPO3_CONF_VARS (without version
check) and in new Namespaces.php. This means however that the namespace
can only be extended with TYPO3_CONF_VARS, not with the new Namespaces.php.
keep TYPO3_CONF_VARS until support for < v14 is dropped by the extension.
This can only be extended with TYPO3_CONF_VARS as well.
implement own merging logic in
ModifyNamespacesEvent if necessary.
Namespaces.php can only extend namespaces defined in Namespaces.php.
TYPO3_CONF_VARS can extend both TYPO3_CONF_VARS and Namespaces.php.
ModifyNamespacesEvent can modify everything.
Impact
Extensions can now register global Fluid namespaces in a dedicated
configuration file Configuration/Fluid/Namespaces.php. The old
TYPO3_CONF_VARS registration can be used for backwards compatibility.
A new default theme has been added. Its main purpose
is to build new sites more rapidly in TYPO3 v14.
The name Camino (spanish "the way") was chosen as v14 development
series, marking the first steps on the way to a glorious future for
TYPO3.
It serves to show that a new site in TYPO3 can be set up within
minutes, being customizable (at least in a limited way), without
having to rely on any external library, and to avoid ANY error
message for newcomers to TYPO3.
Camino will be packaged for new installations by default
and can be activated for new sites alongside existing sites.
The theme shows off basic page structures as well as
some default content elements - completely without any
third-party dependencies nor requiring the "Fluid styled content"
extension.
The rough structure of the theme:
Four different color schemes can be selected in the site settings
The main menu structure and footer structure can be configured
on the root page inside the backend layout / colPos positions
Common content elements for a Hero area and regular content
is available
Minimal configuration is handled in TypoScript
The theme is not meant to evolve within TYPO3, as it will
be moved to TER/Packagist/GitHub in a separate repository
in v15.0. In v15.x a new theme will be added with more
modern features, and it will utilize features that will
be added during v15.x development.
The theme is 100% optional and encapsulated - existing setups
will have no interference.
The theme will be fine-tuned before the TYPO3 v14 LTS release,
specific documentation for its features will be provided in
the theme's documentation.
Installation
For now, the theme is the same as a regular TYPO3 extension.
On fresh classic-mode installations, the theme will be enabled
by default. A new site and a first page will be created, which
can be used to insert content.
On Composer-mode installations, the package typo3/theme-camino
needs to be required, and a fresh installation will also create
the site and a first page.
For existing installations, the theme must be enabled first
(depending on the TYPO3 setup) either via extension manager,
or by requiring the Composer package.
Once the "extension" is activated, the steps to enable
the Camino frontend are:
Create a new root page in the Content > Layout
page tree. Be sure to edit the created page properties and enable
Behavior > Use as Root Page .
This will automatically create a new Site. Check Sites > Setup
to see the created Site. Edit that Site's properties. In
General > Sets for this Site ensure that the Theme: Camino
Site set is added as a dependency.
(Depending on the TYPO3 setup, TYPO3 caches might need to be cleared)
Then edit the created root page properties via Content > Layout again,
and pick Camino: Start page from the tab
Appearance > Backend Layout (this page only).
Now the Camino theme will be applied to the site. Content can be added
in the specific columns, and sub-pages can be created (ensure to set
the backend layout of subpages to the appropriate Camino backend layout,
either Camino: Content page (full-width) or
Camino: Content page (with sidebar).
A custom logo can be set in root page properties Appearance, just
above the backend layout picker.
In Sites > Setup, the Site set configuration can be accessed
to adjust the color scheme and further options.
Impact
A default frontend theme is now available. It can be easily
activated in the TYPO3 installation process, or also be enabled
afterwards.
It is dependency-free and provides and utilizes site sets.
Feature: #108558 - Add original file name to SanitizeFileNameEvent
The PSR-14 event
\TYPO3\CMS\Core\Resource\Event\SanitizeFileNameEvent
does now also provide the original file name. Event listeners can use the
original file name to perform custom string replacements (e.g. space to
hyphen instead of underscore) for the sanitized file name.
Impact
It is now possible to retrieve the original file name in the PSR-14 event
\TYPO3\CMS\Core\Resource\Event\SanitizeFileNameEvent .
Feature: #108623 - Allow content element restrictions per colPos
Backend layouts have been extended with options to allow only
configured types of content elements (referencing
tt_content.CType with names like "text",
"textmedia", "felogin_pi1" and so on) in backend layout columns (
colPos): The two
keys
allowedContentTypes and
disallowedContentTypes add allow and
deny lists on column level. These settings can be set with Page TSConfig based backend layouts,
Database based backend layouts do not allow configuring this value at the moment, but this will
be added soon.
Example for a backend layout with two rows and two columns configured using Page TSConfig:
mod.web_layout.BackendLayouts {
exampleKey {
title = Example
config {
backend_layout {
colCount = 1
rowCount = 2
rows {
1 {
columns {
1 {
identifier = main
name = Main content
colPos = 0
allowedContentTypes = header, textmedia
}
2 {
identifier = right
name = Panel right
colPos = 1
allowedContentTypes = my_custom_cta
}
}
}
2 {
columns {
1 {
identifier footer
name = Footer
colpos = 2
colspan = 2
disallowedContentTypes = header
}
}
}
}
}
}
Copied!
The implementation adapts the "New content element wizard" to show only allowed (or not disallowed)
content elements types when adding a content element to a column. When editing records, the select
boxes "Type" and Column position" are reduced to not allow invalid values based on the configuration.
Similar logic is applied when moving and copying content elements.
The feature has been created with extension content_defender
in mind. This extension by Nicole Hummel has been around for many years and found huge adoption rates
within the community. In comparison to content_defender, the core configuration is slightly simplified
and the core implementation does not provide the additional content_defender feature to restrict the number
of elements per column (
maxitems).
The core implementation supports the content_defender syntax using the arrays
allowed.CType and
disallowed.CType. With the example below,
allowed.CType is internally mapped to
allowedContentTypes. When both
allowed.CType and
allowedContentTypes
are given,
allowed.CType is ignored.
Codewise, the PSR-14 event
ManipulateBackendLayoutColPosConfigurationForPageEvent has been added. It allows manipulation
of the calculated column configuration. It is marked
@internal and thus needs to be used with care since it may
change in the future without further note: The event is not dispatched as systematically as it should be, but refactoring
the surrounding code can probably not be provided with TYPO3 v14 anymore. Extensions like ext:container however
must be able to adapt column configuration with TYPO3 v14 already. The decisions was to provide an event, but to mark
it internal for the time being, declaring it as "use at your own risk" if you know what you are doing and within extensions
that set up proper automatic testing to find issues if the core changes internals.
Impact
Backend layout columns can now restrict, which content element types are allowed or disallowed inside of it.
Feature: #108627 - Allow adding inline language domains to JavaScript
The new method
PageRenderer->addInlineLanguageDomain() allows loading
all labels from a language domain and making them available in JavaScript
via the
TYPO3.lang object.
The domain name follows the format extension.domain (e.g. core.common,
core.modules.media).
The language file is resolved automatically by the LanguageService, resolving
to files like EXT:core/Resources/Private/Language/locallang_common.xlf
and EXT:core/Resources/Private/Language/Modules/media.xlf.
Labels are automatically prefixed with the domain name and accessible as
TYPO3.lang['domain:key'], e.g.
TYPO3.lang['core.common:notAvailableAbbreviation'].
// Access a label from the domainconst label = TYPO3.lang['myextension.frontend:button.submit'];
Copied!
Impact
Previously, it was possible to load entire language files using
addInlineLanguageLabelFile() (which is still available), but labels
were added without any prefix.
This could lead to naming conflicts when multiple extensions used the same
label keys, potentially overriding each other's translations.
With the new domain-based approach, all labels are automatically prefixed
with the domain name (e.g. myextension.frontend:label.key). This provides
a unified, namespaced access pattern that eliminates the risk of collisions
between labels from different extensions or language files.
Feature: #108663 - Adjust visibility of form elements in Form Editor
The Form Editor now provides the ability to configure the visibility of form
elements. This allows form administrators to control which form elements are
displayed or hidden in the form, providing better control over the form structure
and user experience.
Usage
When editing a form element in the Form Editor, a new "Visibility" option is
available in the element's configuration panel. This option allows you to:
Show the element (default behavior)
Hide the element
The visibility setting is stored in the form definition and is evaluated when
the form is rendered on the frontend.
Example for Integrators
If you want to extend your own form elements with the visibility feature, you
need to add the following configuration to the element definition:
prototypes:standard:formElementsDefinition:CustomElement:formEditor:editors:# Choose a key / position according to your own needs240:identifier:enabledtemplateName:Inspector-CheckboxEditorlabel:formEditor.elements.FormElement.editor.enabled.labelpropertyPath:renderingOptions.enabled
Copied!
Impact
This feature improves the usability of the Form Editor and makes it more accessible
to non-technical users who need to manage form visibility.
Feature: #108694 - Context menu items to edit site configuration for pages
Two new context menu items have been added for pages that are site roots:
Edit Site: Opens the site configuration editor for the site associated
with the page.
Edit Site Settings: Opens the site settings editor for the site.
These items appear directly after the "Edit" item in the context menu and are
only visible to admin users on pages that have a site configuration.
Impact
Admin users can now quickly access the site configuration and site settings
directly from the context menu when right-clicking on a site root page. This
improves the workflow for managing sites without needing to navigate to the
Site Management module first.
Deprecation: #108086 - Raise deprecation error on using deprecated labels
Until now, localization labels marked as deprecated, for example by using the
x-unused-since attribute in XLIFF 1.2 or the
subState="deprecated"
attribute in XLIFF 2.0, did not trigger a runtime warning. As a result,
integrators and developers had no automatic way to detect deprecated labels
that were still in use.
With this change, TYPO3 now triggers an
E_USER_DEPRECATED error when a
deprecated label is first written to the localization cache.
Impacted formats
XLIFF 1.2
<trans-unitid="deprecated_label"x-unused-since="4.5"><source>This label is deprecated</source></trans-unit>
Copied!
XLIFF 2.0
<unitid="label5"><segmentsubState="deprecated"><source>This is label #5 (deprecated in English)</source></segment></unit>
Copied!
Custom loaders
When a label identifier ends with .x-unused, TYPO3 raises a deprecation
warning if the label is referenced, regardless of whether the reference includes
the .x-unused suffix.
Custom loaders can use this behaviour to also provide mechanisms to deprecate
labels.
Fallback behaviour
If a label is deprecated in the fallback language but is overridden in the
current locale without a deprecation marker, no deprecation warning is raised.
If a label is deprecated only in the current locale, TYPO3 falls back to the
default language and does not raise a deprecation warning.
A deprecation warning is triggered the first time a deprecated label is written
to cache. Subsequent resolutions of the same label use the cached entry and do
not trigger additional warnings until the cache is cleared.
The following usages emit a deprecation warning when a deprecated label is
resolved.
The Extension Scanner does not detect the usage of deprecated localization
labels. Developers must rely on runtime deprecation logs to identify these
occurrences.
Impact
Integrators and developers may encounter new deprecation warnings during runtime
or in the deprecation log when deprecated localization labels are used. The
warnings help identify and replace outdated labels before they are removed in a
future TYPO3 version.
Affected installations
All TYPO3 installations that use localization labels marked as deprecated are
affected. This includes custom extensions, site packages, or integrations that
still reference deprecated labels from system or extension language files.
When a custom extension or project defines a label whose identifier ends with
.x-unused, that label is considered deprecated regardless of the loader
used. Such usage is technically possible but generally unlikely.
Migration
Review the deprecation log for warnings related to localization labels. Note
that deprecations are only written the first time a label is used after
deleting the cache.
Replace usages of deprecated labels with non-deprecated ones where possible.
If required, override deprecated labels in a custom locale without a
deprecation marker.
Remove or update labels marked with x-unused-since in XLIFF 1.2 or with
subState="deprecated" in XLIFF 2.0 when they are no longer needed.
Avoid defining labels with identifiers ending in .x-unused.
Deprecation: #108524 - Fluid namespaces in TYPO3_CONF_VARS
Registering global namespaces for Fluid templates in TYPO3_CONF_VARS has
been deprecated.
Impact
Defined namespaces in
$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']
will no longer be registered in TYPO3 v15.
Affected installations
Installations and extensions that use
$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']
to define additional global namespaces or extend existing global namespaces.
Migration
Standard use cases, such as registering a new global namespace or extending
an existing one, can be migrated to the dedicated Configuration/Fluid/Namespaces.php
configuration file.
The exception
\TYPO3\CMS\Core\Console\CommandNameAlreadyInUseException
is unused within TYPO3 Core and has been deprecated.
Impact
Creating a new instance of
\TYPO3\CMS\Core\Console\CommandNameAlreadyInUseException
will trigger a PHP deprecation message.
Affected installations
TYPO3 installations with custom extensions using this exception.
Migration
As the exception is unused in TYPO3 Core, there is no direct replacement.
Extensions relying on this exception should implement their own exception
if needed.
Important: #107971 - XLF files now use 2-space indentation
TYPO3 Core XLF (XLIFF) translation files now consistently use 2-space
indentation instead of tabs. This aligns with the formatting used by Crowdin
and by PHP's
\DOMDocument. The .editorconfig file has been updated
accordingly.
In addition to indentation, all XLF files have been normalized using a unified
XML formatter. This ensures consistent XML declaration, attribute ordering, and
whitespace structure across all XLF files in the Core.
Checking and normalizing XLF formatting
A new script has been introduced to check and normalize XLF formatting.
Checking formatting via runTests.sh
To check whether XLF files have correct formatting (dry-run, no modifications):
./Build/Scripts/runTests.sh -s normalizeXliff -n
Copied!
This command scans all XLF files in typo3/sysext/ and reports files that
would be changed.
Normalizing XLF files via runTests.sh
To normalize all XLF files in-place:
./Build/Scripts/runTests.sh -s normalizeXliff
Copied!
This command applies consistent indentation and XML normalization to all XLF
files in typo3/sysext/.
Using the standalone script
The script can also be run directly. Requires PHP 8.2+ with DOM and intl
extensions enabled.
# Show help
./Build/Scripts/xliffNormalizer.php --help# Check files only (dry-run)
./Build/Scripts/xliffNormalizer.php --root typo3/sysext --dry-run
# Normalize files in place
./Build/Scripts/xliffNormalizer.php --root typo3/sysext
# Check a custom directory
./Build/Scripts/xliffNormalizer.php --root path/to/xlf/files --dry-run
Copied!
Impact
Extension developers should update their XLF files to use 2-space indentation
and expect normalized XML formatting.
The provided script can also be used within extensions to keep XLF files
consistent with TYPO3 Core standards.
The former page type "External link" has been renamed to "Link" and now fully
supports all typolink capabilities. It now uses the field
link.
Since the field url was only used by this former page type, it has been
removed.
Impact
Custom code that expects the field url to exist in the table
pages
will fail as the field is not found anymore.
Affected installations
TYPO3 projects that used the former page type External URL to link to
resources other than true external URLs, for example sections such as #abc.
TYPO3 projects with custom code that expects the field url to exist
in the table
pages.
Migration
The upgrade wizard "Migrate links of pages of type link" automatically migrates
pages of the former type External URL to the new page type Link.
It migrates all links that resolve to external URLs. If your project used the
External URL field for other purposes — for example, to link sections such
as #abc — you must migrate those links manually.
If your project contains custom code that expects the field
url to exist
in the table
pages, you can reintroduce this field via a TCA override,
for example:
<?php
defined('TYPO3') || die('Access restricted.');
$GLOBALS['TCA']['pages']['columns']['url'] = [
'label' => 'External URL',
'config' => [
'type' => 'input',
'size' => 50,
'max' => 255,
'required' => true,
'eval' => 'trim',
'softref' => 'url',
'behaviour' => [
'allowLanguageSynchronization' => true,
],
],
];
// Adding column to a existing or new palette and configure showitem,
// for a specific doktype the field is still required, for example:
$GLOBALS['TCA']['pages']['palettes]['custom_url'] = [
'showitem' => 'url',
];
$GLOBALS['TCA']['pages']['types'][$customDokTypeValue] = [
'showitem' => [
--div--;core.form.tabs:general,
doktype,
--palette--;;title,
--palette--;;custom_url,
],
];
Copied!
Adding it back at least ensures that the database field is not renamed and
dropped by the Database Analyzer. In case TCA is not required while keeping
the database field it could be added to a extension ext_tables.sql file.
The
\TYPO3\CMS\Core\Collection\SortableCollectionInterface has been
removed from the TYPO3 Core.
This interface was never properly implemented and served no purpose in the
codebase. It defined methods for sorting collections via callback functions
and moving items within collections, but no concrete implementations existed.
The interface defined the following methods:
usort($callbackFunction) – for sorting a collection via a given callback function
moveItemAt($currentPosition, $newPosition = 0) – for moving items within the collection
Impact
Any code that implements or references
\SortableCollectionInterface
will trigger a PHP fatal error.
Since this interface was never implemented in the TYPO3 Core and had no real-world usage,
the impact should be minimal for most installations.
Affected installations
Installations with custom extensions that implement or reference the
\SortableCollectionInterface are affected.
Migration
Remove any references to
\SortableCollectionInterface
from your code.
If you need sortable collection functionality, implement your own sorting logic
directly in your collection classes, or use PHP's built-in array sorting functions
such as
usort(),
uasort(), or
uksort().
Breaking: #68303 - Make tt_content imagewidth/imageheight nullable
The Page Module preview rendering has been refactored to use the Record API
internally instead of accessing raw database arrays. This affects both custom
preview renderers that extend
StandardContentPreviewRenderer and
Fluid-based preview templates.
The method signature has changed for
\TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer :
StandardContentPreviewRenderer->linkEditContent() now expects a
RecordInterface object as the second
$record parameter
instead of an array
The
\TYPO3\CMS\Backend\View\Event\PageContentPreviewRenderingEvent has
also been updated:
PageContentPreviewRenderingEvent->getRecord() now returns a
RecordInterface object instead of an array
PageContentPreviewRenderingEvent->setRecord() now expects a
RecordInterface object instead of an array
Additionally, the @internal class
\TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem has been
updated to work with Record objects:
The constructor of
GridColumnItem
now requires a
RecordInterface object as
the third
$record parameter instead of an array
GridColumnItem->getRecord() now returns a
RecordInterface object
instead of an array
GridColumnItem->setRecord() now expects a
RecordInterface object
instead of an array
A new method
GridColumnItem->getRow() has been added to access the raw
database array if needed
For Fluid-based content element previews, the template variables have changed.
Previously, all record fields were passed as individual variables to the Fluid
template. Now, only a single
{record} variable is passed, which is a
RecordInterface object providing access to all record data through
the Record API.
Using the
{pi_flexform_transformed} variable in Fluid-based content
element previews no longer works. The resolved flex form can be directly
accessed on the
RecordInterface object, for example via
{record.pi_flexform}. The value is a
FlexFormFieldValues
object, which properly groups the fields by their sheets.
Impact
Extensions that extend
StandardContentPreviewRenderer and override the
linkEditContent() method will need to update their method signature.
Extensions that access
GridColumnItem->getRecord() expecting an array will
need to update their code to work with
RecordInterface objects.
Extensions using event listeners for
PageContentPreviewRenderingEvent that
access the record via
getRecord() expecting an array will need to update
their code to work with
RecordInterface objects.
Custom Fluid templates for content element preview rendering must be updated to
use the
{record} variable instead of accessing individual field variables.
Affected installations
All installations with extensions that:
Extend
StandardContentPreviewRenderer and call or override the
linkEditContent() method
Instantiate
GridColumnItem or call
GridColumnItem->getRecord() /
GridColumnItem->setRecord()
Register event listeners for
PageContentPreviewRenderingEvent
Use custom Fluid templates for content element preview rendering via PageTSconfig
mod.web_layout.tt_content.preview.[recordType]
Migration
For custom preview renderers extending
StandardContentPreviewRenderer :
Update the method signature of
linkEditContent() to accept a
RecordInterface object:
<f:variablename="path"value="s_messages/settings" /><small>{record.pi_flexform.{path}.welcome_header}</small><!-- or --><small>{record.pi_flexform.sheets.s_messages.settings.welcome_header}</small>
The backend submodule Database Relations within DB Check provided
information about potentially broken database relations. However, the
information it displayed was very limited and barely helpful. In addition, the
entire module and its code have not received any meaningful updates in recent
years.
Due to this, the module has been removed.
Impact
The module has been removed. Existing links and stored bookmarks will no longer
work.
The hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterBuildingFinished']
has been removed in favor of the more powerful PSR-14 events
\TYPO3\CMS\Form\Event\BeforeRenderableIsAddedToFormEvent and
\TYPO3\CMS\Form\Event\AfterFormIsBuiltEvent .
Impact
Any hook implementation registered under this identifier will no longer be
executed in TYPO3 v14.0 and later.
Affected installations
TYPO3 installations with custom extensions that implement this hook are
affected. The extension scanner reports such usages as a weak match.
Migration
The hook has been removed without a deprecation phase to allow extensions to
remain compatible with both TYPO3 v13 (using the hook) and v14+ (using the new
events). Implementing the PSR-14 events provides the same or greater control
over form rendering.
Extbase's
PropertyMappingConfigurationInterface
is now fully typed with native PHP types.
Impact
Existing implementations will no longer work without adjustment. According to
the Liskov Substitution Principle, all implementations must follow the updated
method signatures and type restrictions defined in the interface.
Affected installations
TYPO3 installations with custom PHP code implementing a custom
PropertyMappingConfiguration are
affected. Such cases are rare.
Migration
Add the required native PHP types to all custom implementations of the
PropertyMappingConfigurationInterface
to fulfill the updated interface definition.
Breaking: #101392 - getIdentifier() and setIdentifier() from AbstractFile removed
When using the PHP API of the File Abstraction Layer (FAL), several classes
are involved in representing file objects.
In addition to the
FileInterface , there is also the
AbstractFile class, from which most file-
related classes inherit.
To ensure stricter type consistency, the abstract class no longer implements
the methods
getIdentifier() and
setIdentifier(). Implementing
these methods is now the responsibility of each subclass.
The methods are now implemented in the respective concrete classes inheriting
from
AbstractFile .
Impact
In the unlikely case that the TYPO3 File Abstraction Layer has been extended
with custom PHP classes derived from
AbstractFile , this change will
cause a fatal PHP error, as the new abstract methods
getIdentifier() and
setIdentifier() must be implemented by the subclass.
Affected installations
TYPO3 installations that include custom code extending the File Abstraction
Layer are affected. Such cases are considered highly uncommon.
Migration
Implement the two methods
getIdentifier() and
setIdentifier() in
any custom file class extending
AbstractFile .
This can also be implemented in older TYPO3 versions to ensure forward
compatibility with TYPO3 v14 and later.
Breaking: #103141 - Use doctrine GUID type for TCA type=uuid
The TYPO3 Doctrine implementation can now handle the proper
matching database type for UUID's.
Postgresql natively supports the UUID data type and is way faster
than the prior VARCHAR(36) generated from the string type.
The Doctrine DBAL GUID type uses CHAR(26) as the fixed field column size
for non-postgres databases, which is compatible as long
as valid UUID values were persisted in the configured database
table.
Impact
TYPO3 database tables can now natively properly apply the suitable
GUID column type when configured as TCA type=uuid.
A prerequisite for this is that you only have valid UUID's stored
in the database table, otherwise the database update will report
an error when applying migrations.
Affected installations
Projects with database table columns set as TCA type=uuid.
Error are likely to occur, if invalid UUID data is stored
in field columns configured with this type.
Migration
Use the database analyzer to migrate the database fields.
Invalid values are not migrated and need to be manually
cleaned up in affected instances.
Breaking: #103910 - Change logout handling in EXT:felogin
The logout handling has been adjusted to correctly dispatch the PSR-14 event
\TYPO3\CMS\FrontendLogin\Event\LogoutConfirmedEvent when a logout
redirect is configured. The
actionUri variable has been removed, and the
logout template has been updated to reflect this change, including correct use
of the
noredirect functionality.
Impact
The PSR-14 event
LogoutConfirmedEvent is now
correctly dispatched when a logout redirect is configured. Additionally, the
noredirect parameter is now evaluated during logout.
Affected installations
TYPO3 installations using EXT:felogin with a custom Fluid template for
the logout form.
Migration
The
{actionUri} variable is no longer available and must be removed
from custom templates.
Before:
Fluid template adjustment (before)
<!-- Before --><f:formaction="login"actionUri="{actionUri}"target="_top"fieldNamePrefix="">
Redirect handling for the
logoutAction has been removed.
Impact
The
logoutAction no longer performs any configured redirect via plugin
settings or GET parameters.
Affected installations
TYPO3 installations relying on redirect handling within
logoutAction are affected.
Migration
No migration is required. The previous redirect logic in
logoutAction() has been removed because it was incorrect: it ignored
the
showLogoutFormAfterLogin setting and could trigger an unintended
redirect even when this option was enabled.
Valid redirects are already processed correctly by
loginAction() and
overviewAction(), so the faulty branch was
removed without replacement.
Breaking: #104422 - Move GET parameters in sitemap into namespace
The names of the GET parameters used in the sitemap generated by EXT:seo have
been changed from page and sitemap to tx_seo[page] and tx_seo[sitemap],
respectively.
Impact
Applications, routing configurations, and third-party tools that rely on the
parameters being named page and sitemap will break.
Affected installations
This affects installations that use the arguments page and sitemap or
override the sitemap templates of EXT:seo.
Migration
If the arguments are mapped in the routing configuration, the code needs to
be slightly adapted. A working example:
If the templates in EXT:seo/Resources/Private/Templates/XmlSitemap/Index.xml
have been modified, adjust the generated links to match the original ones.
If the URL to a single sitemap has been provided to a third-party tool such as
a crawler or search engine, it must be re-added using the new URL.
The following methods changed signature according to previous deprecations in v13 at the end of the argument list:
\TYPO3\CMS\Backend\View\BackendLayout\DataProviderContext->__construct() - All arguments are now mandatory (Deprecation entry)
\TYPO3\CMS\Core\Imaging\IconFactory->getIcon() (argument 4 is now of type
\TYPO3\CMS\Core\Imaging\IconState|null) (Deprecation entry)
\TYPO3\CMS\Core\Resource\AbstractFile->copyTo() (argument 3 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\AbstractFile->moveTo() (argument 3 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\AbstractFile->rename() (argument 2 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\FileInterface->rename() (argument 2 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\FileReference->rename() (argument 2 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\Folder->addFile() (argument 3 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\Folder->addUploadedFile() (argument 2 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\Folder->copyTo() (argument 3 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\Folder->moveTo() (argument 3 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\InaccessibleFolder->addFile() (argument 3 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\InaccessibleFolder->addUploadedFile() (argument 2 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\InaccessibleFolder->copyTo() (argument 3 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\InaccessibleFolder->moveTo() (argument 3 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\ResourceStorage->addFile() (argument 4 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\ResourceStorage->addUploadedFile() (argument 4 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\ResourceStorage->copyFile() (argument 4 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\ResourceStorage->copyFolder() (argument 4 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\ResourceStorage->moveFile() (argument 4 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\ResourceStorage->moveFolder() (argument 4 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Resource\ResourceStorage->renameFile() (argument 3 is now of type
\TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior ) (Deprecation entry)
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin() (argument 2
$type and 3
$extensionKey have been dropped) (Deprecation entry)
The following public class properties have been dropped:
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['SecondDatabase']['driverMiddlewares']['driver-middleware-identifier']
must be an array, not a class string (Deprecation entry)
Accepting arrays returned by
readFileContent() in Indexed Search external parsers (Deprecation entry)
Allowing instantiation of
\TYPO3\CMS\Core\Imaging\IconRegistry in ext_localconf.php (Deprecation entry)
Accepting a comma-separated list of fields as value for the columnsOnly parameter (Deprecation entry)
Support for extbase repository magic
findByX(),
findOneByX() and
countByX() methods (Deprecation entry)
Fluid view helpers that extend
\TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper
should no longer register
class attribute and should rely on attribute auto registration
for the error class to be added correctly. (Deprecation entry)
The legacy backend entry point typo3/index.php has been removed along with handling of composer.json
setting extra.typo3/cms.install-deprecated-typo3-index-php(Deprecation entry)
The following upgrade wizards have been removed:
Install extension "fe_login_mode" from TER
Migrate base and path to the new identifier property of the "sys_filemounts" table
Migrate site settings to separate file
Set workspace records in table "sys_template" to deleted
Migrate backend user and groups to new module names
Migrate backend groups "explicit_allowdeny" field to simplified format
Migrate sys_log entries to a JSON formatted value
Migrate storage and folder to the new folder_identifier property of the "sys_file_collection" table
The following JavaScript method behaviours have changed:
FormEngineValidation.markFieldAsChanged() always requires
HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement to be passed as first argument (Deprecation entry)
FormEngineValidation.validateField() always requires
HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement to be passed as first argument (Deprecation entry)
The following JavaScript method has been removed:
updateQueryStringParameter() of
@typo3/backend/utility.js(Deprecation entry)
The following smooth migration for JavaScript modules have been removed:
@typo3/backend/page-tree/page-tree-element to
@typo3/backend/tree/page-tree-element(Deprecation entry)
The following localization XLIFF files have been removed:
The
DataHandler PHP API has been
extended to support both qualified and unqualified ISO8601 date formats, in
order to correctly process supplied timezone offsets when provided.
Qualified ISO8601: Includes an explicit timezone offset (for example,
1999-12-11T10:09:00+01:00 or 1999-12-11T10:09:00Z)
Unqualified ISO8601: Omits timezone offsets, representing LOCALTIME
(for example, 1999-12-11T10:09:00)
Previously, TYPO3 incorrectly used qualified ISO8601 with Z (UTC+00:00) to
denote LOCALTIME and applied the server's timezone offset, which led to
misinterpretations when another timezone offset was provided, or when real
UTC-0 was intended instead of LOCALTIME. Now, timezone offsets are accurately
applied if supplied, and are based on server localtime if omitted.
TYPO3 will use unqualified ISO8601 dates internally for communication between
FormEngine and the DataHandler API, ensuring timezone offsets are correctly
processed – instead of being shifted – when supplied to the
DataHandler API.
In essence, this means that existing workarounds for previously applied
timezone offsets should be reviewed and removed.
Impact
TYPO3 now provides accurate and consistent handling of ISO8601 dates,
eliminating previous issues related to timezone interpretation and LOCALTIME
representation.
Affected installations
Installations with custom TYPO3 extensions that invoke the
DataHandler API with data for
type="datetime" fields are affected.
Migration
Qualified ISO8601 dates with intended timezone offsets and
\DateTimeInterface objects can now be passed directly to the
DataHandler without requiring manual
timezone adjustments.
An example of a previous workaround that added timezone offsets for the
DataHandler:
Passing datetime data via DataHandler PHP API (before)
$myDate = new \DateTime('yesterday');
$this->dataHandler->start([
'tx_myextension_mytable' => [
'NEW-1' => [
'pid' => 2,
// A previous workaround added the localtime offset to supplied// dates, as it was subtracted by the DataHandler persistence// layer'mydatefield_1' => gmdate('c', $myDate->getTimestamp() + (int)date('Z')),
],
],
]);
Copied!
Previous timezone-shifting workarounds can be removed and replaced with more
intuitive formats.
Passing datetime data via DataHandler PHP API (after)
$myDate = new \DateTime('yesterday');
$this->dataHandler->start([
'tx_myextension_mytable' => [
'NEW-1' => [
'pid' => 2,
// Pass \DateTimeInterface object directly'mydatefield_1' => $myDate,
// Format as LOCALTIME'mydatefield_2' => $myDate->format('Y-m-d\TH:i:s'),
// Format with timezone information// (offsets will be normalized to the persistence timezone// format: UTC for integer fields, LOCALTIME for native// DATETIME fields)'mydatefield_3' => $myDate->format('c'),
],
],
]);
Copied!
Breaking: #105686 - Avoid obsolete $charset in sanitizeFileName()
Implementing classes no longer need to handle a second argument.
Impact
This change has little to no impact, since the main API caller - the Core
class
ResourceStorage - never passed a
second argument. The default implementation,
LocalDriver , has therefore always
behaved as if handling UTF-8 strings.
Affected installations
TYPO3 installations with custom File Abstraction Layer (FAL) drivers
implementing
DriverInterface may
be affected.
Migration
Implementing classes should drop support for the second argument. Retaining it
does not cause a conflict with the interface, but the TYPO3 Core will never
call
sanitizeFileName() with a second parameter.
The following methods have been removed from
\TYPO3\CMS\Core\Charset\CharsetConverter :
CharsetConverter->conv()
CharsetConverter->utf8_encode()
CharsetConverter->utf8_decode()
CharsetConverter->specCharsToASCII(), use
CharsetConverter->utf8_char_mapping() instead
CharsetConverter->sb_char_mapping()
CharsetConverter->euc_char_mapping()
This removes most helper methods that implemented conversions between different
character sets from the TYPO3 Core. The vast majority of websites now use UTF-8
and no longer require the expensive charset conversions previously provided by
the Core framework.
Impact
Calling any of the removed methods will trigger a fatal PHP error.
Affected installations
The TYPO3 Core has not exposed any of this low-level functionality in upper
layers such as TypoScript for quite some time. The removal should therefore
have little to no impact on most installations.
The only cases that may be affected are import or export extensions that
perform conversions between legacy character sets (for example, those in the
EUC family). Affected extensions can mitigate this change by copying the
TYPO3 v13 version of the class
CharsetConverter , including the relevant
files from core/Resources/Private/Charsets/csconvtbl/, into their own
codebase.
The extension scanner will detect usages and classify them as weak matches.
Migration
Avoid calling any of the removed methods. Extensions that still require this
functionality should copy the necessary logic into their own codebase or use a
third-party library.
This particular case has a direct substitution:
// Before
$charsetConverter->specCharsToASCII('utf-8', $myString);
// After
$charsetConverter->utf8_char_mapping($myString);
Copied!
Breaking: #105728 - Extbase backend modules not in page context rely on global TypoScript only
Configuration of Extbase-based backend modules can be done using frontend
TypoScript.
The standard prefix in TypoScript to do this is
module.tx_myextension. Extbase backend module controllers can
typically retrieve their configuration using a call like:
$configuration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, 'myextension');
TypoScript itself is always bound to a page: The frontend must have
either some rootline page with a sys_template record or a page that has a site
set, otherwise frontend rendering will terminate with an error message.
Extbase-based backend modules are sometimes bound to pages as well: They can
have a rendered page tree configured in their module configuration and then
receive the selected page UID within the request as GET parameter
id.
Other Extbase-based backend modules, however, are not inside a page scope and do
not render the page tree. Examples of such modules within the TYPO3 Core are the
backend modules delivered by the form and beuser extensions.
Such Extbase-based backend modules without a page tree had a hard time
calculating their relevant frontend TypoScript-based configuration: Since
TypoScript is bound to pages, they looked for "the first" valid page in the page
tree, and the first valid sys_template record to calculate their TypoScript
configuration. This dependency on guesswork made final configuration of Extbase
backend module configuration not in page context brittle, opaque, and clumsy.
TYPO3 v14 puts an end to this: Extbase backend modules without page context
compile their TypoScript configuration from global TypoScript only and no longer
calculating TypoScript by guessing "the first valid" page.
The key call to register such "global" TypoScript is the method
ExtensionManagementUtility::addTypoScriptSetup() in
ext_localconf.php files.
Impact
Configuration of Extbase-based backend modules may change if their configuration
is defined by the first valid page in the page tree. Configuration of such
backend modules can no longer be changed by including TypoScript on the "first
valid" page.
Affected installations
Instances with Extbase-based backend modules without a page tree may be affected.
Migration
Configuration of Extbase-based backend modules without a page tree must be
supplied programmatically and made "global" by extending
$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup'] using
ExtensionManagementUtility::addTypoScriptSetup() within extensions’
ext_localconf.php files. The backend module of the form extension is a
good example. Additional locations of extensions that deliver form YAML
definitions are defined like this:
Note it is also possible to use the method
ExtensionManagementUtility::addTypoScriptConstants() to declare "global"
TypoScript constants and to use them in the TypoScript shown above.
Breaking: #105733 - FileNameValidator no longer accepts custom regex in __construct()
The class
FileNameValidator no
longer accepts a custom file deny pattern in
__construct(). The service
is now stateless and can be injected without side effects.
Impact
A custom partial regex passed as the first constructor argument when
instantiating the service is now ignored. The service relies on
$GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] configuration and a
hard-coded constant as a fallback.
Affected installations
Instances with custom extensions using
GeneralUtility::makeInstance(FileNameValidator::class, 'some-custom-pattern');
are affected. This is expected to be a very rare case.
Migration
Extensions that need to be tested with custom patterns that cannot be declared
globally using
$GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern']
should implement their own service for this purpose or inline the necessary
code. The core implementation performing the check is only a few lines long.
The event
\TYPO3\CMS\Core\Mail\Event\AfterMailerInitializationEvent
has been removed. This event became obsolete with the introduction of the
Symfony-based mailer in TYPO3 v10. It was only able to influence the TYPO3 Core
mailer by calling the
@internal method
injectMailSettings()after
the settings had already been determined. The event has been removed since it
no longer had a meaningful use case.
Impact
Event listeners registered for this event will no longer be triggered.
Affected installations
This event has had little purpose since the switch to the Symfony-based mailer
and is probably not used in most instances. The extension scanner will find
usages.
Migration
Check if this event can be substituted by reconfiguring
$GLOBALS['TYPO3_CONF_VARS']['MAIL'] , or by listening for the event
BeforeMailerSentMessageEvent instead.
Breaking: #105855 - Remove file backwards compatibility for alt and link field
When the File Abstraction Layer (FAL) was introduced, the TYPO3 Core file fields
media for table pages, and image and assets for table
tt_content, had their so-called "overlay palettes" overridden to
imageOverlayPalette, so that additional fields like alternative, link, and
crop were displayed. However, this was done for all file types, including
text, application, and the fallback type unknown. For these types, the
additional fields served no meaningful purpose. For this reason, they have now
been removed.
Impact
The TYPO3 Core file fields media for table pages, image and
assets for table tt_content, will no longer display the fields
alternative and link for file types other than image.
Affected installations
This affects installations that use one of the Core fields for file types
other than image (for example text or application) and make use of the
fields alternative and/or link.
This should not affect many installations, as these fields are used primarily
for images.
Migration
These fields can be restored using TCA overrides if necessary. First,
register a new palette for the sys_file_reference table containing the desired
set of fields.
Then, use this palette for your specific Core field and file type. The following
example restores the fields alternative and link for the media field of
the pages table when the file type is text.
The TypoScript setting
exposeNonexistentUserInForgotPasswordDialog has been removed in
EXT:felogin.
Impact
Using the TypoScript setting
exposeNonexistentUserInForgotPasswordDialog has no effect anymore. The
password recovery process in EXT:felogin now always shows the same message
when a username or email address is submitted in the password recovery form.
Affected installations
Websites using the TypoScript setting
exposeNonexistentUserInForgotPasswordDialog in EXT:felogin are affected.
Migration
The setting has been removed without replacement. It is possible to use the
PSR-14 event
SendRecoveryEmailEvent to implement
similar functionality if absolutely necessary. From a security perspective,
however, it is strongly recommended not to expose the existence of email
addresses or usernames.
An exception handling detail within the File Abstraction Layer (FAL) resource
handling has been changed. When calling
getSubFolder('mySubFolderName') on a
Folder object, and if this subfolder does
not exist, the specific
\TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException is
now raised instead of the global
\InvalidArgumentException.
Impact
The change may affect extensions that directly or indirectly call
Folder->getSubFolder() and expect a
\InvalidArgumentException to
be thrown.
Affected installations
FolderDoesNotExistException does not extend
\InvalidArgumentException. Code that currently expects a
\InvalidArgumentException to be thrown needs to be adapted.
Migration
The change is breaking for code that takes an "optimistic" approach like:
"get the subfolder object, and if this throws, create one". Example:
Extensions that need to stay compatible with both TYPO3 v13 and v14 should catch
both exceptions and should later avoid catching
\InvalidArgumentException when v13 compatibility is dropped:
useTYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException;
try {
$mySubFolder = $myFolder->getSubFolder('mySubFolder');
} catch (\InvalidArgumentException|FolderDoesNotExistException) {
// @todo: Remove \InvalidArgumentException from catch list when// TYPO3 v13 compatibility is dropped.
$mySubFolder = $myFolder->createFolder('mySubFolder');
}
Extbase previously supported the TypoScript toggle
config.tx_extbase.persistence.updateReferenceIndex to control
whether the reference index should be updated when records are persisted.
It has become increasingly important that the reference index is always kept
up to date, since an increasing number of TYPO3 Core components rely on current
reference index data. Using the reference index at key points can improve read
and rendering performance significantly.
This toggle has been removed. Reference index updating is now always enabled.
Impact
The change may slightly increase database load, which can become noticeable
when Extbase updates many records at once.
Affected installations
Instances with extensions that write many records using the Extbase persistence
layer may be affected.
Migration
The TypoScript toggle
config.tx_extbase.persistence.updateReferenceIndex should be
removed from any extension codebase, as it is now ignored by Extbase.
Breaking: #106056 - Add setRequest and getRequest to Extbase ValidatorInterface
Custom validators implementing
\TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface must now
also implement the methods
setRequest() and
getRequest().
Impact
Missing implementations of the methods
setRequest() and
getRequest() will now result in a PHP fatal error.
Affected installations
TYPO3 installations with custom extensions implementing
ValidatorInterface .
Migration
The methods
setRequest() and
getRequest() must be implemented in
affected validators.
If there is no need to directly implement
ValidatorInterface , it is
recommended to extend
AbstractValidator , where
both methods are already implemented.
The public property
\TYPO3\CMS\Core\DataHandling\DataHandler->storeLogMessages has
been removed without substitution. It should no longer be used by extensions.
Impact
Setting or reading the property in an extension will now raise a PHP warning-
level error.
Affected installations
Instances with extensions that access this property are affected. This should
be a very rare use case. No TYPO3 Extension Repository (TER) extensions were
affected during verification. The extension scanner is configured to find
usages as a weak match.
Migration
The property has been removed. Any code setting or reading it from
\TYPO3\CMS\Core\DataHandling\DataHandler instances should be removed. The
DataHandler->log() method now always writes the given
$details
to the
sys_log table.
Breaking: #106307 - Use stronger cryptographic algorithm for HMAC
TYPO3 now uses SHA3-256 for HMAC operations across multiple components,
replacing the previously used MD5, SHA1, and SHA256 algorithms. SHA3-256
(Keccak) produces 64-character hexadecimal hashes compared to the 32 or
40 characters of the older algorithms.
The following components have been upgraded:
Component
Previous HMAC
New HMAC
cHash
MD5
SHA3-256
Backend password recovery
SHA1
SHA3-256
Frontend password recovery
SHA1
SHA3-256
File dump controller
SHA1
SHA3-256
Show image controller
SHA1
SHA3-256
Backend form protection
SHA1
SHA3-256
Extbase form request attributes
SHA1
SHA3-256
Form extension request attributes
SHA1
SHA3-256
Database session backend
SHA256
SHA3-256
Redis session backend
SHA256
SHA3-256
Database fields have been extended to accommodate the longer hash values
(and would even support SHA3-512 with 128 hexadecimal characters in the
future):
Backend user password reset links in progress become invalid
Frontend user password reset links in progress become invalid
Users must request new password reset emails
Session handling:
Existing session identifiers will be regenerated on next user login
No immediate session invalidation occurs
Database schema:
Field lengths are automatically updated during upgrade
No data migration is required for existing records
Affected installations
All installations upgrading to TYPO3 v14 are affected.
The impact varies based on usage:
High impact: installations with active password reset processes or
cached frontend URLs with cHash parameters
Medium impact: installations using file dump or show image
controllers with externally stored URLs
Low impact: all other installations (automatic migration on next
use)
Migration
Database schema updates:
Execute the database analyzer in the Install Tool or run
vendor/bin/typo3 upgrade:run.
URLs and caching:
Frontend caches should be cleared to regenerate cHash values
File dump and show image URLs regenerate automatically on next access
External references to file or image URLs must be updated
Important
Existing links with &cHash= URL parameters will become invalid and
respond with an HTTP 404 error. Search engines first need to crawl the
site and discover the new URLs that contain the longer cache hash
value. This likely has an impact on SEO.
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['fallbackToLegacyHash'] = true;
can be used to still allow the legacy MD5 cache hash during
frontend requests.
Sessions:
No manual intervention is required. Sessions are automatically rehashed on
next login.
Custom extensions:
If custom code uses
HashService::hmac() directly, review whether the
default SHA1 algorithm is still appropriate. Consider explicitly passing
HashAlgo::SHA3_256 for new HMAC operations:
To enable dependency injection for TypolinkBuilder classes, several breaking
changes were introduced to the TypolinkBuilder architecture.
The following breaking changes have been made:
The constructor of
AbstractTypolinkBuilder has been
removed. Extending classes can no longer rely on receiving
ContentObjectRenderer and
\TypoScriptFrontendController
through the constructor.
All concrete TypolinkBuilder implementations now implement the new
TypolinkBuilderInterface and use
dependency injection via their constructors instead of extending
AbstractTypolinkBuilder with
constructor arguments.
The method signature of the main link-building method has changed from
build(array &$linkDetails, string $linkText, string $target, array $conf)
to
buildLink(array $linkDetails, array $configuration, ServerRequestInterface $request, string $linkText = '').
Impact
Custom TypolinkBuilder implementations extending
AbstractTypolinkBuilder will fail
with fatal errors due to the removed constructor and changed method signatures.
Extensions that instantiate TypolinkBuilder classes directly will also fail,
as the constructor signatures have fundamentally changed to use dependency
injection.
Directly instantiate TypolinkBuilder classes in PHP code
Override or extend the
build() method of TypolinkBuilder classes
Migration
For custom TypolinkBuilder implementations:
Implement
TypolinkBuilderInterface
Use dependency injection in the constructor for required services
Replace the
build() method with
buildLink()
Note: Classes implementing
TypolinkBuilderInterface are
automatically configured as public services in the dependency injection
container - no manual configuration is required.
Example migration:
Before (TYPO3 v13 and lower)
useTYPO3\CMS\Frontend\Typolink\AbstractTypolinkBuilder;
classMyCustomLinkBuilderextendsAbstractTypolinkBuilder{
publicfunctionbuild(array &$linkDetails, string $linkText, string $target, array $conf): LinkResultInterface{
// Custom link building logicreturnnew LinkResult('news', $linkText);
}
}
For code that instantiates TypolinkBuilder classes directly:
It is strongly recommended to use the
LinkFactory instead of instantiating
TypolinkBuilder classes directly. The
LinkFactory handles proper
instantiation and dependency injection automatically.
Breaking: #106412 - TCA interface settings for list view removed
Each TCA definition previously had an optional section named
['interface'], which defined parameters for displaying TCA records.
The last remaining options within this section,
$GLOBALS['TCA'][$table]['interface']['maxSingleDBListItems'] and
$GLOBALS['TCA'][$table]['interface']['maxDBListItems'] , have been
removed. As a result, the entire
['interface'] section is no longer
supported and will be ignored.
These settings were used to define the number of table rows displayed within
the Content > List backend module.
Impact
The
$GLOBALS['TCA'][$table]['interface'] section in TCA definitions is
no longer evaluated.
Setting any values under this key in custom extensions has no effect and will
be automatically removed during build time.
Affected installations
TYPO3 installations with custom TCA settings defining
$GLOBALS['TCA'][$table]['interface'] are affected.
Migration
Visual display settings can still be overridden on a per-user or per-page
basis via TSconfig. This approach is more flexible, as it allows rendering
different numbers of items per site, page tree, or user group.
The TCA option
$GLOBALS['TCA'][$table]['interface']['maxSingleDBListItems'] has been
removed in favor of
mod.web_list.itemsLimitSingleTable.
The TCA option
$GLOBALS['TCA'][$table]['interface']['maxDBListItems'] has been removed
in favor of
mod.web_list.itemsLimitPerTable.
Breaking: #106427 - File Abstraction Layer related changes
In TYPO3 v14, the PHP API of the File Abstraction Layer (FAL) has undergone
major internal changes that may affect extension authors.
Most PHP classes and interfaces within FAL are now strongly typed using
native PHP type declarations.
Several methods have been moved from
AbstractFile to its concrete
implementation
File , since only this
class should provide such behavior. The moved methods are:
AbstractFile->rename()
AbstractFile->copyTo()
AbstractFile->moveTo()
The
FileInterface method
rename() has been removed, as this operation is only relevant to the
concrete
File implementation.
Classes implementing
FolderInterface
must now implement the following methods to stay compatible with the new
Folder class:
getSubFolder()
getReadablePath()
getFiles()
searchFiles()
Previously, this was not enforced, but it is now required to make
Folder more interchangeable across implementations.
Impact
Calling PHP classes or methods from the File Abstraction Layer directly may
result in fatal PHP errors if incorrect or missing argument types are used.
Affected installations
TYPO3 installations with third-party extensions that interact with the FAL PHP
API in non-documented ways.
Migration
Ensure that all FAL-related code passes and expects the correct PHP types when
using or extending the API.
If you provide custom implementations of
FolderInterface or classes extending
AbstractFile , make sure to implement the
newly required methods accordingly.
Breaking: #106503 - Removal of fields from sys_file_metadata
The following database fields and corresponding TCA definitions have been
removed from the database table
sys_file_metadata without substitution:
visible
fe_groups
These fields were added to the table when the system extension
EXT:filemetadata was installed.
Although the field names suggested access control functionality similar to
those found in other TYPO3 tables, they were never configured to restrict
frontend or backend access. Their intended meaning would have depended on
custom implementation and was not supported by the TYPO3 Core.
Additionally, the field
status has been moved from the Access tab to
the Metadata tab to avoid the impression that this field has any restrictive
behavior.
Impact
The fields
visible and
fe_groups in
sys_file_metadata are
no longer used. After performing a Database Compare in the Install Tool,
these columns will be removed and their data permanently lost.
Accessing these fields in PHP or TypoScript will result in PHP warnings.
Affected installations
Any TYPO3 installation using the fields
visible or
fe_groups
provided by the system extension EXT:filemetadata is affected.
Migration
No automatic migration is available.
If the fields are still required for custom logic, reintroduce their database
columns and TCA configuration within a custom extension.
In earlier TYPO3 versions, the Form Framework provided two template variants
for frontend rendering:
The initial, legacy templates in
EXT:form/Resources/Private/Frontend/Templates and
EXT:form/Resources/Private/Frontend/Partials, which were deprecated
in forge#95456
The newer, Bootstrap 5 compatible and accessible templates in
EXT:form/Resources/Private/FrontendVersion2/Templates and
EXT:form/Resources/Private/FrontendVersion2/Partials, introduced
with forge#94868
The legacy form templates have now been removed. The rendering option
templateVariant, which toggled the template and form configuration
variant, has been removed as well.
The newer template variants have been moved to the original file paths of the
legacy templates.
Impact
The removal of the legacy templates and the
templateVariant
configuration option simplifies the Form Framework rendering logic.
Developers no longer need to choose between multiple template variants,
reducing complexity and improving maintainability. Projects already using the
newer templates benefit from a cleaner configuration and a unified rendering
approach.
Affected installations
All TYPO3 installations using the Form Framework are affected.
Migration
If you still rely on the legacy templates, you must migrate your templates and
partials to the structure of the newer templates.
Websites that use
templateVariant: version2 can simplify their form
configuration. Variants with the condition
'getRootFormProperty("renderingOptions.templateVariant") == "version2"'
are no longer necessary and can be removed.
The TCA control option
$GLOBALS['TCA'][$table]['ctrl']['is_static'] has been removed, as it is
no longer evaluated by the TYPO3 Core.
Originally, this option was introduced to mark certain database tables (for
example, from
static_info_tables) as containing static, non-editable
reference data. Over time, the TYPO3 ecosystem has evolved, and the original
purpose of is_static has become obsolete.
Modern TYPO3 installations rarely rely on static data tables. Better mechanisms
now exist for managing read-only or reference data, such as the TCA options
readOnly and editlock, or backend access control. Removing this
legacy option improves maintainability and reduces complexity for newcomers.
Impact
The option is_static is no longer evaluated. It is automatically removed
at runtime by a TCA migration, and a deprecation log entry is generated to
indicate where adjustments are required.
Affected installations
All TYPO3 installations defining
$GLOBALS['TCA'][$table]['ctrl']['is_static'] in their TCA configuration
are affected.
Migration
Remove the is_static option from the ctrl section of your TCA
configuration.
Breaking: #106869 - Remove static function parameter in AuthenticationService
The method
\TYPO3\CMS\Core\Authentication\AuthenticationService::processLoginData()
no longer accepts the parameter
$passwordTransmissionStrategy.
Additionally, the method now declares a strict return type.
Impact
Authentication services extending or overriding
AuthenticationService
and its method
processLoginData() (or a subtype such as
processLoginDataBE() or
processLoginDataFE()) will no longer
receive the
$passwordTransmissionStrategy parameter.
Affected installations
TYPO3 installations with custom authentication services that extend
AuthenticationService
and implement or override
processLoginData() or one of its subtypes.
Migration
Extensions extending
AuthenticationService
must remove the
$passwordTransmissionStrategy parameter from their
method signature and add the strict return type
bool|int.
Extensions implementing subtype methods such as
processLoginDataBE() or
processLoginDataFE() must also remove the
parameter, as it is no longer passed to these methods.
The TSconfig option
mod.web_list.noViewWithDokTypes has been
removed, as it duplicated the existing configuration
TCEMAIN.preview.disableButtonForDokType.
Since forge#96861, the latter has been established as the single source of
truth for disabling the View button for specific
doktype values.
Impact
The option
mod.web_list.noViewWithDokTypes is no longer evaluated.
Only the configuration
TCEMAIN.preview.disableButtonForDokType
is now respected.
Affected installations
TYPO3 installations that still rely on
mod.web_list.noViewWithDokTypes in Page TSconfig to control the
visibility of the View button in backend modules are affected.
Migration
Remove any usage of
mod.web_list.noViewWithDokTypes from Page
TSconfig.
Use the existing configuration
TCEMAIN.preview.disableButtonForDokType instead:
With forge#105640, context awareness for the CKEditor Rich Text Editor (RTE)
was introduced, allowing the editor interface to automatically adapt to the
user’s system-wide light or dark mode preference.
This feature is now enabled by default. The TYPO3 Core stylesheet
EXT:rte_ckeditor/Resources/Public/Css/contents.css has been updated to
support light and dark mode variants automatically. Previously fixed white
backgrounds now adapt dynamically based on the editor’s preferred color scheme.
Note that this change affects only the backend editor interface. The display of
RTE content in the frontend remains unaffected.
Impact
The previously fixed light mode user interface of the CKEditor RTE is now
context-aware, displaying content in light or dark mode according to the
editor’s system preference.
Affected installations
TYPO3 installations that rely on a fixed light mode presentation of CKEditor
RTE instances in the backend are affected.
Migration
Installations with custom CKEditor modifications should review their
contents.css file.
If the TYPO3 Core default stylesheet was previously used, and a fixed light
mode appearance is desired, this can be enforced in the RTE YAML
configuration:
The TCA control option
$GLOBALS['TCA'][$table]['ctrl']['searchFields']
has been removed.
With the introduction of the Schema API and the
\TYPO3\CMS\Core\Schema\SearchableSchemaFieldsCollector component,
the handling of fields included in searches has changed. By default, all
fields of suitable types, such as input or text, are now automatically
considered searchable.
To manually define searchable fields, use the new
searchable field
configuration option within a field's TCA configuration.
See the full list of supported field types
here.
Unsupported field types (such as file, inline, etc.) are not considered
searchable and do not support the searchable option.
Note
This change does not affect the Page TSconfig option used by
EXT:linkvalidator:
mod.linkvalidator.searchFields.
Impact
The searchFields option is no longer evaluated. It is automatically
removed at runtime through a TCA migration, and a deprecation log entry is
generated to highlight where adjustments are required.
If suitable fields are detected that were
not listed in the removed
searchFields option, they are automatically
set to
searchable => false to preserve previous behavior.
Affected installations
All installations that define searchFields in their TCA configuration.
Migration
Remove the searchFields option from the ctrl section of your TCA
configuration.
If needed, use the searchable option in individual field definitions
to control which fields are included in the search functionality.
Breaking: #106976 - Removal of TCA search field configuration options
The following TCA field-level search configuration options have been removed:
search.case
search.pidonly
search.andWhere
These options were originally intended to customize backend record search
behavior but have proven to be of little practical value:
They were not used in the TYPO3 Core,
They were not used in common third-party extensions,
Their behavior was unclear and inconsistently supported,
They were insufficiently documented and hard to validate.
This removal is part of the ongoing effort to simplify and streamline TCA
configuration and reduce unnecessary complexity for integrators.
Impact
These options are no longer evaluated. They are automatically removed at
runtime through a TCA migration, and a deprecation log entry is generated to
highlight where adjustments are required.
Affected installations
Any installation or extension that defines one or more of these options in its
TCA field configuration:
One of the main features of TCA is the concept of record types. This allows
using a single table for different purposes and in different contexts. The
most well-known examples are the "Page Types" of the
pages table and the
"Content Types" of the
tt_content table. For every specific type, it is
possible to define which fields to display and to customize their
configuration.
A special case historically has been plugin registration, which for a long
time used the so-called subtypes feature of TCA. This was an additional layer
below record types, configured using subtype_value_field (commonly
list_type), and optionally subtypes_addlist and
subtypes_excludelist to add or remove fields depending on the selected
subtype.
FlexForms attached to such subtypes were configured using
ds_pointerField (typically pointing to list_type,CType). This came in
combination with the corresponding ds configuration, which was an array
with keys combining the pointer fields, for example:
Over recent TYPO3 versions, this approach has been deprecated in favor of
using record types exclusively for plugin registration via the CType field,
making configuration cleaner and easier to understand.
With this change, support for ds_pointerField and the multi-entry
ds array format has now been removed. The ds option now points to
a single FlexForm, either directly or via a FILE: reference.
FlexForms must instead be assigned via standard types configuration
using columnsOverrides.
This also affects the data structure identifier, which in the commonly used
tca type is the dataStructureKey. It is now set to default if the
table does not support record types or no record type-specific configuration
exists. Otherwise, the dataStructureKey is set to the corresponding
record type value, for example textpic.
A fallback for TYPO3 v14 resolves comma-separated dataStructureKey
values (for example, list_type,CType) to CType.
To address circular dependencies during schema building,
FlexFormTools now supports
both TCA Schema objects and raw TCA configuration arrays as input. The
following methods accept a union type
array|TcaSchema for the new
$schema parameter:
getDataStructureIdentifier()
parseDataStructureByIdentifier()
cleanFlexFormXML()
Previously, these methods relied on
$GLOBALS['TCA'] internally, which
caused architectural issues. They now operate directly on the provided schema.
All calls to these methods should provide the
$schema parameter with
either a
TcaSchema instance or a raw TCA
configuration array. Since data structure resolution can be customized by
extensions, the parameter is not strictly mandatory, but it is strongly
recommended to provide it in most cases. An
InvalidTcaSchemaException
will be thrown if schema resolution is required but no schema is passed.
This change also enables components like
RelationMapBuilder to use
FlexFormTools during schema
building, even when only raw TCA is available.
The following class has been removed as it is no longer required:
\InvalidCombinedPointerFieldException
Impact
FlexForm Pointer Field Removal
Any TCA definition that still uses
ds_pointerField or a ds
array with multiple entries (for example news_pi1,list) will no longer work
and might cause rendering errors.
FlexFormTools Schema Parameter
All code calling
FlexFormTools
methods (
getDataStructureIdentifier(),
parseDataStructureByIdentifier(),
cleanFlexFormXML()) must be
updated to provide the required
$schema parameter.
Affected installations
FlexForm Pointer Field Removal
All installations using
ds_pointerField (as the pointer field
functionality has been removed entirely) or an array-like structure for
ds in their TCA field type flex configuration.
FlexFormTools Schema Parameter
All installations with custom code that directly call
FlexFormTools methods
without providing the
$schema parameter.
This includes custom extensions or TYPO3 Core patches using these methods.
A TCA migration automatically converts single-entry ds arrays.
Multi-entry definitions require manual migration, as they must be aligned with
the correct record type configuration, which may require additional
configuration changes beforehand.
Example for single-entry migration:
Before:
Migration of single-entry ds configuration (before)
'ds' => [
'default' => '<T3DataStructure>...',
],
Copied!
After:
Migration of single-entry ds configuration (after)
Extbase no longer supports PHP annotations for models, data transfer objects
(DTOs), and controller actions. Use PHP attributes (for example
#[Extbase\Validate]) instead. Attributes provide the same functionality
with better performance and native language support.
PHPDoc annotations such as
@var,
@return, and similar are
unaffected by this change, as they are handled by standard PHPDoc parsing,
not by Doctrine.
Explicit type annotations like
@var ObjectStorage<FileReference>
remain fully supported.
Extbase previously relied on annotation parsing, typically detected when the
annotation namespace was imported, for example:
Since TYPO3 now requires PHP 8.2 as the minimum version, the use of native PHP
attributes is preferred. All Extbase-related annotations have been available
as PHP attributes since TYPO3 v12.
Impact
By dropping support for Extbase annotations, only PHP attributes are now
supported. This provides more type safety and better integration with PHP’s
language features.
Developers benefit from a cleaner, faster, and more reliable implementation
without the need for the deprecated third-party annotation parser.
Affected installations
All Extbase models, Data Transfer Objects (DTOs), and controllers that use
Extbase annotations are affected.
This includes built-in annotations such as
Cascade,
Lazy, and
Validate, as well as any custom annotation implementations.
Migration
Switch from Extbase annotations to native PHP attributes.
Attribute class namespaces have moved from
\TYPO3\CMS\Extbase\Annotation to
\TYPO3\CMS\Extbase\Attribute.
A class alias map remains available for backward compatibility, but since
the old namespaces are deprecated, developers should migrate to the new
Attribute namespace to prevent issues in the future.
Additionally, backward compatibility for the abstract base class
constructor has been removed:
\TYPO3\CMS\Core\Cache\Backend\AbstractBackend
Impact
If you are using or extending the Cache Backend or Cache Frontend, you must
ensure that you are using the correct PHP types. This includes method
parameters, return types, and property types in your classes.
Affected installations
All installations that extend or implement cache backend or frontend
classes are affected. This includes custom cache implementations in
third-party extensions.
Extension authors who have created custom cache backends or frontends
will need to update their class method signatures to match the new type
declarations from the interfaces.
Migration
Update your custom cache backend and frontend implementations to use the
correct PHP types:
Ensure all method signatures match the interface declarations exactly
Add proper type hints for parameters (e.g.,
string
$entryIdentifier)
Add proper return type declarations (e.g.,
bool,
mixed,
void)
Update any extending classes to use the same type declarations
Note for extensions that strive for TYPO3 v13 and v14 compatibility: The
__construct() change of
\TYPO3\CMS\Core\Cache\Backend\AbstractBackend can be mitigated by
omitting a type for the first argument and checking whether its incoming
value is a string (v13) or an array (v14).
For interface changes that added types to method argument signatures,
implementing services may omit the type to keep backwards compatibility
with TYPO3 v13. For added return value types, they must be added for v14
compatibility, and v13 should accept them. All in all, it should be
possible to have a single implementing class for both v13 and v14, but
it may be somewhat tricky. From a code perspective, it may be easier to
have dedicated classes.
The workspace record flag Freeze Editing has been removed without
replacement, effectively removing this feature.
The functionality had only been partially implemented and exhibited several
usability and conceptual issues. It was therefore decided to remove it entirely
in favor of more reliable and flexible workspace configuration options.
Impact
"Freezing" a workspace to prevent editing of records is no longer possible.
Affected installations
Since Freeze Editing did not provide any visible feedback to editors, it was
likely used only rarely.
During the database compare, the field freeze will be removed from the
table
sys_workspace, effectively "unfreezing" all previously frozen
workspaces.
Migration
A more robust alternative to Freeze Editing is to configure a custom
workspace stage and assign responsible persons.
In combination with the following workspace options:
Publish only content in publish stage
Restrict publishing to workspace owners
you can create a workflow in which only designated members or groups are
permitted to move records into the Ready to publish stage and/or perform
publishing actions.
While other members can still edit records, such edits will automatically reset
the workspace stage back to Editing, restarting the review cycle for those
changes.
The handling of PSR-7 response headers in TYPO3 Core and Extbase has been
unified. Previously, different mechanisms caused inconsistent behavior:
Extbase only kept the last value of a header, discarding all
previous values (e.g. only one Set-Cookie header was possible).
Core allowed multiple Set-Cookie headers, but merged all other
headers with multiple values into a single comma-separated string.
According to RFC 9110, this is only valid for headers that explicitly
support comma-separated lists.
With this change, TYPO3 now preserves multiple header values by default.
Each value is emitted as a separate header line, while single values remain
a single-line header.
Impact
Multiple header values are now always emitted as multiple header lines.
Extbase and Core responses can now properly emit multiple headers with the
same name (e.g. Set-Cookie, WWW-Authenticate, Link,
xkey).
Extensions that relied on the old merging or overwriting behavior may need
to be adapted.
Affected installations
Installations are affected if they:
Relied on headers being merged into a comma-separated string.
Relied on only the last header value being retained in Extbase responses.
Migration
If your use case requires merged header values, you must now implement this
explicitly:
Note: There is another edge case not affected by this change. Multiple Extbase
plugins still cannot set multiple header values with the same name (for
example, two Extbase plugins both setting a Set-Cookie header). In this
case, the latter will override the former. Installations affected by this
scenario should resolve it by adding their own middleware.
The hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormCreate']
has been removed in favor of the PSR-14 event
BeforeFormIsCreatedEvent , which provides a
more powerful and flexible way to influence form creation.
Impact
Any hook implementation registered under
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormCreate']
is no longer executed in TYPO3 v14.0 and later.
Affected installations
TYPO3 installations with custom extensions using this hook are affected.
The Extension Scanner will report such usages as a weak match.
Migration
The hook has been removed without prior deprecation.
This allows extensions to remain compatible with both TYPO3 v13 (using the
hook) and v14+ (using the new event) simultaneously.
The Content > List backend module has been refactored to use the
\RecordInterface and related Record API
internally instead of working with raw database row arrays.
This modernization introduces stricter typing and improves data consistency.
As a result, several public method signatures have been updated.
The following public methods in
DatabaseRecordList have changed
their signatures:
renderListRow() now expects a
RecordInterface object instead
of an array as the second parameter.
makeControl() now expects a
RecordInterface object instead of
an array as the second parameter.
makeCheckbox() now expects a
RecordInterface object instead of
an array as the second parameter.
languageFlag() now expects a
RecordInterface object instead of
an array as the second parameter.
makeLocalizationPanel() now expects a
RecordInterface object
instead of an array as the second parameter.
linkWrapItems() now expects a
RecordInterface object instead
of an array as the fourth parameter.
getPreviewUriBuilder() now expects a
RecordInterface object
instead of an array as the second parameter.
isRecordDeletePlaceholder() now expects a
RecordInterface
object instead of an array.
isRowListingConditionFulfilled() has dropped the first parameter
$table and now expects a
RecordInterface object instead of an
array.
These changes enable the List module to operate on structured Record objects,
providing better type safety, consistency, and a foundation for further
modernization of the backend record handling.
Impact
Code that calls these methods directly must be updated to pass
\RecordInterface objects instead of
database row arrays.
Affected installations
TYPO3 installations with custom extensions that:
Extend or XCLASS
DatabaseRecordList and override
any of the affected methods.
Call the affected methods directly with array-based record data.
Migration
When calling affected methods, use the Record API to create a Record object
from a database row:
Before:
Migrating from array-based record handling to the Record API (before)
The hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormDuplicate']
has been removed in favor of the more powerful PSR-14 event
BeforeFormIsDuplicatedEvent .
Impact
Implementations of the removed hook are no longer executed in TYPO3 v14.0
and later.
Affected installations
TYPO3 installations with custom extensions using this hook. The extension
scanner reports any usage as a weak match.
Migration
The hook has been removed without deprecation to allow extensions to remain
compatible with both TYPO3 v13 (using the hook) and TYPO3 v14+ (using the new
event). When implementing the event as well, no further deprecations will
occur.
Use the PSR-14 Event to achieve the same
or greater functionality.
The hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormDelete']
has been removed in favor of the more powerful PSR-14 event
\TYPO3\CMS\Form\Event\BeforeFormIsDeletedEvent .
Impact
Implementations of the removed hook are no longer executed in TYPO3 v14.0
and later.
Affected installations
TYPO3 installations with custom extensions using this hook. The extension
scanner reports any usage as a weak match.
Migration
The hook has been removed without deprecation to allow extensions to remain
compatible with both TYPO3 v13 (using the hook) and TYPO3 v14+ (using the new
event). When implementing the event as well, no further deprecations will
occur.
Use the PSR-14 Event to achieve the same
or greater functionality.
The hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormSave']
has been removed in favor of the more powerful PSR-14 event
\TYPO3\CMS\Form\Event\BeforeFormIsSavedEvent .
Impact
Implementations of the removed hook are no longer executed in TYPO3 v14.0
and later.
Affected installations
TYPO3 installations with custom extensions using this hook. The extension
scanner reports any usage as a weak match.
Migration
The hook has been removed without deprecation to allow extensions to remain
compatible with both TYPO3 v13 (using the hook) and TYPO3 v14+ (using the new
event). When implementing the event as well, no further deprecations will
occur.
Use the PSR-14 Event to achieve the same
or greater functionality.
Breaking: #107397 - Circular dependency between ProcessedFile and Task removed
The circular dependency between
\TYPO3\CMS\Core\Resource\ProcessedFile and File Processing Task
classes has been resolved to improve the architecture and maintainability of
the File Abstraction Layer (FAL) processing system.
The following changes have been made to
ProcessedFile :
The public method
getTask() has been removed.
The public method
generateProcessedFileNameWithoutExtension() has been
removed.
The following changes have been made to
ProcessedFileRepository :
Method
add() now requires a
TaskInterface parameter.
Method
update() now requires a
TaskInterface parameter.
Additionally, the checksum validation logic has been moved from
ProcessedFile to
AbstractTask .
Impact
Any code that calls the following methods will cause PHP fatal errors:
Any code that calls
ProcessedFileRepository->add() or
ProcessedFileRepository->update() without the new
TaskInterface parameter will cause PHP fatal errors.
Code that relied on TYPO3CMSCoreResourceProcessedFile objects
having Task objects available internally will no longer work, as Task objects
are now created externally by
FileProcessingService when
needed.
Affected installations
Installations with custom file processing extensions or custom Task
implementations that directly interact with the
ProcessedFile->getTask() method are affected. The extension scanner will
report any usage of
ProcessedFile->getTask() and
ProcessedFile->generateProcessedFileNameWithoutExtension() as weak
matches.
Extensions that manually call
ProcessedFileRepository->add() or
ProcessedFileRepository->update() are also affected. The extension
scanner will not report usages of these methods due to too many weak matches.
Migration
Replace calls to
ProcessedFile->getTask() with direct creation of Task
objects through the
TaskTypeRegistry :
The helper classes for preview and CropScaleMask (CSM) image generation
prevented further unification of the File Abstraction Layer (FAL) image
processing API.
The two helper classes
\TYPO3\CMS\Core\Resource\Processing\LocalPreviewHelper and
\TYPO3\CMS\Core\Resource\Processing\LocalCropScaleMaskHelper
have been removed. Their functionality has been merged into
\TYPO3\CMS\Core\Resource\Processing\LocalImageProcessor .
These helper classes existed for historical reasons and were never intended
to be part of the public API.
Impact
Any code that extends or references
\LocalPreviewHelper or
\LocalCropScaleMaskHelper
will now trigger PHP fatal errors.
Affected installations
Installations with custom extensions that extend or reference either
\LocalPreviewHelper or
\LocalCropScaleMaskHelper
are affected and will cause PHP fatal errors.
These helper classes were meant to be internal but were never declared as
such. Implementations using them directly instead of the
LocalImageProcessor should
be very rare.
Migration
Remove all references to
\LocalPreviewHelper or
\LocalCropScaleMaskHelper
from your code.
Use the
LocalImageProcessor
directly instead, or implement a custom image processor that executes before
this processor to apply additional functionality.
Breaking: #107436 - Localization system architecture changes
The TYPO3 localization system has been migrated to use the Symfony Translation
components internally (see Feature: #107436 - Symfony Translation Component integration), which introduces
several breaking changes to the internal API and configuration handling.
This change affects only the internal processing of translation ("locallang")
files. The public API of the localization system remains unchanged.
Method Signature Changes
The method
\TYPO3\CMS\Core\Localization\LocalizationFactory::getParsedData()
has a modified signature and behavior:
It now only returns the parsed and combined localization data instead of both
the default and the localized data. To obtain the default (English) data, call
getParsedData($fileReference, 'en').
Language Key Changes
Internally, the fallback language key has been changed from default to en.
This does not affect public API usage, where default can still represent any
configured language key other than English.
Configuration Changes
Custom parser configuration is no longer supported.
The global configuration option
$GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['parser'] has been removed,
and any custom parser registered through it will be ignored.
Several configuration options have been moved or renamed:
Code calling
\TYPO3\CMS\Core\Localization\LocalizationFactory::getParsedData()
with the old signature or expecting a multi-dimensional array structure will
break:
The method now enforces strict parameter types.
The method returns en instead of default for English translations.
Unused parameters have been removed.
Extensions that register custom localization parsers using
$GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['parser'] will no longer have
their parsers executed, potentially leading to missing translations.
Instances using the moved configuration options must update their configuration
to the new paths. The old options are no longer recognized and will be ignored.
Since TYPO3 v13, the basic
lib.parseFunc and
lib.parseFunc_RTE configurations are always available through
EXT:frontend/ext_localconf.php.
The default parseFunc configuration previously provided by
EXT:fluid_styled_content has been removed to avoid duplicated and outdated
settings. This unifies the parseFunc behavior between EXT:frontend and
EXT:fluid_styled_content.
The
allowTags syntax is no longer set by default, as HTML
sanitization has been handled properly by the HTML Sanitizer for some time.
All HTML tags are now allowed by default in frontend output, while the
HTML Sanitizer controls which tags and attributes are ultimately permitted.
Note that the
allowTags directive itself has not been
removed. It can still be set to restrict frontend output where desired.
The conceptual approach is:
The backend (RTE and its HTML parser/processing) already cleans unwanted
content and controls what is stored in the database.
The frontend output (parseFunc) should only add additional restrictions
through
allowTags when content comes from external or
untrusted sources (for example, custom Extbase output).
Impact
Custom TypoScript configurations using
allowTags syntax may no
longer work as expected. Specifically:
allowTags := addToList(wbr) no longer appends wbr.
Instead, it limits allowed tags to only wbr.
Default CSS classes on HTML elements (for example
<table class="contenttable">) are no longer automatically added by
the parseFunc configuration.
The parseFunc configuration in EXT:fluid_styled_content no longer
provides custom link-handling options such as extTarget and keep.
Affected installations
TYPO3 installations that use:
Custom TypoScript configurations relying on prior default
allowTags behavior.
Extensions or sites depending on the specific parseFunc configuration
previously provided by EXT:fluid_styled_content.
Configurations expecting automatic CSS class additions to HTML elements.
Sites relying on the old external link-handling behavior from the
Fluid Styled Content parseFunc.
Migration
If you need to allow specific HTML tags, explicitly configure
allowTags instead of extending a former default.
Before (no longer works):
lib.parseFunc_RTE.allowTags := addToList(wbr)
Copied!
After:
lib.parseFunc_RTE.allowTags = b,span,i,em,wbr,...
Copied!
For custom CSS classes on HTML elements, use custom CSS or add them through
Fluid templates or TypoScript processing instead.
If you require the previous link-handling behavior, configure it explicitly:
The TYPO3 Modal component has been migrated from Bootstrap's modal
implementation to use the native HTML
<dialog> element. This improves
accessibility, reduces bundle size, and removes the dependency on Bootstrap's
JavaScript for modal functionality.
As part of this migration, Bootstrap's native modal events (such as
show.bs.modal,
shown.bs.modal,
hide.bs.modal, and
hidden.bs.modal) are no longer dispatched.
Likewise, direct Bootstrap modal API usage (for example
new Modal() from
Bootstrap or
$(element).modal()) is no longer supported.
Impact
Bootstrap modal events (
*.bs.modal) are no longer available.
Extensions listening to these events must migrate to TYPO3's custom modal
events.
The Modal component now uses the native
<dialog> element with updated
CSS classes and DOM structure.
Any direct manipulation of Bootstrap modal APIs will no longer work.
Extensions using
data-bs-toggle="modal",
data-bs-content="...", or
data-bs-target attributes to trigger modals must migrate to TYPO3's
Modal API.
Affected installations
All installations with custom extensions that:
Listen to Bootstrap modal events (
show.bs.modal,
shown.bs.modal,
hide.bs.modal,
hidden.bs.modal)
Use Bootstrap's modal JavaScript API directly
(for example
new bootstrap.Modal())
Use jQuery to control modals (for example
$(element).modal('show'))
Use
data-bs-toggle="modal" or
data-bs-content="..." attributes
Manipulate modal DOM structures or classes expecting Bootstrap markup
Migration
Event migration
Replace Bootstrap modal event listeners with TYPO3's custom modal events.
Event listeners must be attached to the modal instance returned by the Modal
API, not queried from the DOM.
Before:
const modalElement = document.querySelector('.modal');
modalElement.addEventListener('show.bs.modal', (event) => {
console.log('Modal is about to be shown');
});
modalElement.addEventListener('shown.bs.modal', (event) => {
console.log('Modal is now visible');
});
modalElement.addEventListener('hide.bs.modal', (event) => {
console.log('Modal is about to be hidden');
});
modalElement.addEventListener('hidden.bs.modal', (event) => {
console.log('Modal is now hidden');
});
Copied!
After:
import Modal from'@typo3/backend/modal';
import Severity from'@typo3/backend/severity';
const modal = Modal.show(
'My Modal Title',
'This is the modal content',
Severity.info
);
modal.addEventListener('typo3-modal-show', (event) => {
console.log('Modal is about to be shown');
});
modal.addEventListener('typo3-modal-shown', (event) => {
console.log('Modal is now visible');
});
modal.addEventListener('typo3-modal-hide', (event) => {
console.log('Modal is about to be hidden');
});
modal.addEventListener('typo3-modal-hidden', (event) => {
console.log('Modal is now hidden');
});
Copied!
Bootstrap API migration
Replace Bootstrap modal API calls with TYPO3's Modal API.
Do not instantiate Bootstrap modals or manipulate modal DOM elements directly.
import Modal from'@typo3/backend/modal';
import Severity from'@typo3/backend/severity';
// Show a simple modal
Modal.show(
'My Modal Title',
'This is the modal content',
Severity.info,
[
{
text: 'Close',
btnClass: 'btn-default',
trigger: (event, modal) => modal.hideModal()
}
]
);
// Dismiss the current modal
Modal.dismiss();
Copied!
Data attribute migration
Replace Bootstrap's
data-bs-toggle and
data-bs-target attributes
with TYPO3's modal trigger API.
Before:
<buttontype="button"data-bs-toggle="modal"data-bs-target="#myModal"data-bs-content="Are you sure?">
Open Modal
</button>
Copied!
After:
<buttontype="button"class="t3js-modal-trigger"data-title="Confirmation"data-content="Are you sure?"data-severity="warning"data-button-close-text="Cancel"data-button-ok-text="Confirm">
Open Modal
</button>
The TypoScript condition function
getTSFE() has been removed.
After various properties like
getTSFE().type were already removed
in TYPO3 v13, the remaining parts of this functionality have now been removed
in TYPO3 v14.
The most common remaining use was accessing the current page ID via
getTSFE().id, which can be replaced with
request.getPageArguments().getPageId().
Impact
Conditions using
getTSFE() will no longer evaluate to true and
must be updated.
Affected installations
Instances with TypoScript conditions that use the function
getTSFE() are affected.
Migration
Replace
getTSFE() with an equivalent condition. For example:
The following method in
\TYPO3\CMS\Core\Core\Environment has been
removed in TYPO3 v14.0:
Environment::getComposerRootPath()
Since composer installers v4/v5 (which are required since TYPO3 v12),
getComposerRootPath() and
getProjectPath() return the same
value, because the project path can no longer be changed through
configuration.
Therefore, the method
Environment::getComposerRootPath() has been
removed. It was marked as internal from the beginning.
Impact
Calling this method will result in a PHP error.
Affected installations
TYPO3 installations with custom extensions or custom code that directly
call the removed method are affected:
Environment::getComposerRootPath()
The extension scanner will report any usage as a strong match.
Migration
Instead of calculating relative paths manually, use absolute paths or
the appropriate TYPO3 APIs for path handling.
Use the following replacement:
Environment::getProjectPath()
Breaking: #107488 - Scheduler frequency options moved to TCA
The global configuration array
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['frequencyOptions'] ,
which was used to define frequency options for scheduler tasks, has been
removed.
Frequency options are now configured directly in the TCA using the
overrideFieldTca mechanism on the
tx_scheduler_task.execution_details field.
This change improves consistency with TYPO3’s configuration patterns and
provides better extensibility for scheduler task timing options.
Impact
Extensions that previously added custom frequency options through the global
frequencyOptions array will no longer see their custom options in the
scheduler task frequency field.
Code that relied on reading the global
frequencyOptions configuration
will no longer work as expected.
Affected installations
All installations with extensions providing custom scheduler frequency options
through the global configuration array are affected.
Migration
Extensions should migrate their frequency options from the global configuration
to TCA overrides.
The method
\TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher->getTypoScriptFrontendController()
has been removed.
Since the entire
TypoScriptFrontendController class is being phased out,
this abstract helper method has been removed as part of that cleanup.
Impact
Calling this method in a custom EXT:form finisher will result in a fatal PHP
error.
Affected installations
TYPO3 instances using EXT:form with custom finishers that call this method are
affected. The extension scanner is configured to detect such usages.
Migration
Migration depends on what the finisher previously did with the returned class
instance. The
TypoScriptFrontendController properties and helper methods
have been modernized, with most data now available as request attributes.
For example, accessing the
cObj property can be replaced like this:
The hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['initializeFormElement']
has been removed in favor of the PSR-14 event
\TYPO3\CMS\Form\Event\BeforeRenderableIsAddedToFormEvent .
Impact
Hook implementations registered under
initializeFormElement are no longer executed in TYPO3 v14.0 and later.
Affected installations
TYPO3 installations with custom extensions using this hook are affected.
The extension scanner reports any usage as a weak match.
Migration
The hook was removed without a deprecation phase to allow extensions to work
with both TYPO3 v13 (using the hook) and TYPO3 v14+ (using the new event)
simultaneously.
Use the PSR-14 event instead to influence
form element initialization. Since the event is dispatched at a later point,
it allows more extensive modifications than the previous hook.
The hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRemoveFromParentRenderable']
has been removed in favor of the PSR-14 event
\TYPO3\CMS\Form\Event\BeforeRenderableIsRemovedFromFormEvent .
Impact
Hook implementations registered under
beforeRemoveFromParentRenderable are no longer executed in
TYPO3 v14.0 and later.
Affected installations
TYPO3 installations with custom extensions using this hook are affected.
The extension scanner reports any usage as a weak match.
Migration
The hook was removed without a deprecation phase to allow extensions to
work with both TYPO3 v13 (using the hook) and TYPO3 v14+ (using the new
event) simultaneously.
Use the PSR-14 event instead to allow
greater influence over the form processing workflow. Since the event is
dispatched at a later point, it allows more extensive modifications than
the previous hook.
Breaking: #107537 - Changes in URL generation of system resources
The following changes are considered breaking, although their impact is
expected to be very low.
TypoScript getData function
path previously returned a
relative URL and now returns an absolute URL (prepended with
absRefPrefix).
Access to FAL storages via relative path (fileadmin/templates/main.css)
is limited to the default storage defined in
$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] .
All generated system resource URLs now include cache busting.
Adding custom query strings to resource identifiers no longer disables cache
busting — both are now applied.
page.20 = TEXT
page.20 {
data = path : EXT:core/Resources/Public/Icons/Extension.svg
}
Copied!
Result (TYPO3 classic mode – note the leading "/")
"path" result before: typo3/sysext/core/Resources/Public/Icons/Extension.svg
"path" result now: /typo3/sysext/core/Resources/Public/Icons/Extension.svg
Copied!
Result (TYPO3 Composer mode – note the leading "/")
"path" result before: _assets/5f237792cbcdc97cfceade1e16ea33d7/Icons/Extension.svg
"path" result now: /_assets/5f237792cbcdc97cfceade1e16ea33d7/Icons/Extension.svg
Copied!
Relative path to FAL Storage
Referencing resources via relative path does only work for the default FAL storage
defined in
$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] .
This means if you have other local FAL storages configured, you need to use the FAL resource syntax
(e.g. FAL:42:/path/to/file.css) to reference files in such storage instead of using relative paths
(e.g. my-other-fileadmin/path/to/file.css).
It is generally recommended to use explicit resource identifiers (App resources or FAL resources),
instead of relative paths.
All generated URLs now contain cache busting
All generated resource URLs now include cache busting.
For example, icon URLs that previously had no cache busting will now contain a
cache-busting query string.
Additional query strings applied to the resource identifier
When adding custom query strings to resource identifiers, TYPO3 previously
disabled cache busting.
Now, both the custom query string and the cache-busting parameter are applied.
If custom query strings were used as manual cache busters, you can now remove
them safely.
page.20 = TEXT
page.20 {
data = asset : EXT:core/Resources/Public/Icons/Extension.svg?v=1234
}
Copied!
Result
result before: /typo3/sysext/core/Resources/Public/Icons/Extension.svg?v=1234
result now: /typo3/sysext/core/Resources/Public/Icons/Extension.svg?v=1234&1709051481
Copied!
Impact
getText "path" in TypoScript
All usages of
path in TypoScript
data now resolve
to absolute URLs instead of relative ones.
Relative path to FAL Storage
In installations referencing resources in additional local FAL storages using
a relative path syntax, an exception is thrown.
All generated URLs now contain cache busting
Generated URLs differ slightly from previous TYPO3 versions, especially when
cache busting was not applied before.
Additional query strings applied to the resource identifier
URLs now include both the original query string and the cache-busting
parameter, resulting in different output compared to earlier TYPO3 versions.
Affected installations
getText "path" in TypoScript
All installations using
path in TypoScript
data.
Relative path to FAL Storage
All installations referencing resources in additional local FAL storages using
a relative path syntax (e.g. my-other-fileadmin/path/to/file.css).
All generated URLs now contain cache busting
All installations having third party code, that misuses generated URLs
to assume file system paths from them.
Additional query strings applied to the resource identifier
All installations using resource identifiers with custom query strings, for
example:
EXT:foo/Resources/Public/rte.css?v=123
Migration
Relative path to FAL Storage
Either convert the relative path to a FAL resource like so:
FAL:1:/path/to/file.css
Alternatively it is possible to convert it to a resource URI:
URI:/my-other-fileadmin/path/to/file.css
The first will add cache busting, the latter will use the URI as is.
The hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterInitializeCurrentPage']
has been removed in favor of the PSR-14 event
\TYPO3\CMS\Form\Event\AfterCurrentPageIsResolvedEvent .
Impact
Hook implementations registered under afterInitializeCurrentPage are
no longer executed in TYPO3 v14.0 and later.
Affected installations
TYPO3 installations with custom extensions using this hook are affected.
The extension scanner reports any usage as a weak match.
Migration
The hook was removed without a deprecation phase to allow extensions to work
with both TYPO3 v13 (using the hook) and TYPO3 v14+ (using the new event)
simultaneously.
Use the PSR-14 event instead to allow
greater influence over the form rendering process. Since the event is
dispatched at a later point, it allows more extensive modifications than the
previous hook.
The hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterSubmit']
has been removed in favor of the PSR-14 event
\TYPO3\CMS\Form\Event\BeforeRenderableIsValidatedEvent .
Impact
Hook implementations registered under
afterSubmit are no longer executed
in TYPO3 v14.0 and later.
Affected installations
TYPO3 installations with custom extensions using this hook are affected.
The extension scanner reports any usage as a weak match.
Migration
The hook was removed without a deprecation phase to allow extensions to work
with both TYPO3 v13 (using the hook) and TYPO3 v14+ (using the new event)
simultaneously.
Use the PSR-14 event instead to allow
greater influence over the form submission process. Since the event is
dispatched at a later point, it allows more extensive modifications than the
previous hook.
The hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRendering']
has been removed in favor of the PSR-14 event
\TYPO3\CMS\Form\Event\BeforeRenderableIsRenderedEvent .
Impact
Hook implementations registered under
beforeRendering are no longer
executed in TYPO3 v14.0 and later.
Affected installations
TYPO3 installations with custom extensions using this hook are affected.
The extension scanner reports any usage as a weak match.
Migration
The hook was removed without a deprecation phase to allow extensions to work
with both TYPO3 v13 (using the hook) and TYPO3 v14+ (using the new event)
simultaneously.
Use the PSR-14 event instead to allow
greater influence over the rendering process. Since the event is dispatched
at a later point, it allows more extensive modifications than the previous
hook.
The frontend rendering event
\TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent
has been adjusted due to the removal of the
\TypoScriptFrontendController class.
The method
getController() has been removed and replaced by two new
methods:
getContent() and
setContent().
Impact
Event listeners that call
getController() will trigger a fatal PHP
error and must be adapted.
Affected installations
This event is commonly used in frontend rendering–related extensions, since
it provides an opportunity to access and manipulate the fully rendered
response body content at a late point in the rendering chain.
Instances with extensions listening to
AfterCacheableContentIsGeneratedEvent may be
affected. The extension scanner will detect such usages.
Migration
In most cases,
AfterCacheableContentIsGeneratedEvent->getController() was
used within event listeners to get and modify the
TypoScriptFrontendController->content property at the end of the
rendering process.
The event now provides
getContent() and
setContent() methods
to achieve the same goal more directly.
The
typo3/cms-adminpanel
system extension provides the interface
\TYPO3\CMS\Adminpanel\ModuleApi\DataProviderInterface . It can be used by
extensions that extend the Admin Panel with custom modules and allows storing
additional request-related data in the Admin Panel–specific data store.
The signature of the interface method
getDataToStore() has changed.
Impact
Extension authors may benefit from the additional argument passed with TYPO3
v14, but implementations must be adjusted accordingly.
Affected installations
Most installations are not affected, as few extensions extend the Admin Panel.
Instances with classes implementing
DataProviderInterface are affected.
The
getDataToStore() method is called by the Admin Panel after the
Response has been created by the TYPO3 Core.
Starting with TYPO3 v14, the method receives the
ResponseInterface
as an additional argument:
The random subpage option for shortcut pages has been removed from TYPO3
Core.
This option allowed shortcut pages to redirect to a random subpage, which was
problematic for caching and resulted in unpredictable behavior - a "random"
page was not truly random, as the page linking to this shortcut was cached.
The following changes have been made:
The class constant
\TYPO3\CMS\Core\Domain\Repository\PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE
has been removed.
The method signature of
\TYPO3\CMS\Core\Domain\Repository\PageRepository::resolveShortcutPage()
has changed from
resolveShortcutPage(array $page, bool $resolveRandomSubpages = false, bool $disableGroupAccessCheck = false)
to
resolveShortcutPage(array $page, bool $disableGroupAccessCheck = false).
The TCA configuration for
pages.shortcut_mode no longer includes the
option Random subpage of selected/current page.
Impact
Code using the removed constant
\TYPO3\CMS\Core\Domain\Repository\PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE
will fail with a PHP fatal error.
Code calling
PageRepository::resolveShortcutPage() with three parameters,
where the second parameter was
$resolveRandomSubpages, will fail. The
second parameter is now
$disableGroupAccessCheck.
Shortcut pages configured to use random subpage mode will now behave as if
they were configured for first subpage mode.
Affected installations
TYPO3 installations with:
Extensions using the constant
PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE
Extensions calling
PageRepository::resolveShortcutPage() with the
$resolveRandomSubpages parameter
Extensions extending
PageRepository
and overriding
getPageShortcut()
Shortcut pages configured with random subpage mode in the database
Migration
Remove any usage of
PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE.
Update calls to
PageRepository::resolveShortcutPage() to remove the
$resolveRandomSubpages parameter:
For shortcut pages configured with random subpage mode, update the database
records to use a different shortcut mode (for example, first subpage or a
specific target page):
UPDATE pages
SET shortcut_mode = 1WHERE shortcut_mode = 2;
Copied!
Breaking: #107677 - Drop prepend and append modes from TCA value picker
The prepend and append modes of the value picker specified in TCA and used
in FormEngine (for example
$GLOBALS['TCA']['tx_example']['columns']['example']['config']['valuePicker']['mode'] )
have been removed.
These modes were designed to insert predefined values before or after the
existing input value, but they served only niche use cases and caused
inconsistent behavior across different input types.
Maintaining these modes introduced unnecessary complexity in both
implementation and accessibility. Prepending or appending content dynamically
to user input fields could easily lead to unexpected results, break input
validation, and interfere with assistive technologies such as screen readers.
Additionally, this approach mixed presentation and data logic in ways that are
not consistent with modern form handling patterns.
Future improvements to the value picker will focus on a consistent
mode=replace behavior and may be implemented as new form input types to
provide a more robust and accessible user experience.
Impact
Any value picker TCA configuration using the mode options prepend or
append will no longer have any effect. TYPO3 ignores these settings, and the
picker defaults to the standard replace behavior.
Affected installations
Installations using custom field wizard configurations or integrations that
rely on the value picker with
mode = prepend or
mode = append
are affected.
Migration
There is no direct replacement for the removed modes.
If your implementation depends on prepending or appending content to existing
values, implement a custom input type or custom form element to handle
this behavior explicitly. This allows you to maintain full control over the
user interface, data handling, and accessibility.
For most use cases, it is recommended to replace prepend or append logic with
the standard replace value picker behavior or use dedicated UI controls that
clearly indicate how values are modified.
If the value picker was used to suggest combinable values, consider listing
these elements in the field description instead so that users can copy and
paste them manually into the input field.
Breaking: #107712 - New method hasSubmoduleOverview() in ModuleInterface
The interface
\TYPO3\CMS\Backend\Module\ModuleInterface has been extended
with a new method
hasSubmoduleOverview() to support the new card-based
submodule overview feature introduced in
Feature: #107712 - Introduce card-based submodule overview.
Impact
All custom implementations of
\TYPO3\CMS\Backend\Module\ModuleInterface must now implement the new
method
hasSubmoduleOverview(): bool.
Existing implementations that do not implement this method will trigger a PHP
fatal error.
Affected installations
TYPO3 installations with custom PHP code that directly implement the
ModuleInterface are affected.
This is uncommon, as most backend modules use the provided
\TYPO3\CMS\Backend\Module\Module class or extend from
\TYPO3\CMS\Backend\Module\BaseModule .
Migration
Add the
hasSubmoduleOverview() method to your custom implementation of
ModuleInterface .
The method should typically return the configured value rather than a fixed
boolean:
useTYPO3\CMS\Backend\Module\ModuleInterface;
classMyCustomModuleimplementsModuleInterface{
protectedarray $configuration = [];
publicfunctionhasSubmoduleOverview(): bool{
// Return the configured value, defaulting to falsereturn$this->configuration['showSubmoduleOverview'] ?? false;
}
}
Copied!
This allows the behavior to be controlled through the module's configuration.
Breaking: #107777 - Use strict types in Extbase class Argument
The method
\TYPO3\CMS\Core\Resource\Index\ExtractorRegistry::registerExtractionService()
has been removed in favor of automatic registration via the interface
\TYPO3\CMS\Core\Resource\Index\ExtractorInterface .
Registration of metadata extractors now happens automatically when a class
implements the required interface
ExtractorInterface . No further
registration is necessary.
Impact
Any call to
ExtractorRegistry::registerExtractionService() is now a
no-op and has no effect in TYPO3 v14.0+. Metadata extractors are automatically
registered via the interface.
Affected installations
TYPO3 installations with custom extensions that register metadata extractor
classes via the mentioned method in ext_localconf.php.
The extension scanner will report such usages.
Migration
The method has been removed without deprecation in order to allow extensions
to work with TYPO3 v13 (using the registration method) and v14+ (using automatic
interface-based registration) without additional deprecations.
Remove the manual registration from ext_localconf.php:
Since custom extractors already implement the required interface
ExtractorInterface , no further
changes are required inside the extractor class itself.
Breaking: #107784 - Remove backend layout data provider registration via $GLOBALS
The possibility to register backend layout data providers via
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider']
has been replaced by autoconfiguration using the service tag
page_layout.data_provider.
The tag is automatically added when a class implements
\TYPO3\CMS\Backend\View\BackendLayout\DataProviderInterface . Manual
configuration via Services.yaml remains possible, especially when
autoconfiguration is disabled.
Additionally, the possibility to dynamically add backend layout data providers
to the global
DataProviderCollection via its
add() method has been
removed. Developers should register their data providers as service definitions
in the container as described above.
Impact
Using the global array
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider']
to register backend layout data providers has no effect in TYPO3 v14.0 and
later.
Affected installations
All installations that use
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider']
for backend layout data provider registration are affected.
This registration is typically done in an ext_localconf.php file.
The extension scanner will report such usages.
Migration
Migrate existing registrations to the new autoconfiguration-based approach.
TYPO3 Core TCA and user settings (
$GLOBALS['TYPO3_USER_SETTINGS'])
configurations have been updated to use short form
translation reference formats (e.g., core.form.tabs:*) instead of
the full LLL:EXT: path format in showitem strings.
This change affects all core TCA showitem definitions that previously
used full LLL:EXT: paths for labels. The most prominent updates are
tab labels using the --div-- syntax, though this pattern may be applied
to other TCA elements in the future.
Examples of changed references in tab labels:
// Before (TYPO3 v13)'--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general''--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access''--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language'
Copied!
// After (TYPO3 v14)'--div--;core.form.tabs:general''--div--;core.form.tabs:access''--div--;core.form.tabs:language'
Copied!
Impact
Custom extensions that programmatically manipulate TCA or
$GLOBALS['TYPO3_USER_SETTINGS']showitem strings
from core tables and expect the full LLL:EXT: path format will break.
This particularly affects code that:
Uses string search/replace operations on showitem strings to find or
modify specific labels (tabs, palettes, or other elements)
Parses showitem strings using regular expressions expecting the
LLL:EXT: pattern
Extracts translation keys from TCA configurations for analysis or
documentation purposes
Builds custom TCA configurations by copying and modifying core
showitem strings
Currently, the following label categories have been migrated to short-form:
Removed the hardcoded palette name in string --palette--;Capabilities;capabilities
in table
sys_file_storage in favor of a label attached directly to
the palette using the short syntax core.form.palettes:*.
Field label overrides removed in showitem definitions:
Field labels can be overridden in showitem definitions for types or palettes, but
should rather be kept in the field definition itself. The following field label
overrides have been removed from showitem strings in favor of using the field's
own label definition:
Programmatically read and manipulate TCA showitem strings from
$GLOBALS['TCA'] or
$GLOBALS['TYPO3_USER_SETTINGS'].
Override core TCA or TYPO3_USER_SETTINGS by copying and modifying
existing showitem configurations
Perform string operations on showitem definitions expecting
specific LLL:EXT: path formats
Generate documentation or analysis tools based on TCA label path references
The extension scanner will not detect these usages, as they involve runtime
string manipulation rather than direct PHP API usage.
Note:
Additional TCA elements beyond tab labels may follow this pattern in future
TYPO3 versions, further extending the use of short-form references in
showitem strings.
Migration
TCA Migration
Extension developers should review their Configuration/TCA/Overrides/
files and any PHP code that manipulates TCA showitem strings
programmatically.
Option 1: Support both formats in string operations
Update your code to handle both the old LLL:EXT: path format and the
new short-form references:
// Before - hardcoded search for old formatif (str_contains($showitem, 'LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general')) {
// Will not work in TYPO3 v14+
}
Copied!
// After - handle new formatif (str_contains($showitem, 'core.form.tabs:general') ||
str_contains($showitem, 'LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general')) {
// Works in both versions
}
Copied!
Option 2: Use the TCA API instead of string manipulation
Rather than manipulating showitem strings directly, use TYPO3's TCA
manipulation APIs:
// Instead of string manipulationuseTYPO3\CMS\Core\Utility\ExtensionManagementUtility;
// Add fields using the API
ExtensionManagementUtility::addToAllTCAtypes(
'tx_myext_domain_model_foo',
'my_field',
'',
'after:title'
);
Copied!
Recommended action for all extension developers
Scan your extension's Configuration/TCA/Overrides/ directory and any
PHP code that interacts with showitem strings for patterns such as:
str_contains(),
str_replace(),
preg_match() or similar
string functions operating on showitem values
String operations looking for 'LLL:EXT:' patterns in TCA
configurations
Custom parsing of
$GLOBALS['TCA'] showitem strings expecting
specific path formats
This review is especially important since future TYPO3 versions may further
expand the use of short-form references across additional TCA elements.
TYPO3_USER_SETTINGS migrations
Update your code to handle the new short form references:
Before:
EXT:my_extension/ext_tables.php
// Before - hardcoded search for old format
$showitem = $GLOBALS['TYPO3_USER_SETTINGS']['showitem'];
if (str_contains($showitem, 'LLL:EXT:setup/Resources/Private/Language/locallang.xlf:personal_data')) {
// Will not work in TYPO3 v14+
}
Copied!
After:
EXT:my_extension/ext_tables.php
// After - handle new format
$showitem = $GLOBALS['TYPO3_USER_SETTINGS']['showitem'];
if (str_contains($showitem, 'core.form.tabs:personaldata')
|| str_contains($showitem, 'LLL:EXT:setup/Resources/Private/Language/locallang.xlf:personal_data')) {
// Works in both versions
}
The Reports module has been refactored to use native backend submodules instead
of dynamic service-based report registration. Individual reports are now
registered as proper backend submodules in Configuration/Backend/Modules.php.
As a result, the following public interfaces have been removed:
\TYPO3\CMS\Reports\ReportInterface
\TYPO3\CMS\Reports\RequestAwareReportInterface
Impact
Extensions that register custom reports by implementing
\TYPO3\CMS\Reports\ReportInterface or
\TYPO3\CMS\Reports\RequestAwareReportInterface will no longer work.
These reports will no longer appear in the backend Reports module.
Affected installations
TYPO3 installations with custom extensions that provide reports by implementing
\ReportInterface or
\RequestAwareReportInterface.
Migration
Custom reports must be migrated to backend submodules.
The controller should implement a standard PSR-7 request handler that returns a
\Psr\Http\Message\ResponseInterface instance.
Alternatively, you can create a standalone module with
showSubmoduleOverview enabled if you need to group multiple reports
under your own container module.
Breaking: #107823 - Strict typing and API cleanup in backend template components
The backend template components system (buttons, dropdown items, and menus) has
been modernized with strict type hints, consistent return types, and improved
architecture to enhance type safety and developer experience.
Impact
Extensions that implement or extend backend template components must verify
their type declarations and update any usage of changed methods.
New ComponentInterface
A new
\TYPO3\CMS\Backend\Template\Components\ComponentInterface
has been introduced as the parent interface for both
\ButtonInterface and
\DropDownItemInterface.
This unifies the common contract for all renderable backend components.
Both interfaces now extend
ComponentInterface ,
which defines:
isValid(): bool
getType(): string
render(): string
Custom implementations of
\ButtonInterface
or
\DropDownItemInterface will
now trigger a
\TypeError if these return types are missing.
PositionInterface enforced
The
\TYPO3\CMS\Backend\Template\Components\PositionInterface now enforces
strict type hints:
getPosition(): string
getGroup(): int
This interface allows buttons to define their own fixed position and group,
which automatically override the position and group parameters passed to
ButtonBar::addButton().
Icon nullability
Icons are now consistently nullable across all button types. The
AbstractButton::$icon property and related getter/setter methods now use
?Icon instead of
Icon.
This affects classes extending
AbstractButton , including
LinkButton ,
InputButton , and
SplitButton .
Note
While icons are technically optional at the type level, validation methods
may still require icons for buttons to be considered valid.
Method signature changes
Several methods now have stricter parameter types or modified signatures:
MenuItem::isValid() and
Menu::isValid() no longer accept
parameters
AbstractDropDownItem::render() now declares a
string return
type
Various setter methods now require strict type hints for their parameters
Return type consistency
Abstract classes now use
static return types for better inheritance
support, while concrete implementations may use
self or
static
depending on extensibility requirements.
SplitButton API improvement
The
SplitButton::getButton() method has been replaced with
getItems(), which returns a type-safe
SplitButtonItems DTO
instead of an untyped array.
Old (removed):
publicfunctiongetButton(): array // Returnsarraywithmagickeys 'primary' and 'options'
public readonly AbstractButton $primary - The primary action button
public readonly array $options - Array of option buttons
This change improves type safety and prevents runtime errors from accessing
non-existent array keys.
Affected installations
All TYPO3 instances with custom backend components, such as buttons, menus, or
dropdown items, that extend or implement the affected interfaces are impacted.
Migration
Extension authors should:
Verify custom button implementations have correct return types on
interface methods.
Check custom classes extending abstract buttons use proper strict types.
Update `isValid()` calls on
MenuItem and
Menu objects
(remove the parameter).
Handle nullable icons when working with
getIcon() methods.
The frontend rendering related event
\TYPO3\CMS\Frontend\Event\AfterCachedPageIsPersistedEvent has been
modified due to the removal of the class
\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController.
The method
getController() has been removed.
Impact
Event listeners that call
getController() will now trigger a fatal
PHP error and and must be adapted.
Affected installations
Instances with extensions listening for the event
AfterCachedPageIsPersistedEvent may be
affected.
The extension scanner will detect and report such usages.
Migration
In most cases, data that was previously retrieved from the
\TYPO3\CMS\Frontend\Controller\`TypoScriptFrontendController
instance can now be accessed through the request object, available via
$event->getRequest().
All remaining properties have been removed from
\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController, making the
class a readonly internal service used by the TYPO3 Core only.
The class will be fully removed in a later TYPO3 v14 release.
The following instance access patterns have been removed:
All API methods that returned an instance of
\TypoScriptFrontendController -
usually named
getTypoScriptFrontendController() or similar - have been
removed as well.
Impact
Any remaining direct or indirect usage of
\TypoScriptFrontendController will
now result in a fatal PHP error.
Affected installations
Extensions that still relied on properties or methods of
\TypoScriptFrontendController are
affected.
The class was already marked internal and breaking in TYPO3 v13.
In particular, extensions that used
AbstractContentObject->getTypoScriptFrontendController() can now access
the relevant data from the PSR-7 request object, for example:
The following public properties of the PHP class TYPO3\CMS\Core\DataHandling\DataHandler have been removed:
copyWhichTables
neverHideAtCopy
copyTree
Impact
Accessing or setting the properties will throw a PHP warning and have no effect anymore.
Affected installations
Any installation working with the public properties in a third-party extension.
Migration
The configuration values neverHideAtCopy and copyTree are directly read from the
backend user
BE_USER object. To modify them, use the following values instead:
// Before
DataHandler->neverHideAtCopy
// After
DataHandler->BE_USER->uc['neverHideAtCopy']
// Before
DataHandler->copyTree
// After
DataHandler->BE_USER->uc['copyLevels']
Copied!
To retain database consistency, the list of tables to be copied now only relied on permissions
of the given backend user. If the user has admin access, all tables will be copied if needed.
If not, all tables with access will be copied.
Breaking: #107871 - Remove backend avatar provider registration via
$GLOBALS
Registering backend avatar providers via
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['avatarProviders']
has been replaced by autoconfiguration using the
backend.avatar_provider
service tag. This tag is added automatically when the PHP attribute
#[AsAvatarProvider] is applied. Manual configuration in
Services.yaml is still possible, particularly if autoconfiguration is
disabled.
Impact
Using the
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['avatarProviders']
array has no effect in TYPO3 v14.0 and later.
Affected installations
All installations that use
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['avatarProviders']
to register backend avatar providers are affected. This registration is
typically performed in an ext_localconf.php file. The extension
scanner will report any such usages.
Migration
Migrate existing registrations to the new autoconfiguration-based approach.
If you need to support multiple TYPO3 Core versions simultaneously, ensure that
both registration methods are implemented: the legacy
$GLOBALS based
registration as well as the new tag-based approach.
Breaking: #107884 - Rework actions to use Buttons API with Components
The record list and file list action system (the "button bar" in every
row of the table-like display) has been completely reworked to use
the Buttons API, utilizing proper component objects instead of plain HTML strings.
This modernization improves type safety, provides better extensibility, and
enables more structured manipulation of action buttons through PSR-14 events.
The following components have been affected by this change:
Buttons can now be placed into ActionGroups, which are identified by the
PHP enum
\TYPO3\CMS\Backend\Template\Components\ActionGroup and
distinguish between a "primary" and a "secondary" group.
In addition,
\TYPO3\CMS\Backend\Template\Components\ComponentGroup
enhances the ability to group multiple Button API Components into a single data
object and manage their state.
Impact
Extensions that listen to the
ModifyRecordListRecordActionsEvent
or
ProcessFileListActionsEvent to modify
record or file actions need to be updated.
The events no longer work with HTML strings but with
ComponentInterface objects
(see forge#107823).
Extensions that directly call
DatabaseRecordList::makeControl() must
update their code, as the
$table parameter has been removed.
ModifyRecordListRecordActionsEvent
The method
setAction() now requires a
ComponentInterface object, and
getAction() now returns either null or a
ComponentInterface instance.
The following methods now expect an
ActionGroup enum value as the
$group parameter:
hasAction()
getAction()
removeAction()
getActionGroup()
The method
getRecord() no longer returns a raw array but an instance of
the Record API.
A new method
getRequest() has been added to access the current PSR-7
request context.
Removed methods:
getActions()
setActions()
getTable()
ProcessFileListActionsEvent
The
\TYPO3\CMS\Filelist\Event\ProcessFileListActionsEvent has received
identical API changes to
ModifyRecordListRecordActionsEvent ,
allowing manipulation of items in both supported
ActionGroup contexts
(primary and secondary).
New methods:
setAction()
getAction()
removeAction()
moveActionTo()
getActionGroup()
getRequest()
Buttons can now also be repositioned or inserted at specific before/after
locations within an action group.
Removed methods:
getActionItems()
setActionItems()
Affected installations
TYPO3 installations with custom PHP code that modifies record or file list
actions, or utilizes the mentioned PSR-14 events, are affected.
This event now behaves identically to the file list event:
actions must be created via the Button API and added as
ComponentInterface instances using
setAction().
The
setActions() and
getActions() methods are removed and must be
replaced by distinct calls to
setAction() or use
getActionGroup() to access existing actions.
The
getRecord() method now returns a Record API object instead of an
array.
getTable() can be replaced with
getRecord()->getMainType().
The
resource property
external in the
PAGE properties
includeCSS,
includeCSSLibs,
includeJS,
includeJSFooter,
includeJSFooterlibs and
includeJSLibs
is now obsolete.
Instead of marking URIs as URIs with an additional option, prefix the URI, that shall be used with URI:,
or simply use absolute URLs starting with http(s)://, where the prefix is not required.
This URI resource definition will work across the system, not only in TypoScript or AssetCollector/ AssetRenderer.
Impact
Using the external property in TypoScript or the external option in AssetCollector
will have no effect any more. If absolute URLs have been used as resources, everything
will work as before. Relative URIs must be prefixed with URI: from now on, otherwise
an exception is thrown.
Additionally, the string after the URI: keyword must be a valid URI, otherwise an exception
is thrown as well. Before this change, invalid URIs (marked as external) would have been rendered
to HTML, without any obvious feedback for developers or integrators. Browsers then ignored
such invalid references.
Affected installations
TYPO3 installations using the external property in TypoScript or the external option in AssetCollector.
Migration
TypoScript before:
page = PAGE
page.includeCSS {
main = https://example.com/styles/main.css
main.external = 1
other = /styles/main.css
other.external = 1
}
Copied!
TypoScript after:
page = PAGE
page.includeCSS {
main = https://example.com/styles/main.css
other = URI:/styles/main.css
}
The TYPO3 frontend and backend applications previously allowed compressing
their HTTP responses using the configuration options
$GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] and
$GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel'] .
This feature, which was always disabled by default, has now been removed.
TYPO3 will no longer compress its HTTP responses itself.
Response compression should be handled by the web server rather than the
application layer.
Removing this feature avoids potential conflicts when both TYPO3 and the web
server attempt to compress responses and allows modern web servers to use
advanced compression algorithms such as brotli or zStandard when supported by
the client.
Impact
TYPO3 can no longer compress its HTTP responses.
This responsibility is now fully delegated to the web server.
HTTP response compression had to be explicitly enabled before, so most
installations will not notice a change unless they relied on this setting.
Affected installations
Instances that configured
$GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] or
$GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel'] to non-zero values
are affected.
Administrators should verify that the web server applies HTTP compression by
checking for a response header such as:
Content-Encoding: gzip
when requesting frontend or backend documents with a header like:
Accept-Encoding: gzip, deflate
All commonly used web servers enable this feature by default.
Migration
The configuration toggles for the backend
$GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel']
and the frontend
$GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] are
obsolete, existing settings in settings.php configuration files are
actively removed when first using the install tool after upgrade to TYPO3 v14.
Breaking: #107944 - Frontend CSS file processing no longer removes comments and whitespaces
When the TYPO3 frontend was configured to compress included CSS assets, it also
attempted to minify CSS by removing comments and certain whitespace characters.
This behavior has now been removed. The previous implementation was brittle,
especially with modern CSS syntax, and provided no measurable performance
benefit in either file transfer or client-side parsing.
Impact
CSS asset files included in frontend pages may become slightly larger if they
contain many comments. TYPO3’s internal CSS parsing was disabled by default and
only active when explicitly enabled using
$GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel']
along with additional TypoScript configuration.
In most cases, this change has minimal or no practical impact.
Affected installations
Instances that actively used TYPO3's built-in CSS parsing feature for frontend
asset management are affected.
Migration
If minimizing CSS file size is important, consider one of the following options:
Optimize or minify CSS files manually during deployment.
Accept that comments and whitespace are retained (usually negligible impact).
Preferably, integrate a dedicated frontend build chain to handle CSS and
JavaScript minification.
Modern frontend build tools provide many additional advantages, such as
linting, syntax validation, and advanced optimizations, which are beyond the
scope of the TYPO3 Core.
Breaking: #107945 - Class FlexFormService merged into FlexFormTools
The class
\TYPO3\CMS\Core\Service\FlexFormService has been merged into
\TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools .
The following methods are affected:
FlexFormService->convertFlexFormContentToArray($flexFormContent, $languagePointer = 'lDEF', $valuePointer = 'vDEF'): array
is now
FlexFormTools->convertFlexFormContentToArray(string $flexFormContent): array.
The method name is unchanged, but the method signature has been simplified.
FlexFormService->convertFlexFormContentToSheetsArray(string $flexFormContent, string $languagePointer = 'lDEF', string $valuePointer = 'vDEF'): array
is now
FlexFormTools->convertFlexFormContentToSheetsArray(string $flexFormContent): array.
Again, the name is identical, but the parameters have been reduced.
The helper method
FlexFormService->walkFlexFormNode() has been made a
private method within
FlexFormTools.
Impact
Instantiating or injecting
FlexFormService
remains possible in TYPO3 v14 due to a maintained class alias for backward
compatibility.
This alias will be removed in TYPO3 v15.
Affected installations
Any extensions or TYPO3 installations using
FlexFormService are affected.
The extension scanner will automatically detect these usages.
Migration
Extensions typically did not use the now internal helper method
walkFlexFormNode().
In the unlikely case this private method was used, its functionality must now be
implemented within the consuming extension.
The methods
convertFlexFormContentToArray() and
convertFlexFormContentToSheetsArray() have lost their second and third
arguments.
These parameters (
lDEF and
vDEF) were already fixed internally in
TYPO3 and could no longer be changed, so their removal has no functional impact.
To continue using these methods, extensions should inject
FlexFormTools instead of
FlexFormService.
For extensions that need to remain compatible with both TYPO3 v13 and v14, it is
still possible to use
FlexFormService for
now.
However, when adding compatibility for TYPO3 v15 (and dropping TYPO3 v13),
extensions must switch fully to
FlexFormTools .
Breaking: #108054 - Enforce explicit opt-in for TypoScript/TSconfig callables
To strengthen TYPO3's security posture and implement defense-in-depth
principles, a new hardening mechanism has been introduced that requires
explicit opt-in for methods and functions that can be invoked through
TypoScript configuration.
The new PHP attribute
#[\TYPO3\CMS\Core\Attribute\AsAllowedCallable] must be applied to
any method that should be callable via:
TypoScript
userFunc processing (including the
USER and
USER_INT content objects)
TypoScript
stdWrap functions
preUserFuncInt,
postUserFunc and
postUserFuncInt
TypoScript constant comment user functions
TSconfig
renderFunc in suggest wizard configuration
This security enhancement implements strong defaults through explicit
configuration, following the principle of least privilege.
Implementation details:
New
\TYPO3\CMS\Core\Attribute\AsAllowedCallable PHP attribute
New
\TYPO3\CMS\Core\Security\AllowedCallableAssertion service
for validation
Extension code that provides custom processing methods callable from
TypoScript or TSconfig will fail with a
\TYPO3\CMS\Core\Security\AllowedCallableException if the target
method is not explicitly marked with the
#[AsAllowedCallable]
attribute.
The error message will be:
Attribute TYPO3\CMS\Core\Attribute\AsAllowedCallable required for
callback reference: ["VendorName\\ExtensionName\\ClassName","methodName"]
Copied!
Affected installations
Scenarios using:
custom processing via TypoScript userFunc
custom processing via TypoScript constant comments
custom suggest wizard rendering via TSconfig renderFunc
Migration
Add the
#[AsAllowedCallable] attribute to all methods that should
be callable from TypoScript or TSconfig.
The implementation of CSS and JavaScript asset concatenation and
pre-compression has been removed from the TYPO3 Core in v14.
The following TypoScript options are now obsolete:
Config property
config.compressCss
Config property
config.compressJs
Config property
config.concatenateCss
Config property
config.concatenateJs
The
resource properties
disableCompression and
excludeFromConcatenation in the
PAGE properties
includeCSS,
includeCSSLibs,
includeJS,
includeJSFooter,
includeJSFooterlibs
and
includeJSLibs.
The configuration option
$GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] is obsolete
in combination with Breaking: #107943 - Frontend and backend HTTP response compression removed. Existing settings
in settings.php configuration files are automatically removed
when the install tool is first used after upgrading to TYPO3 v14.
The PHP class
\TYPO3\CMS\Core\Resource\ResourceCompressor has
been removed.
Feature rundown
In TYPO3 versions prior to v14, the system included a built-in mechanism
to compile multiple registered CSS and JavaScript files into single files
and to prepare compressed versions of those concatenated files for
direct delivery by the web server.
This functionality had to be explicitly enabled by setting the global
configuration option
$GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] and the
TypoScript options
config.concatenate* and optionally
config.compress*. CSS and JavaScript files registered in the
frontend using the
PAGE.include* options were then
concatenated and optionally compressed into a single file.
The implementation also concatenated external asset resources referenced
via http and https, which were fetched server-side using
GeneralUtility::getUrl() and merged with local resources.
Concatenation was designed to be as transparent as possible: hashes were
created from all referenced file names and their contents (including
external content) for each TYPO3 frontend page request, generating
unique file names that changed whenever an included file or its content
was modified. The resulting concatenated files were then referenced in
the generated HTML page instead of the original resource links.
Pre-compression operated on top of concatenation: if enabled, a gzip-
compressed .gz file was created and referenced in the HTML page
response.
Configuration was handled via TypoScript, while asset registration could
be performed using TypoScript or PHP with the
PageRenderer. The
Fluid asset ViewHelpers
f:asset.css and
f:asset.script
register assets using the
AssetCollector, introduced in TYPO3
v10. This implementation never supported concatenation or compression,
as it was developed independently from the PageRenderer's asset handling
and did not use the
ResourceCompressor.
Concatenation and compression removal reasoning
A closer look at the concatenation and compression functionality reveals
several reasons for its removal:
HTTP/2 and HTTP/3: Modern HTTP versions allow multiple resource
requests in parallel ("multiplexing"), making server-side asset
concatenation obsolete. These versions also provide significant
performance improvements on both the server and client sides compared
to HTTP/1.1 with concatenation. HTTP/2 and HTTP/3 are only available
via SSL (HTTPS), which is now the standard for all serious websites.
All major web servers and browsers have supported at least HTTP/2 for
years.
Fragile implementation: Concatenating multiple CSS files within
the application caused path and encoding issues. The resulting files
had to be stored in writable public directories, and relative paths in
CSS files (such as those in
@import rules) had to be parsed and
adjusted. Additionally, the CSS statement
@charset had to be
parsed since it must appear only once per CSS file, leading to
potential collisions that have never been resolved.
Parallel systems: Concatenation and compression were supported
only for assets registered via TypoScript
page.include*
or the
PageRenderer in PHP. The Fluid ViewHelpers
f:asset.css and
f:asset.script operated independently
and never supported these features. Removing concatenation and
compression simplifies future unification of both asset systems.
Performance issues with external assets: To create as few asset
resources as possible, external assets (example:
https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js)
were by default (if
excludeFromConcatenation = 1 was not
explicitly set) fetched by TYPO3 when creating the page. The hashed
resource path and the file content were part of the created
concatenation filename. This ensured that the server-side created file
was always current. TYPO3 therefore fetched external resources for
each uncached page request. Worse, it fetched those external
resources for each request that contained a non-cached content
element ("INT" elements), and also for each request when an instance
enabled CSP handling. This could easily lead to severe performance
degradation.
Compression dependency: Pre-compression (gzip) was only available
when concatenation was enabled, adding further complexity and
limitations.
HTTP violations with caching: When TYPO3 cached a page that
referenced pre-compressed assets, it stored the version based on the
client’s Accept-Encoding header. Subsequent requests from
clients not supporting gzip would still receive references to
compressed assets, violating HTTP standards.
Double compression risk: Web servers might automatically re-
compress already compressed files. TYPO3 tried to prevent this with an
Apache-specific .htaccess configuration, but other servers
like nginx required custom setups, often confusing integrators.
Modern compression standards: Modern web servers and browsers
support more efficient algorithms such as Brotli and Zstandard, which
TYPO3 never implemented due to the complexity of its existing system.
Minimal performance gain: The benefit of pre-compression was
minor. Modern web servers can compress small assets (like typical CSS
or JS files) on the fly with negligible overhead. As a rough ballpark
estimation, a single CPU core can compress hundreds of MB per second,
while a large JavaScript library like jQuery is usually below 100 KB.
CPU time is rarely the bottleneck.
In summary, asset concatenation and pre-compression are no longer needed
and should not be handled at the application level. The implementation
was riddled with issues over the years and integrators struggled to
reach working solutions. The Core team closed issues in this area for
years with "Won't fix, use a different solution."
Alternatives
Most TYPO3 instances can operate perfectly well without the removed
asset concatenation and compression features. Modern browsers, web
servers, and HTTP protocol versions provide efficient alternatives that
make the previous TYPO3-internal implementation unnecessary.
If your instance still relies on concatenated or pre-built asset bundles
for specific use cases, consider the following alternatives:
Use a modern bundler: Tools such as Vite
or Webpack can handle asset concatenation, minification, and
optimization during the build process. A TYPO3-specific integration is
available as an extension:
Vite AssetCollector.
Consider the `sgalinski/scriptmerger` extension: This community
extension provides an alternative approach to script and stylesheet
merging and compression. It may be useful for projects that cannot yet
switch to build-time bundling.
Enable HTTP/2 or HTTP/3: Modern HTTP versions support
multiplexing, allowing browsers to download multiple assets
simultaneously from a single connection, eliminating the need for
server-side concatenation.
# Enable HTTP/2 on your SSL virtual host
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.crt;
ssl_certificate_key /etc/ssl/private/example.key;
# Serve pre-compressed assets if available
gzip_static on;
# Optionally also enable on-the-fly compression
gzip on;
gzip_types text/css application/javascript;
[...]
}
Copied!
Both configurations ensure that compressed versions of static assets
(e.g., .js.gz or .css.gz) are automatically delivered
to clients that support gzip encoding.
Both Apache and nginx can also cache the compressed output in memory
or on disk to avoid runtime overhead. The keywords to look for are
mod_deflate with mod_cache for Apache, and
proxy_cache for nginx.
Use a Content Delivery Network (CDN): For TYPO3 instances
experiencing heavy frontend traffic or high asset load, a CDN can
serve static resources such as CSS, JavaScript, and images directly
from distributed edge servers. This offloads delivery from the main
web server, reduces latency, and improves caching efficiency.
In summary, most TYPO3 setups can safely rely on HTTP/2, modern build
pipelines, and proper web server or CDN configuration to achieve optimal
frontend performance without any TYPO3-internal concatenation or
compression.
Impact
The configuration toggles mentioned above are now obsolete, and TYPO3
will no longer concatenate or compress included assets.
Affected installations
Instances that configured
$GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] to non-zero
values and enabled the TypoScript settings described above for asset
concatenation or compression are affected.
Migration
Consider one or more of the alternatives outlined above.
Breaking: #108055 - Removed PageRenderer related hooks and methods
no longer receive the array keys
compress or
excludeFromConcatenation inside the following data array keys:
jsFiles
jsInline
jsLibs
cssFiles
cssInline
cssLibs
Impact
Calling any of the removed methods listed above will raise PHP fatal errors.
Registrations for removed hooks are no longer executed. Submitting ignored
arguments has no effect anymore, and hook consumers receive slightly different
data from the TYPO3 Core due to removed TypoScript configuration values.
Affected installations
Instances with extensions dealing with low level asset manipulation may be
affected. The extension scanner will find affected extensions when they call
removed methods or hooks.
Migration
There is no direct one-to-one migration in this case.
In general, extensions must no longer expect the existence of code related to
the TypoScript configuration options
config.compressCss,
config.compressJs,
config.concatenateCss,
config.concatenateJs, and the
resource properties
disableCompression and
excludeFromConcatenation.
The removed hooks and "handlers" can be turned into listeners of:
Existing hook registrations of these three should check whether the
implementations access the array keys
compress and
excludeFromConcatenation and avoid doing so. If required, affected code
may need to determine TypoScript options from the
$GLOBALS['TYPO3_REQUEST']
request attribute
frontend.typoscript directly.
Another alternative is to avoid hook usage altogether by turning the
implementations into PSR-15 middlewares instead.
Breaking: #108084 - Allow rootless paths in URI implementation
Previously, the TYPO3 implementation of
\UriInterface
always prefixed rootless paths (paths without a preceding slash) with a slash.
With this normalization in place, it was impossible to represent rootless
paths. This has now changed so that a slash is only prepended to the path
when an authority (host name) is present.
Example
Input: rootless/path/
Examples with different URIs
useTYPO3\CMS\Core\Http\Uri;
$uri = new Uri('rootless/path/');
$uriAsString = (string)$uri;
// before: /rootless/path/// after: rootless/path/// Same behavior with authority
$uri = (new Uri('https://example.com'))->withPath('rootless/path/');
$uriAsString = (string)$uri;
// before: https://example.com/rootless/path/// after: https://example.com/rootless/path/// Colon in first path segment
$uri = new Uri('rootless:path/to/resource');
$uriAsString = (string)$uri;
// before: /rootless:path/to/resource/// after: ./rootless:path/to/resource/
Copied!
Impact
Regarding top level TYPO3 API and functionality, nothing has changed. Required
TYPO3 code has been adapted.
Third party code that uses the
Uri class
directly will get different results when representing rootless paths without
authority. Code that relied on the normalization done by TYPO3 before is
likely to break.
Since TYPO3 is always dealing with absolute paths, due to URL rewriting in the
backend and the frontend, it is unlikely that much third party code relies on
relative paths, so the impact is expected to be low.
Affected installations
Third party code that is using the
Uri class
directly and that is representing rootless paths without authority.
Breaking: #108093 - Add respectSubmittedDataValue argument in password ViewHelper
The
<f:form.password> ViewHelper now provides the argument
respectSubmittedDataValue, which allows configuration of whether a
submitted field value will be put into the HTML response of the form on
validation errors after submission. The default value of the new argument is
set to
false, resulting in a submitted field value being cleared on
validation errors of the form.
Impact
A submitted password will not remain as the value for the password field if
form validation fails.
Affected installations
TYPO3 instances using the
<f:form.password> ViewHelper.
Migration
If the submitted field value of the
f:form.password ViewHelper must
remain on validation errors of the form, users must adapt the password
ViewHelper usage as shown below:
Class
\TYPO3\CMS\Core\Mail\MailMessage is a data object that
should not contain service methods like
send(). The following methods
have been removed from this class:
send()
isSent()
Impact
Using the removed methods on instances of this class will raise fatal PHP
errors.
Affected installations
Instances that create
MailMessage objects
and call
send() or
isSent() are affected. The extension scanner
is not configured to find affected code since the method names are too generic.
Migration
The service (usually a controller class) that sends emails should be
reconfigured to get an instance of
MailerInterface
injected and should use that service to call
send().
Example before:
useTYPO3\CMS\Core\Mail\MailMessage;
final readonly classMyController{
publicfunctionsendMail(){
$email = new MailMessage();
$email->subject('Some subject');
$email->send();
}
}
Copied!
Example after:
useTYPO3\CMS\Core\Mail\MailMessage;
useTYPO3\CMS\Core\Mail\MailerInterface;
final readonly classMyController{
publicfunction__construct(
private MailerInterface $mailer
){}
publicfunctionsendMail(){
$email = new MailMessage();
$email->subject('Some subject');
$this->mailer->send($email);
}
}
Copied!
Breaking: #108113 - Globals _GET and _POST not reset to current Request data anymore
The frontend and backend application chain roughly splits like this:
Bootstrap
Create Request object from globals
Start application
Run middleware chain
Run RequestHandler to create a Response by calling controllers (backend) or
ContentObjectRenderer
(frontend)
There was old compatibility code in
RequestHandler
that reset the PHP global variables
_GET,
_POST,
HTTP_GET_VARS and
HTTP_POST_VARS to values that may have been
written to their Request object counterparts by middlewares.
This backwards compatibility layer has been removed.
Additionally, in frontend rendering, the global variable
$GLOBALS['TYPO3_REQUEST'] is no longer populated within the
PrepareTypoScriptFrontendRendering
middleware. It is now set later in
RequestHandler .
$GLOBALS['TYPO3_REQUEST'] itself is another compatibility layer that the
TYPO3 Core aims to phase out over time.
Impact
The impact is twofold:
Some TYPO3 Core middlewares manipulate the Request object's "GET"
parameter list (
$request->getQueryParams()) to, for example,
resolve the frontend slug into the page uid. This is itself a
backwards compatibility layer. Frontend-related code can no longer
expect these manipulated variables to exist in the globals
_GET,
_POST,
HTTP_GET_VARS and
HTTP_POST_VARS.
Middlewares that are executed after
PrepareTypoScriptFrontendRendering
(middleware key typo3/cms-frontend/prepare-tsfe-rendering)
can no longer rely on
$GLOBALS['TYPO3_REQUEST'] being set.
Affected installations
Instances running code that relies on the removed compatibility layers
may fail or lead to unexpected results.
Migration
Middlewares receive the Request object directly and should use it instead
of fetching it from
$GLOBALS['TYPO3_REQUEST']. Services triggered by
middlewares that rely on the Request should have it passed in explicitly.
One example frequently used in middlewares is
ContentObjectRenderer :
Code should in general never rely on the globals
_GET,
_POST,
HTTP_GET_VARS and
HTTP_POST_VARS. Request-related
state should always be fetched from the Request object. Note that the helper
method
GeneralUtility::getIndpEnv() will also be phased out once the
TYPO3 Core has removed its last remaining usages.
Breaking: #108114 - Global frontend content link prefixing removed
The frontend rendering contained logic that searched for links to resources
within the generated Response content to globally replace them with the
configured URL prefix (TypoScript
config.absRefPrefix).
This solution has always been brittle and has now been obsoleted with the
introduction of the System Resource API.
The global search and replace code has been removed, which also obsoletes
setting
$GLOBALS['TYPO3_CONF_VARS']['FE']['additionalAbsRefPrefixDirectories'] .
Impact
Generated content can no longer expect links to resources to be globally
"fixed". They must create the final URL themselves.
The obsolete
$GLOBALS['TYPO3_CONF_VARS']['FE']['additionalAbsRefPrefixDirectories']
will be automatically removed from settings.php after upgrading to
TYPO3 v14 and accessing the install tool.
Affected installations
The System Resource API is integrated into TYPO3 in a way that extensions
usually create proper links automatically as long as the provided Core API is
used.
Instances with extensions that hard code link generation may be affected.
Migration
Instances should check the rendered frontend for broken links after upgrading
to TYPO3 v14 and update hard coded link generation to use proper API calls,
for example based on the various URL, URI and asset-related Fluid ViewHelpers.
This change depends heavily on the specific extension code. There is no general
advice that covers all possible cases for extension developers.
Breaking: #108148 - CDATA sections in Fluid templates no longer removed
Previous versions of Fluid and TYPO3 removed code wrapped in <![CDATA[ ]]>
from template files altogether. This meant that it was possible to use CDATA
to comment out template code. This is no longer possible, since CDATA
sections are now interpreted by Fluid in a different way; see
Feature: #108148 - Alternative Fluid Syntax for CDATA Sections.
Impact
<![CDATA[ ]]> can no longer be used to comment out code in Fluid template
files.
Affected installations
Installations that contain Fluid templates using <![CDATA[ ]]> to comment
out code are affected. A deprecation has been written to the deprecation log
since TYPO3 13.4.21 if this construct is encountered in a Fluid template
during rendering.
Migration
To comment out code in Fluid templates, the
Comment ViewHelper <f:comment>
should be used. Since TYPO3 v13, potential Fluid syntax errors are ignored by
this ViewHelper, which allows commenting out invalid Fluid syntax safely.
With Fluid 5, it is no longer possible to define custom template variables
that start with an underscore (_). These variable names are reserved for
future internal use by Fluid itself, similarly to the already existing
{_all}.
Impact
This change affects ViewHelpers that define new variables, such as:
It also affects Fluid's PHP APIs, namely
$view->assign() and
$view->assignMultiple().
Affected installations
Installations with Fluid templates that use custom variable names starting
with an underscore (_) will encounter exceptions when such a template is
rendered. A deprecation has been written to the deprecation log since
TYPO3 13.4.21 if this is encountered in a Fluid template during rendering.
For template files already using the new *.fluid.* file extension, the built-in
template analyse command will discover affected template files:
vendor/bin/typo3 fluid:analyse
Copied!
For each affected template, the command will output an error like this:
[ERROR] packages/myext/Resources/Private/Templates/Test.fluid.html: Variable identifiers cannot start with a "_": _myvariable
Copied!
Migration
The following examples no longer work with Fluid 5:
#1756622558 TYPO3Fluid\Fluid\Core\Variables\InvalidVariableIdentifierException
Variable identifiers cannot start with a "_": _myVariable
Copied!
In all cases, the variable name must be changed to no longer start with an
underscore (_).
Note that this only affects variable names, not property names in objects or
array keys that are accessed within a Fluid template. The following examples
are not affected by this change:
{myArray._myKey}
{myObject._myProperty}
Copied!
Also note that the existing {_all} (and any further internal variables added
by Fluid) are not affected. This code will continue to work:
<f:renderpartial="Footer"arguments="{_all}"/>
Copied!
Breaking: #108148 - Strict Types in Fluid ViewHelpers
With Fluid 5, various changes have been made to use stricter types in the
context of ViewHelpers. This has consequences in three areas:
Validation of arguments passed to ViewHelpers
(see #1194 on GitHub)
Passing null values to ViewHelpers that generate a HTML tag, also
known as tag-based ViewHelpers
(see #1233 on GitHub)
Required type declarations for custom ViewHelper implementations
(see #1219 on GitHub)
Impact
ViewHelper argument validation
Fluid ViewHelpers now use stricter validation for their arguments by default.
The previous argument validation had numerous blind spots, which meant that
ViewHelper implementations couldn't really rely on the type requirements
specified in the ViewHelper's API. The new implementation performs a stricter
validation, which means that Fluid might reject arguments passed to ViewHelpers
that were previously considered valid (but which the ViewHelper in question
usually didn't know how to handle). The new implementation does however deal
with simple type conversions automatically, so that a ViewHelper that requires
a
string still can receive an
int as input.
For integrators, this change might reject certain ViewHelper arguments that
were previously valid, but not covered by the ViewHelper's specified API.
For developers of custom ViewHelpers, this change allows to get rid of custom
validation logic that was previously necessary due to Fluid's spotty
validation.
Note that the
Argument ViewHelper <f:argument>,
which can be used to define an API for a template, is not affected by this
change, as it already used the improved validation from the beginning.
Passing
null to tag-based ViewHelpers
Previously, Fluid's
TagBuilder class, which is used to create HTML tags
in tag-based ViewHelpers, treated
null values as empty strings, leading
to an HTML tag with an empty HTML attribute. With Fluid 5,
null values lead
to the HTML attribute being omitted from the resulting HTML tag.
In most cases, the impact of this change is non-existent. However, there are
some edge cases where this change is relevant. In TYPO3 Core, the
<f:image>
ViewHelper needed to be adjusted to always render the
alt attribute,
even if its internal value is
null, to match the previous output and
to produce valid HTML code.
TYPO3 Core ships with the following tag-based ViewHelpers:
<f:media> and
<f:image>
<f:asset.css> and
<f:asset.script>
<f:form> and
<f:form.*>
<f:link.*>, except for <f:link.typolink>, which uses TypoScript internally
<f:be.link>
<be:link.*>
<be:thumbnail>
Type declarations in ViewHelper classes
Fluid's ViewHelperInterface now requires proper return types for all ViewHelper
methods. Thus, custom ViewHelper implementations need to be adjusted accordingly.
This is backwards-compatible to previous TYPO3 versions.
Affected installations
All installations need to verify that
ViewHelpers aren't called with invalid argument types
null values passed to tag-based ViewHelpers don't lead to unexpected
HTML output
Custom ViewHelper implementations need to make sure that they declare
proper return types in the ViewHelper class to conform to Fluid 5's
interface changes, for example:
initializeArguments() must specify void as return type
render() should specify a return type other than void. Even though a
specific type is recommended, it is not required, and mixed can be used as well.
Note that properties in ViewHelper classes are not affected.
The following example doesn't need to be adjusted, no types can/should
be specified for these properties:
Unfortunately, the other changes concern runtime characteristics of
Fluid templates, as they depend on the concrete values of variables that
are passed to a template. Thus, it is not possible to scan for affected
templates automatically.
However, the majority of issues these changes in Fluid might uncover in
existing projects would have already been classified as a bug
(in the template or extension code) before this Fluid change, such as
passing an array to a ViewHelper that expects a string.
Breaking: #108277 - Remove superfluous CacheHashCalculator public methods
Calling the removed methods will result in a fatal PHP error.
Both methods were only used internally for testing purposes and were not part
of the public API contract.
Affected installations
TYPO3 installations that used these methods directly in custom code are
affected. However, this is highly unlikely as they were intended for internal
testing only.
Migration
Instead of modifying configuration after instantiation using the removed
setConfiguration() method, merge configuration arrays before creating
the
CacheHashConfiguration instance, then pass it to the
CacheHashCalculator constructor.
Example:
// Before (removed approach):
$subject = new CacheHashCalculator(
new CacheHashConfiguration($baseConfiguration),
$hashService
);
$subject->setConfiguration([
'cachedParametersWhiteList' => ['whitep1', 'whitep2'],
]);
// After (correct approach):
$configuration = new CacheHashConfiguration(
array_merge($baseConfiguration, [
'cachedParametersWhiteList' => ['whitep1', 'whitep2'],
])
);
$subject = new CacheHashCalculator($configuration, $hashService);
Copied!
Breaking: #108304 - Populate extension title from composer.json
To avoid reading the legacy ext_emconf.php file even in
Composer mode, the extension title is now optionally pulled
from the composer.json description. If the character sequence
- (space, dash, space) is present in description field
in composer.json, then everything before this sequence
is used as title of the extension and the second part
is used as extension description. Note that only the first
occurrence of this sequence is evaluated, so it remains
possible to have this inside the extension description if required.
Impact
Extensions not having their title incorporated in the
composer.json description field, will be shown with the
full description in extension manager and from command
line with typo3 extension:list command.
Affected installations
Installations having custom extensions, where the title
is not part of the description in composer.json
Migration
Put the desired extension title into the composer.json description field
and separate it from the description with - (space, dash, space),
or use the extension manager "Composer Support of Extensions" to get
a suggestion for updating composer.json files accordingly.
All TYPO3 core extensions have set their description in composer.json
accordingly already.
Example of description with title in composer.json:
Extension detection in classic mode now requires a valid composer.json
file instead of ext_emconf.php. The composer.json file must
include "type": "typo3-cms-*" and the extension key in
extra.typo3/cms.extension-key.
Impact
Extensions without a valid composer.json are no longer detected
and loaded in classic mode installations.
Affected installations
All classic mode installations must verify that every extension contains
a composer.json with:
"type" starting with "typo3-cms-"
"extra.typo3/cms.extension-key" containing the extension key
Composer-based installations are not affected.
Migration
Extension authors must ensure their extensions include a valid
composer.json. TER extensions have required this since 2021.
The former page type "External link" has been renamed to "Link" and now fully
supports all typolink capabilities.
It can be used, for example, to configure pages linking to:
external URLs
other pages
content elements or sections
files and folders
email addresses
telephone numbers
custom records, for example news records
Such pages can be displayed in menus and linked from the link wizard of type Page.
The upgrade wizard "Migrate links of pages of type link." automatically migrates
pages of the former type "External URL".
The page type "Shortcut" remains unchanged.
Impact
Editors and integrators can now use the updated page type "Link" to create
menus that contain any link type supported by typolink - including section
links for anchor-based navigation, email addresses, telephone numbers, or
custom record links (for example news records).
Feature: #92760 - Configurable timezone for DateViewHelper
Translation domains have been introduced as a shorter alternative to
file-based references for label resources (.xlf XLIFF files). The
syntax uses the format package[.subdomain...].resource and is fully backward
compatible with existing LLL:EXT: references. Package refers to the
extension key, such as "backend" for "EXT:backend".
This syntax is designed to improve readability, remove explicit references to
file extensions, and provide convenience for new developers and integrators.
The previous locallang.xlf convention has been replaced with a more
generic "messages" resource name, following common conventions in other
localization systems (for example Symfony). This is also where the term
translation domain originates.
The existing syntax and naming
(LLL:EXT:extension/Resources/Private/Language/locallang.xlf:label)
will remain available without deprecation for a long time.
Translation Domain Format
The format defines two parts: the package part (extension key) and the
resource part, separated by a dot.
The resource part omits historical namings such as locallang.xlf and the
locallang_ prefix. The actual label identifier is separated by a colon.
Format
Example usage of "package.resource:identifier"
$languageService->sL('backend.toolbar:save');
// Resolves to: EXT:backend/Resources/Private/Language/locallang_toolbar.xlf// and returns the translated "save" identifier.
Copied!
Domain Resolution
Deterministic File-Based Mapping
Translation domains are resolved deterministically by scanning the file
system. When a domain is first requested for a package:
All label files in Resources/Private/Language/ are discovered.
A domain name is generated from each file name.
The domain-to-file mapping is cached in cache.l10n.
Subsequent requests use the cached mapping.
This ensures that domain names always correspond to existing files and avoids
speculative file system lookups.
When there are filename conflicts such as locallang_db.xlf and
db.xlf, then locallang_db.xlf will be ignored.
Performance Characteristics
The implementation reduces file system operations compared to traditional
file-based lookups, as all label files within an extension are discovered once.
Domain Generation Rules
Domain names are generated from file paths using these transformation rules:
The base path Resources/Private/Language/ is omitted.
The domain name messages is currently evaluated for both the
legacy file name locallang.xlf but also for new files messages.xlf.
If a file messages.xlf is present, this means the locallang.xlf
will never be automatically evaluated for the resulting messages domain.
It is recommended to avoid having both files in the same directory, unless
both contain the same label contents, as no merging of these two
files is performed.
Usage
The translation domain system integrates with the existing
\TYPO3\CMS\Core\Localization\LanguageService API. Both domain-based and
file-based references are supported:
Domain-based references are shorter and reveal less implementation detail than
full file paths.
CLI Command
The development command
bin/typo3 language:domain:list lists all available
translation domains along with their available translations and label counts:
# List domains in active extensions
php bin/typo3 language:domain:list
# Filter by extension
php bin/typo3 language:domain:list --extension=backend
The Labels column displays the number of translatable labels within the
English source file.
On top of this, the development command
bin/typo3 language:domain:search
can be used to search for specific label contents. Both commands are provided
in the EXT:lowlevel extension.
PSR-14 Event
The event
\TYPO3\CMS\Core\Localization\Event\BeforeLabelResourceResolvedEvent
is dispatched after domain generation, allowing customization of domain names.
The event provides these public properties:
$packageKey — The extension key (read-only).
$domains — An associative array mapping domain names to label files
(modifiable):
array<string, string>.
Example
Event listener implementation:
namespaceMyVendor\MyExtension\EventListener;
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Core\Localization\Event\BeforeLabelResourceResolvedEvent;
final readonly classCustomTranslationDomainResolver{
#[AsEventListener(identifier: 'my-extension/custom-domain-names')]publicfunction__invoke(BeforeLabelResourceResolvedEvent $event): void{
if ($event->packageKey !== 'my_extension') {
return;
}
// Use file my_messages.xlf even if locallang.xlf is found
$event->domains['my_extension.messages'] =
'EXT:my_extension/Resources/Private/Language/my_messages.xlf';
}
}
Copied!
Backend modules
Previously, backend module labels (including their title and description) were
defined in a file like this:
The naming for the short-hand translation domain for modules should follow
the following pattern as best practice:
<extensionkey>.modules.<modulename> - when multiple modules exist for an extension.
Both extensionKey and modulename should use lower snake case ("some_long_module_name"),
ideally without underscores (qrcode.modules.generator is more readable than
qrcode.modules.backend_image_generator for example). Files are put into
EXT:extensionkey/Resources/Private/Languages/Modules/modulename.xlf.
<extensionkey>.module - single backend module only
The file is saved as EXT:extensionkey/Resources/Private/Languages/module.xlf.
To summarize, the key changes are:
Use a speaking XLIFF file inside /Resources/Private/Languages/Modules (best practice, could be any sub-directory)
Use understandable XLIFF identifiers:
- "title" instead of "mlang_tabs_tab"
- "short_description" instead of "mlang_labels_tablabel"
- "description" instead of "mlang_labels_tabdescr"
Use short-form identifiers ("my_extension.modules.my_module" instead of "LLL:EXT:my_extension/Resources/Private/Language/locallang_mod.xlf")
inside the Backend/Modules.php registration.
All TYPO3 Core backend modules that used the old label identifiers have been migrated to the new syntax, the utilized
files are now deprecated, see deprecation. TYPO3 Core also uses
singular module language containers like workspaces.module instead of workspaces.modules.workspaces.
Impact
Translation domains provide a shorter, more readable alternative to file-based
label references. The implementation uses deterministic file-system scanning
with per-package caching to reduce lookups.
All existing LLL:EXT: file references continue to work. Translation domains
are optional and can be adopted incrementally. Both syntaxes can coexist in
the same codebase. This affects TypoScript, Fluid
<f:translate> usages, TCA configuration, and PHP code using the
LanguageService API.
TYPO3 Core will gradually migrate internal references to translation domains
over time, increasing readability—especially in Fluid templates or TCA
definitions.
Image processing in TYPO3 can now configure default and specific formats to
use when images are rendered or converted in the frontend, for example in
Fluid:
Depending on the TYPO3 version, processed images were rendered with
.png (or earlier, .gif or .png) file extensions, as
long as the fileExtension parameter with a fixed output format was not
specified.
This default solution had two major drawbacks:
The default file format was hardcoded and not configurable.
Utilizing new file formats (like webp and avif) required code changes.
This has now been changed with the new configuration option
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imageFileConversionFormats'] .
When resizing or cropping an image with a file extension of jpg, jpeg,
gif, png, or svg (and not setting a specific
fileExtension target format), those images will retain their
respective file formats.
Otherwise, the file format png is used.
Related configuration options that still apply:
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
Current default:
gif,jpg,jpeg,tif,tiff,bmp,pcx,tga,png,pdf,ai,svg,webp,avif
$GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext']
Current default:
txt,ts,typoscript,html,htm,css,tmpl,js,sql,xml,csv,xlf,yaml,yml
$GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext']
Current default:
gif,jpg,jpeg,bmp,png,webp,pdf,svg,ai,mp3,wav,mp4,ogg,flac,opus,webm,youtube,vimeo,avif
These still define, per installation, which files can be uploaded or used as
images.
If a new format like heic becomes supported by the used graphics
engine (for example GraphicsMagick or ImageMagick), system maintainers can add
the file extension to
imagefile_ext. TYPO3 will then recognize this
format and convert it to the default target format (png by default) when
processing images.
If the format should also be available as a target format, add
'heic' => 'heic' to
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imageFileConversionFormats'] .
Otherwise, heic images can be selected, but processing will still produce
png.
If all image processing (resizing, thumbnails, PDF previews, etc.) should use
heic, set
'default' => 'heic' and remove other entries.
Currently, the option cannot be configured via
System > Settings > Configure options ... because it uses
an array syntax. It can be changed manually in settings.php. GUI
support may be added in a future release.
The array notation allows defining a target (thumbnail) file extension for
each original file extension individually using the format
{originalExtension} => {targetExtension}.
This configuration would produce the following behavior:
Fluid image rendering results
<f:imagesrc="somefile.jpg"width="80">
-> renders an 80 px thumbnail from "somefile.jpg" to "somefile.jpg"
(rule: "jpg => jpg")
<f:imagesrc="somefile.gif"width="80">
-> renders an 80 px thumbnail from "somefile.gif" to "somefile.avif"
(rule: "default => avif")
<f:imagesrc="somefile.png"width="80">
-> renders an 80 px thumbnail from "somefile.png" to "somefile.avif"
(rule: "default => avif")
<f:imagesrc="somefile.svg"width="80">
-> renders the original SVG at 80 px width
(rule: "svg => svg")
<f:imagesrc="somefile.pdf"width="80">
-> renders an 80 px PDF thumbnail to "somefile.avif"
(rule: "default => avif")
<f:imagesrc="somefile.heic"width="80">
-> renders an 80 px thumbnail from "somefile.heic" to "somefile.webp"
(rule: "heic => webp")
Copied!
Impact
TYPO3 is now more future-proof regarding new image formats and allows modern
file formats to be used by configuration.
Projects can now specify precisely how each format should be converted or
which default format should be used for processed images.
Feature: #97559 - Support property-based configuration for Extbase attributes
PHP attributes in the Extbase context can now be configured using properties
instead of an array of configuration values. This resolves a limitation that
existed since the introduction of Extbase annotations in TYPO3 v9, where
annotation configuration was restricted: all available options needed to be
defined in a single array.
Since annotations were removed with forge#107229 in favor of PHP attributes,
configuration options can now be defined in a more flexible and type-safe way.
This patch serves as a follow-up to forge#107229 and aims to improve the
attribute configuration mechanism by using constructor property promotion in
combination with strictly typed properties.
To maintain backwards compatibility, the first property of each attribute still
accepts an array of configuration options. However, this behavior is deprecated
and will be removed in TYPO3 v15.0 (see
deprecation notice).
Developers are advised to migrate to single-property configuration when using
PHP attributes in Extbase.
Feature: #98239 - PSR-14 Event to modify form after being built
A new PSR-14 event
\TYPO3\CMS\Form\Event\AfterFormIsBuiltEvent
has been introduced which serves as an improved replacement for the
now removed hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterBuildingFinished'] .
The event provides the
$form public property.
Example
An example event listener could look like the following:
The Administration > Users module has been extended with a new detail view
for backend user groups, complementing the existing detail view for individual
backend users. This comprehensive view provides administrators with complete
visibility into backend user group configurations and their calculated
properties.
The detail view displays:
Basic information about the backend user group (title, description, and so on)
All assigned subgroups and the full inheritance chain
A complete overview of permissions and access rights from the group and
all inherited subgroups
Calculated and processed TSconfig settings showing the final effective
configuration
Database mount points and file mount access permissions
Module access permissions and workspace restrictions
TYPO3 administrators can now efficiently analyze backend user group
configurations without manually tracing through complex inheritance structures.
This enhanced visibility simplifies permission troubleshooting, security
auditing, and group management by providing a consolidated view of all
calculated permissions and settings in one place, similar to the existing
backend user detail functionality.
Feature: #99409 - New PSR-14 BeforeLiveSearchFormIsBuiltEvent
A new PSR-14 event
\TYPO3\CMS\Backend\Search\Event\BeforeLiveSearchFormIsBuiltEvent
has been added.
To modify the live search form data, the following methods are available:
addHint(): Adds a single hint.
addHints(): Adds one or multiple hints.
setHints(): Sets hints and can be used to reset or overwrite the
current ones.
getHints(): Returns all current hints.
getRequest(): Returns the current PSR-7 request.
getSearchDemand(): Returns the
SearchDemand used by the live search.
setSearchDemand(): Sets a custom
SearchDemand object.
getAdditionalViewData(): Returns the additional view data set to be
used in the template.
setAdditionalViewData(): Sets the additional view data to be used in
the template.
Note
The method
setAdditionalViewData() is useful to provide additional
data to the template without the need to cross-class (XCLASS) the controller.
The additional view data can then be used in an overridden backend template
of the live search form.
The "Create new record" component in the backend, which is accessible in the
Content > List module, has been enhanced to automatically detect and
display all available record types for tables that support sub-schemas
(record types). This improvement simplifies creating specific record types and
eliminates the need to change the type afterward in the editing form, which
could previously lead to invalid record states being stored in the database.
The interface now features automatic record type detection. Tables with
multiple record types are automatically expanded to show all available types
in a collapsible dropdown interface.
The options to create new pages now also show the different page types
(
doktype) as expandable options when creating new pages "inside" or
"after" an existing page.
To disable direct creation of a specific record type, a new TCA option
['creationOptions']['enableDirectRecordTypeCreation'] is available at
the record type level:
useTYPO3\CMS\Core\Domain\Repository\PageRepository´;
// Disable direct creation of shortcuts
$GLOBALS['TCA']['pages']['types'][(string)PageRepository::DOKTYPE_SHORTCUT]['creationOptions']['enableDirectRecordTypeCreation'] = false;
Copied!
Individual titles for each record type are automatically picked up from the
type-specific
title configuration in the types section (see
Feature #108027),
falling back to the select item label if no type-specific title is defined.
Additionally, the new PSR-14 event
\TYPO3\CMS\Backend\Controller\Event\ModifyNewRecordCreationLinksEvent
allows for complete customization of the creation links.
Impact
For tables that support sub-schemas (multiple record types), the new record
wizard automatically detects all available types and displays them in a
collapsible interface. This includes:
All tables with TCA type fields (such as
sys_file_collection
or
index_config)
The
pages table with its different
doktype values
Extension tables with custom record types
Note
The collapsible interface keeps the view clear while providing access
to all options. Icons and labels are automatically generated for each
record type based on the TCA configuration.
The
ModifyNewRecordCreationLinksEvent provides complete control over
the creation link structure, allowing extensions to:
Add custom record creation options
Modify existing groups and items
Override icons, labels, and URLs
Create entirely custom wizard interfaces
Data Structure
The event works with a nested array structure representing grouped creation
links:
A new TCA field type called country has been added to TYPO3 Core. Its
main purpose is to use the newly introduced
Country API to
provide a country selection in the backend and use the stored representation in
Extbase or TypoScript output.
TCA Configuration
The new TCA type displays all filtered countries including the configurable
name and the corresponding flag.
Configuration/TCA/tx_myextension_mymodel.php
'country' => [
'label' => 'Country',
'config' => [
'type' => 'country',
// available options: name, localizedName, officialName, localizedOfficialName, iso2, iso3'labelField' => 'localizedName',
// countries which are listed before all others'prioritizedCountries' => ['AT', 'CH'],
// sort by the label'sortItems' => [
'label' => 'asc'
],
'filter' => [
// restrict to the given country ISO2 or ISO3 codes'onlyCountries' => ['DE', 'AT', 'CH', 'FR', 'IT', 'HU', 'US', 'GR', 'ES'],
// exclude by the given country ISO2 or ISO3 codes'excludeCountries' => ['DE', 'ES'],
],
'default' => 'HU',
// When required=false, an empty selection ('') is possible'required' => false,
],
],
required (bool) - whether an empty selection can be made or not
Extbase usage
When using Extbase Controllers to fetch Domain Models containing properties
declared with the
Country type, these models can be used with their
usual getters, and passed along to Fluid templates as usual.
usePsr\Http\Message\ResponseInterface;
useTYPO3\CMS\Core\Country\Country;
useTYPO3\CMS\Core\Country\CountryProvider;
useTYPO3\CMS\Extbase\Mvc\Controller\ActionController;
classItemControllerextendsActionController{
// ...publicfunction__construct(
private readonly CountryProvider $countryProvider,
){}
publicfunctionsingleAction(SomeDomainModel $model): ResponseInterface{
// Do something in PHP, using the Country APIif ($model->getCountry()->getAlpha2IsoCode() == 'DE') {
$this->loadGermanLanguage();
}
$this->view->assign('model', $model);
// You can access the `CountryProvider` API for additional country-related// operations, too (ideally use Dependency Injection for this):$this->view->assign('countries', $this->countryProvider->getAll());
return$this->htmlResponse();
}
}
You can use any of the
getXXX() methods available from the
Country API via
the Fluid
{model.country.XXX} accessors.
If you use common Extbase CRUD (Create/Read/Update/Delete) with models using a
Country type, you can utilize the existing ViewHelper
f:form.countrySelect within your
<f:form> logic.
Please keep in mind that Extbase by default has no coupling (in terms of
validation) to definitions made in the TCA for the properties, as with other
types like file uploads or select items.
That means, if you restrict the allowed countries via filter.onlyCountries on
the backend (TCA) side, you also need to enforce this in the frontend.
It is recommended to use
Extbase Validators
for this task. If you want to share frontend-based validation and TCA-based
validation non-redundantly, you could use data objects (DO/DTO) or ENUMs for
returning the list of allowed countries:
The type
Country does not point to a real
Extbase model, and thus has no inherent localization or query logic based on
real records. It is just a pure PHP data object with some getters, and a magic
__toString() method returning a language label (LLL:...) translation
key for the name of the country (
Country->getLocalizedNameLabel()).
Here are some examples how to access them and provide localization:
<f:comment>Will show something like "AT" or "DE"</f:comment>
Country ISO2:
{item.country.alpha2IsoCode}
<f:comment>Will show something like "CHE"</f:comment>
Country ISO3:
{item.country.alpha3IsoCode}
<f:comment>Will show something a flag (UTF-8 character)</f:comment>
Country flag:
{item.country.flag}
<f:comment>Will show something like "LLL:EXT:core/Resources/Private/Language/Iso/countries.xlf:AT.name"</f:comment>
Country LLL label:
{item.country}
Actual localized country:
<f:translatekey="{item.country}" /><f:comment>Will show something like "LLL:EXT:core/Resources/Private/Language/Iso/countries.xlf:AT.official_name"</f:comment>
Country LLL label:
{item.country.localizedOfficialNameLabel}
Actual localized official country name:
<f:translatekey="{item.country.localizedOfficialNameLabel}" /><f:comment>Will show something like "Germany" (always English)</f:comment>
{item.country.name}
Copied!
You can use the Extbase
\TYPO3\CMS\Extbase\Utility\LocalizationUtility in
PHP scope (Controllers, Domain Model) to create a custom getter in your Domain
Model to create a shorthand method:
As mentioned above, since
Country has no
database-record relations. The single-country relation always uses the 2-letter
ISO alpha2 key (respectively custom country keys, when added via the PSR-14 event
BeforeCountriesEvaluatedEvent ). Thus, queries need to utilize them as string
comparisons:
The default Extbase repository magic method
$repository->findBy(['country' => 'DE']) will work, too.
TypoScript rendering usage via record-transformation
Database records using country type fields can be rendered with the
TypoScript-based record-transformation rendering (data processor).
You can specify how a field containing a country is rendered in the output
(using the name, the flag icon, specific ISO keys) with regular Fluid logic
then:
Step 1: TypoScript utilizing record-transformation, defining a Homepage.html Fluid template
page = PAGE
page {
# Just an example basic template for your site. The important section starts with `dataProcessing`!100 = FLUIDTEMPLATE100 {
templateName = Homepage
templateRootPaths {
0 = EXT:myextension/Resources/Private/Templates/
}
dataProcessing {
10 = database-query
10 {
as = mainContent
# This table holds for example a TCA type=country definition for a field "country"
table = tx_myextension_domain_model_mycountries
# An extra boolean field "show_on_home_page" would indicate whether these# records are fetched and displayed on the home page
where = show_on_home_page=1
# Depending on your table storage you may need to set a proper pidInList constraint.#pidInList = 4711
dataProcessing {
# Makes all records available as `{mainContent.[0..].myRecord}` in the# Fluid file EXT:myextension/Resources/Private/Templates/Homepage.html10 = record-transformation
10 {
as = myRecord
}
}
}
}
}
}
<f:ifcondition="{mainContent}"><f:foreach="{mainContent}"as="element"><!-- given that your 'tx_myextension_domain_model_mycountries' has a TCA field called "storeCountry":
Selected Country:
<f:translate key="{element.myRecord.storeCountry.localizedOfficialNameLabel}" />
</f:for>
<!-- note that you can access any transformed record type object via 'element', also multiple country
elements could be contained in 'element.myRecord'. --></f:if>
Copied!
Hint
Instead of adding the data processor to the
PAGE definition, you could
create your own country Content Element type and set it for
tt_content.country, and utilize a Content-Element specific Fluid template
accessing this data, providing something like a "Store" Content Element
associated with a country.
Impact
It is now possible to use a dedicated TCA type for storing a relation to a
country in a record.
Using the new TCA type, corresponding database columns are added automatically.
Country-annotated properties of Extbase Domain Models can be evaluated in
Extbase and via TypoScript.
Feature: #101059 - Allow install tool sessions without shared file system
The default file-based session handling for the Install Tool remains unchanged.
If no alternative session handler for the Install Tool is configured,
the default behavior is used.
Custom session handlers can be created by implementing PHP's
\SessionHandlerInterface.
Feature: #103258 - Language filter for list module
The list module now provides a language filter in the document header, similar
to the existing language selector in the page module. This allows backend users
to filter records by language, making it easier to focus on content in a
specific language when working with multilingual websites.
The language filter appears as a dropdown button in the document header toolbar
and provides the following options:
All available site languages that have page translations
"All languages" (if translations exist on the current page)
Key behaviors
Language selection persistence
The selected language is stored in the backend user's module data and persists
across page navigation. When switching between pages, the previously selected
language remains active if available on the new page.
Automatic fallback
If navigating to a page where the selected language is not available (no
translation exists), the list module automatically falls back to:
"All languages" mode (if the page has any translations)
The default language (if the page has no translations at all)
This fallback is temporary and does not overwrite the language preference.
When navigating to a page with the selected language translation, it will
automatically be restored.
Display behavior
When a specific language is selected:
Records in the selected language are displayed
Default language records (language 0) are always included as fallback
Records with the "all languages" flag (-1) are included
Example: If French is selected, one will see French translations, default
language content, and content marked for "all languages".
Localization restrictions
The localization panel respects page translation availability. When a language
is selected or when viewing in "all languages" mode, the list module only
offers localization options for languages where the page has an existing
translation.
This ensures data integrity by preventing the creation of records in
languages where the parent page does not exist.
Impact
Backend users can now efficiently filter list module records by language,
improving the workflow when managing multilingual content.
The language selection persists across page navigation, reducing the need to
repeatedly select the same language. The intelligent fallback mechanism ensures
the list module always displays relevant content, even when switching between
pages with different translation availability.
This enhancement brings the list module's language handling in line with that
of the page module, providing a consistent user experience across TYPO3's
backend.
Feature: #103740 - Language selection for backend module "Status - Pagetree Overview"
This change makes it possible to switch the displayed page tree to the selected
language and adjust all labels, as well as edit and view links, accordingly.
The language selection dropdown is located next to the other filter options
(recursion depth, information type) and complements the
Content > Status > Localization Overview module by providing a
page- and record-focused view.
Impact
The Content > Status backend module is now more useful for sites with
multiple languages, offering a quick overview of information for the selected
page and its subpages in the chosen language.
Feature: #104047 - Option to report redirects in link validator
A new Page TSconfig option
mod.linkvalidator.linktypesConfig.external.allowRedirects
has been introduced to the link validator. It allows redirects
(HTTP 3xx responses) to be reported as problems when validating external links.
A new command cache:flushtags has been introduced to allow flushing cache
entries by tag.
Multiple tags can be flushed by passing a comma-separated list of tags.
It is also possible to flush tags for a specific cache group by using the
--groups or -g option. If no group is specified, all cache groups
are considered.
Note that certain combinations of groups and tags do not make sense,
specifically the di and system cache groups.
The Install Tool password can now also be set via the TYPO3 command line interface
instead of only via direct file access. This allows for better automation and
easier hash generation without manual steps.
Usage
Interactively create and write an Install Tool password hash:
vendor/bin/typo3 install:password:set
Copied!
Return the generated password hash without writing to the configuration:
vendor/bin/typo3 install:password:set --dry-run
Copied!
Run without interaction (generates a random password):
The last variation can, for example, be used in CI/CD automation to
generate a suitable password, process the output (the generated password
needs to be persisted separately), and store it in vaults or environment
variables for later use.
Impact
It is now possible to set the hashed Install Tool password using the
TYPO3 command line interface.
Feature: #105549 - Support qualified and unqualified ISO8601 dates in DataHandler
The
DataHandler API has been extended
to support both qualified and unqualified ISO 8601 date formats, correctly
handling supplied timezone offsets when provided.
Qualified ISO 8601
Includes an explicit timezone offset (for example,
1999-12-11T10:09:00+01:00 or 1999-12-11T10:09:00Z)
Unqualified ISO 8601
Omits timezone offsets, representing LOCALTIME (for example,
1999-12-11T10:09:00)
The
DataHandler now accepts five
different formats:
Format
Examples
Unqualified ISO 8601
(LOCALTIME)
'Y-m-d\\TH:i:s'
1999-11-11T11:11:11
Qualified ISO 8601
'Y-m-d\\TH:i:sP'
1999-11-11T10:11:11Z
1999-11-11T11:11:11+01:00
DateTime objects
\DateTimeInterface
new \DateTime('yesterday')
new \DateTimeImmutable()
SQL-flavored dates
(internal use)
'Y-m-d H:i:s'
1999-11-11 11:11:11
Unix timestamps
(internal use)
'U'
942315071
The ISO 8601 variants and
\DateTimeInterface objects are intended for use
in the public API. The SQL-flavored variant and Unix timestamps are primarily
intended for internal operations such as copy or import processes involving
native
DATETIME and
INT timestamp database fields.
Passing datetime data via the DataHandler PHP API
$myDate = new \DateTime('yesterday');
$this->dataHandler->start([
'tx_myextension_mytable' => [
'NEW-1' => [
'pid' => 2,
// Format as LOCALTIME'mydatefield_1' => $myDate->format('Y-m-d\\TH:i:s'),
// Format with timezone information// (offsets will be normalized to the persistence timezone format,// UTC for integer fields, LOCALTIME for native DATETIME fields)'mydatefield_2' => $myDate->format('c'),
// Pass \DateTimeInterface objects directly'mydatefield_3' => $myDate,
],
],
]);
Copied!
Impact
TYPO3 now provides accurate and consistent handling of ISO 8601 dates,
eliminating previous issues related to timezone interpretation and LOCALTIME
representation.
Feature: #105624 - PSR-14 event after a backend user password has been reset
A new PSR-14 event
\TYPO3\CMS\Backend\Authentication\Event\PasswordHasBeenResetEvent
has been introduced. It is dispatched right after a backend user has reset their password
and it has been hashed and persisted to the database.
The event contains the corresponding backend user UID.
Example
The corresponding event listener class:
<?phpnamespaceVendor\MyPackage\Backend\EventListener;
useTYPO3\CMS\Backend\Authentication\Event\PasswordHasBeenResetEvent;
useTYPO3\CMS\Core\Attribute\AsEventListener;
finalclassPasswordHasBeenResetEventListener{
#[AsEventListener('my-package/backend/password-has-been-reset')]publicfunction__invoke(PasswordHasBeenResetEvent $event): void{
$userId = $event->userId;
// Add custom logic for the backend user UID
}
}
Copied!
Impact
It is now possible to add custom business logic after a backend user has reset
their password using the new PSR-14 event
PasswordHasBeenResetEvent .
TYPO3 now notifies backend users by email when a failed MFA (multi-factor
authentication) verification attempt occurs. The notification is sent only if
an MFA provider is configured and the user has a valid email address in their
profile.
Impact
TYPO3 backend users now benefit from improved security awareness through
immediate email notifications about failed MFA verification attempts. This
feature is particularly useful in cases where backend accounts with active MFA
configuration are targeted by unauthorized access attempts.
Feature: #105833 - Extended page tree filter functionality
The page tree is one of the central components in the TYPO3 backend,
particularly for editors. However, in large installations, the page tree can
quickly become overwhelming and difficult to navigate. To maintain a clear
overview, the page tree can be filtered using basic terms, such as the page
title or ID.
To enhance the filtering capabilities, the new PSR-14 event
\TYPO3\CMS\Backend\Tree\Repository\BeforePageTreeIsFilteredEvent
has been introduced. This event allows developers to extend the filter
functionality and process the given search phrase in more advanced ways.
Using this event, it is for example possible to evaluate a given URL or to
add additional field matchings, such as filtering pages by their
doktype or their configured backend layout.
The event provides the following public properties:
$searchParts:
The search parts to be used for filtering
$searchUids:
The uids to be used for filtering by a special search part, which
is added by Core always after listener evaluation
$searchPhrase
The complete search phrase, as entered by the user
$queryBuilder:
The current
QueryBuilder
instance to provide context and to be used to create search parts
Important
The
QueryBuilder instance is
provided solely for context and to simplify the creation of search parts
by using the
ExpressionBuilder
via
$queryBuilder->expr(). The instance itself must not be
modified by event listeners and is not considered part of the public API.
TYPO3 reserves the right to change the instance at any time without prior
notice.
Example
The following event listener class demonstrates how to add additional
conditions to the page tree filter using the PHP attribute
#[AsEventListener] for registration.
useTYPO3\CMS\Backend\Tree\Repository\BeforePageTreeIsFilteredEvent;
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Core\Database\Connection;
finalclassMyEventListener{
#[AsEventListener]publicfunctionremoveFetchedPageContent(BeforePageTreeIsFilteredEvent $event): void{
// Add an additional UID to the filter
$event->searchUids[] = 123;
// Add evaluation of doktypes to the filterif (preg_match('/doktype:([0-9]+)/i', $event->searchPhrase, $match)) {
$doktype = $match[1];
$event->searchParts = $event->searchParts->with(
$event->queryBuilder->expr()->eq(
'doktype',
$event->queryBuilder->createNamedParameter($doktype, Connection::PARAM_INT)
)
);
}
}
}
Copied!
Impact
With the new PSR-14 event
BeforePageTreeIsFilteredEvent ,
custom functionality and advanced evaluations can now be added to enhance the
page tree filter.
Feature: #106072 - Introduce regex-based replacements for slugs
A second replacement configuration array has been added to support
regular expression (regex)-based definitions. This allows defining
case-insensitive or wildcard replacements for slug generation.
Note
Regular expression replacements are applied before any sanitation
of the field values takes place. This means:
Values are still in their original form - not lowercased or otherwise processed.
The replacements apply only to the fields defined in
['generatorOptions']['fields'].
No automatic escaping or character detection is performed, so
patterns must be written carefully.
Keep these points in mind when defining regex patterns.
Impact
Slug fields now support a new regexReplacements configuration array
inside generatorOptions.
Example TCA configuration
$GLOBALS['TCA'][$table]['columns']['slug']['config']['generatorOptions']['regexReplacements'] => [
// Case-insensitive replacement of Foo, foo, FOO, etc. with "bar"'/foo/i' => 'bar',
// Remove string wrapped in parentheses'/\(.*\)/' => '',
// Same, using a custom regex delimiter'@\(.*\)@' => '',
];
Copied!
Feature: #106074 - Show editor information in workspace "Publish" module
The "Last changed" column in the Content > Publish module has
been improved. It now displays the username and avatar of the editor who most
recently modified the corresponding record.
The column shows the editor's username in a badge, or Unknown if no editor
information is available. This helps reviewers quickly identify who last worked
on each workspace record.
Impact
Workspace information now reveals the last editor of a record within the
workspace context.
Feature: #106092 - Associative array keys for TCA valuePicker items
It is now possible to define associative array keys for the
items
configuration of the TCA type
valuePicker.
The new keys are called
label and
value.
This follows the change made previously to the
items configuration
of the TCA types
select,
radio, and
check.
See forge#99739.
Impact
The TCA
items configuration can now be defined in a more consistent
and readable way using associative array keys. This eliminates ambiguity
about whether the label or value comes first.
Optional keys such as
icon,
group, or
description can be
used as needed.
Feature: #106232 - Provide record title tag provider
The class
\TYPO3\CMS\Core\PageTitle\RecordTitleProvider
introduces a new page title provider with the identifier recordTitle.
It is executed before the
\SeoTitlePageTitleProvider,
which uses the TypoScript identifier seo.
This provider can be used by third-party extensions to set the page title
programmatically.
A new PSR-14 event
\TYPO3\CMS\Redirects\Event\AfterPageUrlsForSiteForRedirectIntegrityHaveBeenCollectedEvent
has been introduced. It allows extensions to register event listeners to modify
the list of URLs processed by the CLI command
redirects:checkintegrity.
Example
The following example shows an event listener that uses the PHP attribute
#[AsEventListener] to register itself and adds the URLs found in a site's
XML sitemap to the list of URLs checked by the redirects integrity command.
All implementations of
TypolinkBuilderInterface are
automatically configured as public services in the dependency injection
container, removing the need for manual service configuration.
TypolinkBuilder classes can now use proper dependency injection through
their constructors, improving testability and aligning with TYPO3's
architectural best practices.
The
\ServerRequestInterface is now passed
directly, providing access to the request context without relying on global
state.
The new interface introduces a cleaner separation of concerns
and more explicit parameter passing.
Example usage
Creating a custom TypolinkBuilder using the new interface:
The
FormManagerController now uses
the
YamlFileLoader when
creating new forms from templates. This change enables the processing of
placeholders within template files, such as environment variables in the
format
%env(ENV_NAME)%, as well as the import of other YAML files.
This enhancement allows for more flexible form templates that can adapt to
different environments through environment variable substitution and YAML
imports.
imports:-{resource:'User.yaml'}identifier:contactFormlabel:'Contact Form %env(ENV_NAME)%'type:Formrenderables:-type:Pageidentifier:messagelabel:Messagerenderables:-defaultValue:''type:Textidentifier:text-1label:Subject
Form templates can now contain environment variable placeholders using the
%env(ENV_NAME)% syntax and import other YAML files. These placeholders
and imports are automatically resolved when new forms are created from the
template.
Feature: #106510 - Add PSR-14 events to Extbase Backend::getObjectCountByQuery method
The class
\TYPO3\CMS\Extbase\Persistence\Generic\Backend is the central
entity for retrieving data from the database within the Extbase persistence
framework.
Since 2013, the
getObjectDataByQuery() method has supported events
(previously signals) to allow modification of data retrieval.
In many use cases, especially when used together with
\QueryResult, another key method is
involved:
getObjectCountByQuery(). This method is frequently used in
combination with Fluid templates.
Until now, extensions or other code using the existing events for data retrieval
could not ensure consistent modification of queries between data retrieval and
counting operations, resulting in mismatched query results.
The
getObjectCountByQuery() method has now been enhanced with new PSR-14
events, enabling extensions to modify all aspects of query processing within
Extbase's generic
Backend to achieve consistent results.
The new events are:
ModifyQueryBeforeFetchingObjectCountEvent
Allows modification of the query before it is passed to the storage backend.
ModifyResultAfterFetchingObjectCountEvent
Allows adjustment of the result after the query has been executed.
Typically, an extension should implement these events pairwise:
ModifyQueryBeforeFetchingObjectCountEvent together with
ModifyQueryBeforeFetchingObjectDataEvent
ModifyResultAfterFetchingObjectCountEvent together with
ModifyResultAfterFetchingObjectDataEvent
A new ARIA 1.2–compliant combobox web component has been introduced, replacing
the legacy value picker select pattern. The implementation follows the W3C
accessibility guidelines and provides complete keyboard navigation support.
FormEngine elements, including
EmailElement ,
InputTextElement , and
NumberElement , have been updated to
use the new combobox component instead of the previous value picker
implementation. The link browser components have also been adapted to use the
combobox pattern.
Impact
The new combobox component offers full keyboard navigation using the arrow keys,
Enter, Tab, and Escape.
It includes visual selection indicators with checkmarks and a clear button
for resetting the input value, improving accessibility and overall usability
in the TYPO3 backend.
Feature: #106686 - Enhance file browsers with column selector
The file browsers used in the file selector (for example, in file fields)
and the file link browser (for example, for RTE links) have been enhanced
to support the column selector component.
This allows backend users to customize which columns are displayed
(for example, "alternative", "timestamp", and so on), improving usability and
aligning the interface with the standard File List module.
This enhancement is particularly useful in large file storages, making it
easier to locate recently updated files or identify large files more quickly.
Impact
The file selector and link browser interfaces now include a column selector.
This improves consistency across TYPO3 backend modules and provides users with
greater control over the visibility of file metadata.
No additional configuration is required for this feature.
Whether the column selector is shown is still determined by the user TSconfig
option
options.file_list.displayColumnSelector.
Feature: #106739 - Scheduler tasks as native TCA table
For historical reasons, the TYPO3 system extension
typo3/cms-scheduler
used a special mechanism for persisting data to
the database. This was achieved by serializing the entire task object into a
single field of the database table
tx_scheduler_task.
In TYPO3 v14, this behavior has been reworked. The logic for custom fields has
been split into a dedicated database field
parameters of type
json
and a new database field
tasktype that stores the scheduler task name or
CLI command.
An upgrade wizard automatically migrates all existing scheduler tasks to the new
database structure.
Impact
With this change, TCA is now defined for the
tx_scheduler_task table in
TYPO3. This provides several advantages over the previous implementation:
The editing interface is now handled via FormEngine, making it more flexible
and extensible.
Changes are stored to the database via DataHandler, which allows
customization of persistence operations. The history and audit
functionality is now also available for scheduler tasks.
Database entries of
tx_scheduler_task can now be exported and
imported using the standard import/export functionality.
Deleted tasks can be restored via the recycler module.
Additional functionality such as support for automated database restrictions and
the TCA schema is available as well.
Feature: #106752 - Add password hashing option to SaveToDatabase finisher
A new option
hashed has been added to the
\SaveToDatabaseFinisher of the system
extension
typo3/cms-form
.
When saving form data to a database table, setting
hashed: true for a
field causes the value to be hashed using the default frontend password hashing
mechanism before it is written to the database.
This improves security by preventing passwords from being stored in plain text.
A new CLI command
vendor/bin/typo3 completion has been added to the
typo3 CLI dispatcher script.
This command enables shell auto-completion for supported shells, allowing
developers to use the Tab key to trigger command and option suggestions.
The
completion command is provided by the
symfony/console
package and is not a custom implementation.
This ensures compatibility with ongoing improvements in the Symfony ecosystem
and benefits from a broad user base and community support.
Supported shells
The command reports unsupported shells and lists available ones:
# bin/typo3 completion shell
Detected shell "shell", which is not supported by Symfony shell completion
(supported shells: "bash", "fish", "zsh").
Copied!
Installation modes
The command supports two installation modes — static and dynamic.
Run vendor/bin/typo3 completion --help to see detailed usage instructions and
the supported shells (bash, fish, zsh).
Static installation
Dump the completion script to a file and source it manually or install it
globally, for example:
vendor/bin/typo3 completion bash | sudo tee /etc/bash_completion.d/typo3
The
typo3 CLI dispatcher now supports shell auto-completion, improving
the user experience without affecting existing command usage.
This also lays the foundation for further enhancements such as improved
auto-completion for command options and arguments.
The following existing commands already provide completion for their arguments:
redirects:cleanup
redirects:checkintegrity
styleguide:generate
Example
The following example shows how to add auto-completion support to a custom
Symfony console command:
EXT:my_extension/Classes/Command/GreetCommand.php
<?phpdeclare(strict_types=1);
namespaceMyVendor\MyExtension\Command;
useSymfony\Component\Console\Attribute\AsCommand;
useSymfony\Component\Console\Command\Command;
useSymfony\Component\Console\Completion\CompletionInput;
useSymfony\Component\Console\Input\InputArgument;
#[AsCommand(
name: 'myextension:greet',
)]
classGreetCommandextendsCommand{
protectedfunctionconfigure(): void{
$this
->addArgument(
'names',
InputArgument::IS_ARRAY,
'Who do you want to greet (separate multiple names with a space)?',
null,
function(CompletionInput $input): array{
// Value already typed by the user, e.g. "myextension:greet Fa"// before pressing Tab — this will contain "Fa"
$currentValue = $input->getCompletionValue();
// Example of available usernames
$availableUsernames = ['jane', 'jon'];
return $availableUsernames;
}
);
}
}
A new dynamic category "Recently used" has been added to the record creation
wizards for the following components:
Content elements
Form elements
Dashboard widgets
This category lists the record or form types that a user has recently selected
in the respective wizard. It remains hidden until at least one record has been
created, keeping the interface uncluttered for new users.
This enhancement allows backend users to quickly access and reuse frequently
used record types, improving workflow efficiency and usability.
Impact
All record wizards now feature a "Recently used" section. This feature is
enabled by default and requires no additional configuration.
Users can manage the display of this category in their personal backend
settings.
This improvement streamlines access to frequently used records, enhancing the
overall editing experience for editors and integrators.
Example
When creating a new content element, users will now see a "Recently used"
section listing previously used content types, making them accessible with
a single click.
Feature: #106945 - Allow usage of Symfony validators in Extbase
Extbase models and controllers now support the use of
Symfony Validators.
Validators are based on Symfony Constraints, which can be added as attributes
to domain model properties and controller methods.
Once a constraint attribute is detected while reflecting properties or methods,
it is decorated by the new
ConstraintDecoratingValidator
class, which is compatible with Extbase's
\ValidatorInterface.
Decorated constraints may include localizable messages. If a message contains
valid LLL: syntax, the label will be translated automatically. The decorating
validator also handles message parameters by converting named parameters such as
{{ value }} into
sprintf-compatible placeholders like %1$s.
Important
Most available Symfony constraints, such as
#[NotBlank] and
#[Regex], can be used. However, more complex constraints such as
#[File] or
#[Image] are not yet compatible with the current
Extbase implementation, as they are closely tied to the Symfony framework.
Compatibility for those constraints may be added in the future.
A wide range of Symfony validators can now be used directly in Extbase.
This provides a more flexible and standardized validation workflow without the
need to implement custom validators, as Symfony already ships with a large
number of predefined constraints.
TYPO3 now automatically includes all fields of suitable types in
backend search operations, e.g., in the List module.
This eliminates the need for the previously used TCA ctrl option
searchFields, which has been removed.
Instead, a new per-field configuration option
searchable
has been introduced. It allows integrators to fine-tune whether
a specific field should be included in backend search queries.
By default, all fields of supported types are considered searchable.
To exclude a field from being searchable, set the following in the
field’s TCA configuration:
Note that until
searchFields is manually removed from your TCA, the
automatic TCA migration sets all suitable fields, which are not included
in the
searchFields configuration, to
searchable => false to
keep current behavior.
Supported Field Types
The following TCA field types support the
searchable option and are
automatically considered in searches unless explicitly excluded:
color
datetime (when not using a custom
dbType)
email
flex
input
json
link
slug
text
uuid
Unsupported field types such as
file,
inline,
password or
group are excluded from search and do not support the
searchable option.
Impact
Backend search becomes more consistent and automatic.
No need to manually maintain a
searchFields list in TCA.
Integrators have more granular control over search behavior on a field level.
Custom fields can easily be excluded from search using the
searchable option.
Migration
If your extension previously relied on the
searchFields TCA option,
remove it from the
ctrl section and instead define
'searchable' => false
on fields that should be excluded from search results.
No action is needed if the default behavior (search all suitable fields)
is acceptable.
Following forge#106934, which introduced the dynamic Recently used
category in record wizards, the component has been extended to
store the last selected category. When opening a record wizard,
for example, the wizard to create new content elements, it will now
automatically preselect the category that was last used.
This enhancement improves usability and consistency, especially in
installations with many categories, including those added by third-party
extensions.
Impact
Record wizards now automatically preselect the last used category when opened again.
No migration or configuration is required. The feature is enabled by default.
Dashboard widgets can now be configured on a per-instance level using the
Settings API. This allows widget authors to define settings that editors can
modify directly from the dashboard interface, making widgets more flexible and
adaptable to different use cases.
Typical configuration options include URLs for RSS feeds, limits on displayed
items, or categories for filtering content.
Each widget instance maintains its own configuration, enabling multiple
instances of the same widget type with different settings on the same or
different dashboards.
Impact
For editors
Dashboard widgets now display a settings (cog) icon when configuration is supported.
Clicking the icon opens a modal dialog with configurable options.
Settings are applied immediately after saving, and the widget refreshes automatically.
Each widget instance is configured independently per user.
For integrators
Dashboard widgets can now expose user-configurable options.
Settings are validated using the existing Settings API type system.
Includes an upgrade wizard to migrate existing widgets of this type to the new format.
Example
Widget authors can implement configurable widgets by migrating from the
current interface
WidgetInterface to
the new renderer interface
WidgetRendererInterface ,
which allows defining settings in the widget renderer:
The methods
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin()
and
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin() have been
extended to accept a FlexForm definition directly via an additional
$flexForm
argument.
This new argument allows extensions to provide the FlexForm data structure when
registering a plugin. The FlexForm can either be a reference to a FlexForm
XML file (for example, FILE:EXT:my_extension/Configuration/FlexForm.xml) or
the XML content itself.
This simplifies configuration and avoids the need to define the FlexForm separately
in TCA.
Internally, this adds the FlexForm definition to the ds option of the plugin
via the
columnsOverrides configuration and also adds the pi_flexform
field to the showitem list. For more information, see
Breaking: #107047 - Remove pointer field functionality of TCA flex, which describes the migration of the ds
option from multi-entry to single-entry.
FlexFormTools schema parameter requirement
The service
FlexFormTools
has been refactored to remove its dependency on
$GLOBALS['TCA'] , which caused
architectural issues.
The following methods now support an explicit
$schema parameter that accepts
either a
TcaSchema object or a raw TCA
configuration array:
getDataStructureIdentifier()
parseDataStructureByIdentifier()
cleanFlexFormXML()
Previously, these methods had no schema parameter and relied on
$GLOBALS['TCA'] internally, which was problematic during schema building.
Calling code must now explicitly provide schema data, either as:
A resolved
TcaSchema object (for normal usage)
A raw TCA configuration array (for schema building contexts)
This architectural improvement eliminates circular dependencies and allows
FlexFormTools to be used
during schema building processes where TCA Schema
objects are not yet available, resolving issues in components such as the
RelationMapBuilder .
useTYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
useTYPO3\CMS\Core\Utility\GeneralUtility;
// In RelationMapBuilder - previously not possible
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
foreach ($tca as $table => $tableConfig) {
foreach ($tableConfig['columns'] ?? [] as $fieldName => $fieldConfig) {
if ($fieldConfig['config']['type'] === 'flex') {
// Can now use raw TCA during schema building
$dataStructure = $flexFormTools->parseDataStructureByIdentifier(
$identifier,
$tableConfig // Raw TCA array
);
}
}
}
Copied!
Impact
Direct FlexForm plugin registration
This enhancement simplifies plugin configuration and FlexForm integration, as
FlexForms can now be registered directly with the plugin. The call
ExtensionManagementUtility::addPiFlexFormValue() is no longer required.
This method has been deprecated; see Deprecation: #107047 - ExtensionManagementUtility::addPiFlexFormValue().
FlexFormTools schema support
The service now automatically detects the input type and uses the appropriate
resolution strategy for both TCA Schema objects and raw TCA arrays. It no longer
relies on
$GLOBALS['TCA'] , allowing direct control over the service and
making it usable during schema building where no TCA Schema is available.
Technical details
The service uses PHP union types (
array|TcaSchema) and automatically routes
to the appropriate internal methods. Both input types produce identical normalized
output, ensuring consistent data structures for all
FlexFormTools consumers.
Feature: #107056 - Introduce headerData and footerData ViewHelpers
Two new Fluid ViewHelpers have been introduced to allow injecting arbitrary
content into the HTML
<head> or before the closing
</body> tag
of a rendered page:
<f:page.headerData> - injects content into the
<head> section
<f:page.footerData> - injects content before the closing
</body> tag
The ViewHelpers internally use the
PageRenderer
API and are useful when existing ViewHelpers such as
<f:asset.css> or
<f:asset.script> do not support all required
attributes or use cases (for example,
dns-prefetch,
preconnect,
tracking scripts, or inline JavaScript).
<f:page.footerData><script>var _paq = window._paq = window._paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u = "https://your-matomo-domain.example.com/";
_paq.push(['setTrackerUrl', u + 'matomo.php']);
_paq.push(['setSiteId', '1']);
var d = document,
g = d.createElement('script'),
s = d.getElementsByTagName('script')[0];
g.async = true;
g.src = u + 'matomo.js';
s.parentNode.insertBefore(g, s);
})();
</script></f:page.footerData>
Copied!
Both ViewHelpers output the given content as-is. Any user-supplied input passed
to these ViewHelpers must be escaped manually to prevent Cross-Site Scripting
(XSS) vulnerabilities.
Impact
Extension authors and integrators can now use the new ViewHelpers to add raw
HTML content, such as
<link> or
<script> tags, directly into
the rendered page output.
Feature: #107081 - Add options to predefine link target and class in link browser
The link browser now supports preconfiguring link target and class attributes.
This allows administrators to set global defaults for all link types or specific
defaults per handler type, reducing manual selection effort for editors.
This feature improves editor workflows by providing meaningful default values
for link attributes while preserving full flexibility for customization.
The hierarchical configuration system allows both global defaults and
handler-specific overrides, making link creation faster and more consistent.
TYPO3 already uses the native
URL and
URLSearchParams objects when
working with URLs. The module
@typo3/core/factory/url-factory.js has been
introduced to provide a consistent and convenient way to create and manage these
objects.
Impact
URL and
URLSearchParams objects can now be created by the factory's
createUrl() and
createSearchParams() methods.
The method
createUrl() creates a full
URL object and automatically
sets its base. It accepts the following arguments:
url - string
parameters - mixed
If provided, the
parameters value is passed to
createSearchParams() (described below).
The method
createSearchParams() creates a
URLSearchParams object and
accepts the following argument:
parameters - mixed
Parameters can be passed as plain string values or nested objects.
Passing a plain array is not supported.
Note
When a nested object is passed, values of type
null or
undefined are discarded.
Examples
The following examples assume the existence of
TYPO3.settings.ajaxUrls.my_dedicated_endpoint, pointing to the route
/custom_endpoint, while being on https://localhost for documentation
purposes.
Unlike the native
URLSearchParams constructor, the
UrlFactory.createSearchParams() method does not support JavaScript
entries objects directly. If needed, entries can be converted into an
object by using
Object.fromEntries().
Feature: #107105 - Introduce expression for accessing site locale
A new Symfony ExpressionLanguage expression
locale() has been
introduced.
This expression allows integrators and developers to directly access
the current site locale, which is provided as a locale object.
All public methods of this object are available for use.
For more information, refer to the API documentation:
API
Example
Using locale() in TypoScript conditions
[locale().getName() == "en-US"]
page.20.value = bar
[END][locale().getCountryCode() == "US"]
page.30.value = foo
[END][locale().isRightToLeftLanguageDirection()]
page.40.value = bar
[END]
With forge#101567 the usage of Symfony's #[AsCommand] attribute
has been introduced, which allows configuring a Symfony CLI command
with a corresponding name, description and further options.
It however lacked TYPO3's custom implementation of the schedulable
option, which allows flagging a CLI command to be not allowed to be
scheduled via the Administration > Scheduler backend module.
Note
The top-level backend modules were renamed in TYPO3 v14.
The module now called Administration was formerly named
System, and the module now called System was formerly
named Admin Tools.
This previously required tagging such a command with the
schedulable: false tag attribute in the Services.yaml or
Services.php definition.
For this, the PHP attribute
AsNonSchedulableCommand has been
introduced. Any Symfony Command can use this empty attribute. The automatic
Scheduler registry will ignore any command with this tag.
By default, a Symfony Command remains schedulable using the regular Symfony
attribute. To prevent redundancy, the new attribute
#[AsNonSchedulableCommand] should be used only on top of that.
Another advantage is that an IDE like PhpStorm is capable of showing all
usages of that attribute inside a project.
Impact
Developers can now fully embrace using the Symfony
#[AsCommand]
attribute and still be able to declare a non-schedulable execution within
the scope of the same class, without any service registration.
This is achieved by using the
#[AsNonSchedulableCommand] in addition
to the
#[AsCommand] attribute.
Example
useSymfony\Component\Console\Attribute\AsCommand;
useSymfony\Component\Console\Command\Command;
useTYPO3\CMS\Core\Attribute\AsNonSchedulableCommand;
#[AsCommand('myextension:import', 'Import data from external source')]#[AsNonSchedulableCommand]finalclassImportCommandextendsCommand{
// ...
}
Copied!
Feature: #107180 - PSR-14 events for Backend Users module
Several PSR-14 events have been added to allow customizations of the Backend
Users module - in particular, which users, groups, and file mounts can be viewed
in the module.
This event is dispatched when the backend user repository fetches a list of
filtered backend users (itself called when displaying the list of users in the
backend module). It makes it possible to modify the query constraints based on
the currently active filtering.
The event provides the following public properties:
$demand: An instance of
\TYPO3\CMS\Beuser\Domain\Model\Demand containing the current search
criteria
$query: The
\TYPO3\CMS\Extbase\Persistence\QueryInterface instance being used to
assemble the query
$constraints: An array of query constraints. New constraints can be
added to this array.
This event is dispatched when the backend group repository fetches a list of
filtered backend groups (itself called when displaying the list of groups in the
backend module). It makes it possible to modify the query constraints based on
the currently active filtering.
The event provides the following public properties:
$demand: An instance of
\TYPO3\CMS\Beuser\Domain\Dto\BackendUserGroup containing the current
search criteria
$query: The
\TYPO3\CMS\Extbase\Persistence\QueryInterface instance being used to
assemble the query
$constraints: An array of query constraints. New constraints can be
added to this array.
A list of user groups can be used to filter the users list in the backend
module. This event is dispatched right after this list is assembled and makes it
possible to modify it.
The event provides the following public properties:
These events can be used to implement custom user or permission management
processes in the Backend Users module. Be aware that this area is security
sensitive. Ensure that no unauthorized data exposure or privilege escalation
occurs when modifying these queries or lists.
Feature: #107201 - Extended RSS Widget with Atom Support
Building upon the configurable dashboard widgets functionality introduced in
Feature: #107036 - Configurable dashboard widgets, the existing RSS widget has been extended to
support Atom feeds. This provides a unified solution for both RSS and Atom feed
formats within the TYPO3 Dashboard system.
The configurable widget architecture makes it possible to create feed-based
widgets that users can configure directly through the dashboard interface. This
capability now extends seamlessly to Atom feeds, which are commonly used by
platforms such as GitHub for release feeds and project updates.
The RSS widget now automatically detects and parses both RSS and Atom feed
formats. Atom feeds are parsed according to the Atom Syndication Format
(RFC 4287). This provides comprehensive support for modern feed formats used by
many development platforms and services, all within a single widget
implementation.
Impact
The existing "RSS Widget" now supports both RSS and Atom feeds automatically.
Atom feeds (commonly used by GitHub, GitLab, and other platforms) can now be
displayed using the same RSS widget.
Widget instances are configurable with custom labels, feed URLs, and display
limits.
Each widget can be configured independently, allowing multiple feeds of
different formats on the same dashboard.
Automatic caching ensures optimal performance with configurable cache
lifetimes.
Currently configurable options
The RSS widget supports the following configuration options for both RSS and
Atom feeds:
Label
Custom title for the widget instance.
Optional field that defaults to the widget’s default title.
Feed URL
RSS or Atom feed URL to display.
Format detection is automatic based on feed content.
Can be preconfigured in the service definition to create read-only
instances.
Limit
Number of feed entries to show (default: 5).
Configurable integer value to control widget content density.
Lifetime
Cache duration in seconds for the feed (default: 43200 = 12 hours).
Advanced setting typically configured by integrators.
Not configurable in the user interface.
Example for RSS widget with Atom feed
Service configuration
EXT:my_extension/Configuration/Services.yaml
services:# Button provider for external linkdashboard.buttons.github_releases:class:'TYPO3\CMS\Dashboard\Widgets\Provider\ButtonProvider'arguments:$title:'View all releases'$link:'https://github.com/TYPO3/typo3/releases'$target:'_blank'# RSS widget with Atom feed URLdashboard.widget.github_releases:class:'TYPO3\CMS\Dashboard\Widgets\RssWidget'arguments:$buttonProvider:'@dashboard.buttons.github_releases'$options:feedUrl:'https://github.com/TYPO3/typo3/releases.atom'lifeTime:43200limit:10tags:-name:dashboard.widgetidentifier:'github_releases'groupNames:'general'title:'my_extension.widgets:github_releases.title'description:'my_extension.widgets:github_releases.description'iconIdentifier:'content-widget-rss'height:'large'width:'medium'
Copied!
Usage in the dashboard
Navigate to the dashboard where you want to add the widget.
Click Add widget and select the RSS widget.
Click the settings (cog) icon to customize the widget.
Configure the feed URL (RSS or Atom), limit, and label as needed.
Save the configuration to apply the changes.
The widget automatically detects the feed format and displays entries with
titles, publication dates, content summaries, and author information when
available.
Feed format support
The RSS widget now supports both feed formats:
RSS feeds
Item titles
displayed as clickable links.
Publication dates
used for sorting entries (newest first).
Descriptions
displayed as entry content (HTML tags stripped).
Atom feeds
Entry titles
displayed as clickable links.
Publication dates
used for sorting entries (newest first).
Content/Summary
displayed as entry description (HTML tags stripped).
Author information
name, email, and URL when provided in the feed.
Feature: #107240 - Add warning when pasting password with whitespace
A new warning mechanism has been introduced in the backend login form to help
users avoid authentication issues when pasting passwords that contain leading
or trailing whitespace.
When a password is pasted into the backend login form, TYPO3 now detects if the
pasted text contains leading or trailing whitespace characters (spaces, tabs,
newlines, etc.) and displays a warning message to the user.
The warning includes an action button that allows users to automatically remove
the surrounding whitespace from the pasted password, ensuring successful login
attempts.
This feature helps prevent common login failures caused by accidentally copying
whitespace along with passwords from password managers, text editors, or other
sources.
The implementation includes:
Detection of leading and trailing whitespace in pasted passwords
Visual warning message displayed to the user
One-click action to remove the surrounding whitespace
Example
The whitespace detection covers various whitespace characters including:
Regular spaces (U+0020)
Tabs (U+0009)
Line breaks (U+000A, U+000D)
Other Unicode whitespace characters
Impact
Users now receive immediate feedback when pasting passwords that contain
surrounding whitespace, reducing login failures and improving the overall user
experience when authenticating to the TYPO3 backend.
Feature: #107256 - PSR-14 event to modify options in EmailFinisher
A new PSR-14 event
\TYPO3\CMS\Form\Event\BeforeEmailFinisherInitializedEvent
has been introduced. This event is dispatched before the
\EmailFinisher is initialized and allows
listeners to modify the finisher options dynamically.
This enables developers to customize email behavior programmatically, such as:
Setting alternative recipients based on frontend user permissions
Modifying the email subject or content dynamically
Replacing recipients with developer email addresses in test environments
Adding or removing CC or BCC recipients conditionally
Customizing reply-to addresses
The event provides access to both the finisher context (read-only) and the
options array, allowing for flexible manipulation of the email configuration.
To modify the
\EmailFinisher options, the
following methods are available:
getFinisherContext(): Returns the
FinisherContext containing
form runtime and request information
getOptions(): Returns the current finisher options array
setOptions(): Allows setting the modified options array
It is now possible to dynamically modify
\EmailFinisher options before email
processing begins, using the new PSR-14 event
BeforeEmailFinisherInitializedEvent . This
provides developers with full control over email configuration without needing
to extend or override the
\EmailFinisher class.
Feature: #107281 - Type-specific TCAdefaults support
The
TCAdefaults configuration has been extended to support
type-specific syntax similar to
TCEFORM, enabling different
default values based on the record type.
This allows configuration like:
Page TSconfig
# Field-level default (applies to all content types)
TCAdefaults.tt_content.header_layout = 1
# Type-specific defaults (applies only to specific content types)
TCAdefaults.tt_content.header_layout.types.textmedia = 3
TCAdefaults.tt_content.frame_class.types.textmedia = ruler-before
Copied!
The same syntax is supported in User TSconfig as well.
Type-specific defaults take precedence over field-level defaults, and Page
TSconfig overrides User TSconfig following the established inheritance pattern.
Implementation details
The feature is implemented in two main areas:
Backend forms: The
DatabaseRowInitializeNew
class now processes type-specific defaults when creating new records in the
backend.
DataHandler: The
DataHandler class supports
type-specific defaults when creating records programmatically via the PHP
API.
Important
Type-specific defaults only work when the record type is defined in the new
record values. This happens automatically when using the New Content Element
Wizard (via
defVals) or when explicitly providing the type field in
the DataHandler datamap (for example,
'CType' => 'textmedia' for
content elements).
If no type information is available during record creation, only
field-level
TCAdefaults will be applied.
Fallback behavior
If no type-specific default is found for a given record type, the system falls
back to:
Field-level
TCAdefaults configuration
TCA
'default' configuration
Database field default value
Automatic field discovery
This enhancement makes
TCAdefaults consistent with
TCEFORM patterns and enables automatic field discovery.
PHP API usage - DataHandler with type-specific defaults
useTYPO3\CMS\Core\DataHandling\DataHandler;
useTYPO3\CMS\Core\Utility\GeneralUtility;
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$datamap = [
'tt_content' => [
'NEW123' => [
'pid' => 42,
'CType' => 'textmedia',
'header' => 'My Content Element',
// header_layout and frame_class will be set automatically// based on type-specific TCAdefaults configuration
],
],
];
$dataHandler->start($datamap, [], $backendUser);
$dataHandler->process_datamap();
Copied!
Impact
This feature provides a more flexible and consistent way to configure default
values for different record types, reducing repetitive configuration and
improving the user experience when creating new records.
The type-specific syntax aligns
TCAdefaults with the established
TCEFORM pattern, making the configuration more intuitive for
TYPO3 developers and integrators.
Feature: #107322 - New PSR-14 AfterRichtextConfigurationPreparedEvent
A new PSR-14 event
\TYPO3\CMS\Form\Event\BeforeFormIsCreatedEvent
has been introduced. It serves as a direct replacement for the now
removed hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormCreate'] .
The new event is dispatched immediately before a new form is created in the
backend.
The event provides the following public properties:
$form: The form definition array
$formPersistenceIdentifier: The form persistence identifier used to
store the new form
A new Fluid ViewHelper
<f:page.title> has been introduced to allow
setting the page title directly from Fluid templates.
This is especially useful for Extbase plugins that need to set a page title
in their detail views without having to implement a custom page title
provider.
<f:page.title>About Us - Company Information</f:page.title><h1>About Us</h1><p>Welcome to our company...</p>
Copied!
Impact
Extension developers can now easily set page titles from their Fluid templates
without creating custom page title providers for each extension. This
simplifies the implementation of dynamic page titles in Fluid templates or in
Extbase plugins for detail views where the title should reflect the displayed
record.
The ViewHelper integrates seamlessly with TYPO3's existing page title provider
system and respects the configured provider priorities.
Feature: #107359 - Introduce Fluid page meta ViewHelper
The ViewHelper supports all features of the MetaTagManager API:
OpenGraph and Twitter / X Card meta tags:
<f:page.metaproperty="og:title">
My Article Title
</f:page.meta><f:page.metaproperty="og:description">
Article description
</f:page.meta><f:page.metaproperty="twitter:card">
summary_large_image
</f:page.meta>
<f:page.metaproperty="author"type="name">
John Doe
</f:page.meta><f:page.metaproperty="robots"type="name">
index, follow
</f:page.meta>
Copied!
Replacing existing meta tags:
<f:page.metaproperty="description"replace="true">
Override any existing description
</f:page.meta>
Copied!
ViewHelper Arguments
property (required): The meta property name (for example,
description, og:title)
type (optional): The meta type attribute (name, property,
http-equiv). If not set, the appropriate MetaTagManager determines
the type automatically.
subProperties (optional): Array of sub-properties for complex
meta tags.
replace (optional): Boolean to replace existing meta tags with
the same property (default: false).
Impact
Extension developers can now easily set meta tags from Fluid templates
without needing to use the MetaTagManager API directly in PHP code. This
simplifies the implementation of SEO-optimized pages in Extbase plugins,
especially for detail views where meta tags should reflect the displayed
record.
The ViewHelper integrates seamlessly with TYPO3's existing
MetaTagManager system and respects all configured meta tag managers and
their priorities.
Feature: #107380 - PSR-14 to manipulate form duplication process
A new PSR-14 event
\TYPO3\CMS\Form\Event\BeforeFormIsDuplicatedEvent
has been introduced. It serves as a direct replacement for the now
removed hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormDuplicate'] .
The new event is dispatched immediately before a form is duplicated in the
backend.
The event provides the following public properties:
$form: The form definition array
$formPersistenceIdentifier: The form persistence identifier used to
store the duplicated form
A new PSR-14 event
BeforeFormIsDeletedEvent
has been introduced. It serves as a direct replacement for the now
removed hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormDelete'] .
The new event is dispatched immediately before a form is deleted in the
backend.
The event provides the following public properties:
$formPersistenceIdentifier: The form persistence identifier
(read-only)
$preventDeletion: A boolean flag that can be set to true
to prevent the deletion of the form
The new event is stoppable. As soon as
$preventDeletion is set to
true, no further listener is called.
Example
An example event listener could look like this:
Example event listener class
namespaceMyVendor\MyExtension\Form\EventListener;
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Form\Event\BeforeFormIsDeletedEvent;
finalclassBeforeFormIsDeletedEventListener{
#[AsEventListener('my_extension/before-form-is-deleted')]publicfunction__invoke(BeforeFormIsDeletedEvent $event): void{
$event->preventDeletion = true;
$persistenceIdentifier = $event->formPersistenceIdentifier;
// Do something with the persistence identifier
}
}
Copied!
Impact
With the new
BeforeFormIsDeletedEvent , it is
now possible to prevent the deletion of a form and to add custom logic based on
the delete action.
Feature: #107388 - PSR-14 to manipulate form before it is saved
A new PSR-14 event
\TYPO3\CMS\Form\Event\BeforeFormIsSavedEvent
has been introduced. It serves as a direct replacement for the now
removed hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormSave'] .
The new event is dispatched immediately before a form is saved in the backend.
The event provides the following public properties:
$form: The form definition array
$formPersistenceIdentifier: The form persistence identifier used to
store the form
The file replacement functionality has been significantly improved to provide
a better user experience in the TYPO3 backend. Instead of opening the file
replacement interface in a new window, it now opens in a modal dialog,
maintaining the user's context and providing a more consistent experience with
other TYPO3 backend operations.
The modal displays thumbnails for supported file types, allowing users to
visually verify the current file before replacement. Additionally, the
following detailed information is shown:
File type
File size
Creation date
Impact
This enhancement significantly improves the file management experience in
TYPO3 by providing a more modern, context-aware interface for file replacement.
The combination of modal-based interaction and file information display creates
a more efficient and user-friendly workflow that aligns with modern web
application patterns.
The changes particularly benefit content editors who frequently work with
files, providing them with the visual and metadata context needed to make
confident file replacement decisions without interrupting their content
creation workflow.
TYPO3 now utilizes the Symfony Translation component for reading localization
label files such as XLIFF and PO, instead of its custom localization parsers.
The migration brings several improvements:
Standardized file parsing using Symfony's translation loaders
Enhanced API for accessing translation catalogues
Support for custom translation loaders following Symfony standards
The new system maintains backward compatibility while providing a modern
foundation for future improvements with translatable labels.
In addition, all label-related configuration options have been streamlined
under the
$GLOBALS['TYPO3_CONF_VARS']['LANG'] namespace.
The following new configuration options have been introduced:
All previous configuration options have been moved to the new
$GLOBALS['TYPO3_CONF_VARS']['LANG'] namespace. These are automatically
migrated to the new location when accessing the install tool.
Please note: This functionality only affects the internal handling of
translation files ("locallang" files). The public API of the localization
system remains unchanged.
Feature: #107441 - Allow more hashing algorithms in FAL
Previously, FAL's
LocalDriver only supported md5
and sha1 as hashing algorithms. While this may be sufficient for many use
cases, it might be necessary to use different hashing algorithms depending on
the specific scenario.
The method
LocalDriver->hash() is now able to use any hashing algorithm
that is registered in PHP itself by building a
HashContext object and
updating it by streaming the file content.
Impact
FAL's
LocalDriver can now make use of different hashing algorithms, e.g.
crc32, sha256 and many more.
A wizard to create new scheduler tasks has been introduced in the
Administration > Scheduler module to significantly improve the user
experience when creating new scheduler tasks. The wizard replaces the previous
dropdown-based task selection in FormEngine with a modern, categorized
interface similar to the content element wizard.
Note
The top-level backend modules were renamed in TYPO3 v14.
The module now called Administration was formerly named
System, and the module now called System was formerly
named Admin Tools.
Categorized task selection: Tasks are now organized by extension or
category for better discoverability
Search functionality: Users can search and filter available tasks
Visual task representation: Each task displays with proper icons,
titles, and descriptions
Tip
Using the
iconOverlay option on task type registration, an icon
overlay can be added, which is then displayed in the wizard. This can be
useful for similar task types that use the same base icon but still need
to be differentiated.
Technical improvements
Prevents broken records: The old system preselected the first available
task type when creating new tasks, which caused validation issues when
users changed the task type, since required fields for the new type might
not be properly initialized
Clean task type preselection: The selected task type is directly passed
to FormEngine, eliminating the need to change the type in the form
Extensible via PSR-14 event: Extensions can modify wizard items through
the new
ModifyNewSchedulerTaskWizardItemsEvent
PSR-14 event
A new PSR-14 event
ModifyNewSchedulerTaskWizardItemsEvent
has been introduced to allow extensions to modify the wizard items.
The event provides the following methods:
getWizardItems(): Returns the current wizard items array
setWizardItems(): Sets the complete wizard items array
addWizardItem(): Adds a single wizard item
removeWizardItem(): Removes a wizard item by key
getRequest(): Returns the current server request
Example
A corresponding event listener class:
Example event listener class
namespaceMyVendor\MyExtension\EventListener;
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Scheduler\Event\ModifyNewSchedulerTaskWizardItemsEvent;
finalclassModifySchedulerTaskWizardListener{
#[AsEventListener('my-extension/scheduler/modify-wizard-items')]publicfunction__invoke(ModifyNewSchedulerTaskWizardItemsEvent $event): void{
// Add a custom task to the wizard
$event->addWizardItem('my_custom_task', [
'title' => 'My Custom Task',
'description' => 'A custom task provided by my extension',
'iconIdentifier' => 'my-custom-icon',
'taskType' => 'MyVendor\\MyExtension\\Task\\CustomTask',
'taskClass' => 'MyVendor\\MyExtension\\Task\\CustomTask',
]);
// Remove an existing task
$event->removeWizardItem('redirects_redirects:checkintegrity');
// Modify existing wizard items
$wizardItems = $event->getWizardItems();
foreach ($wizardItems as $key => $item) {
if (isset($item['title'])
&& str_contains($item['title'], 'referenceindex:update')) {
$item['title'] = 'Update reference index';
$event->addWizardItem($key, $item);
}
}
}
}
Copied!
Impact
The scheduler task creation workflow is significantly improved with better
UX
The risk of creating broken task records due to task type changes is
eliminated
Extensions can easily modify the wizard through the PSR-14 event
The interface is more consistent with other TYPO3 wizard interfaces
Task discovery is improved through categorization and search functionality
The scheduler task timing configuration has been migrated to the single TCA
execution_details field, which is of type json. This field has now been
enhanced to allow extensions to customize the corresponding timing-related
fields, particularly the frequency field with custom cron expressions. This is
achieved through the new
overrideFieldTca option, available in the TCA
field configuration.
Previously, frequency options were only configurable through the global
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['frequencyOptions']
array, which has been moved to the TCA configuration to provide better
extensibility and consistency with TYPO3's configuration patterns, see
Breaking: #107488 - Scheduler frequency options moved to TCA.
Enhanced customization options
Extensions can now override any timing-related field configuration using the
overrideFieldTca mechanism in the
execution_details field.
Available fields:
Frequency field:
frequency
Running type field:
runningType
Parallel execution settings:
multiple
Start or end date fields:
start and
end
Example usage
Extensions can now add custom frequency options by creating a TCA override file:
This enhancement provides a more flexible and extensible way to configure
scheduler task timing options, allowing extensions to seamlessly integrate
custom timing configurations while maintaining consistency with TYPO3's
configuration patterns.
Feature: #107518 - PSR-14 event to modify form elements after being added
A new PSR-14 event
BeforeRenderableIsAddedToFormEvent
has been introduced. It serves as an improved replacement for the now
removed hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['initializeFormElement'] .
The new event is dispatched immediately before a renderable has been
constructed and added to the corresponding form. This allows full
customization of the renderable after it has been initialized.
The event provides the
$renderable public property.
With the new
BeforeRenderableIsAddedToFormEvent ,
it is now possible to modify a renderable after it has been initialized and
right before being added to its form.
Feature: #107519 - Add "discard" command to DataHandler
Always ensure that you are using the UID of the versioned record when
discarding workspace records. Using the live record's UID will not work as
expected. You can identify versioned records by checking that
t3ver_wsid > 0 and
t3ver_oid points to the live record.
Migration from legacy commands
The new discard command replaces the previous version-based approach,
which was not widely known:
Legacy approach (still supported but discouraged)
// Old way - will be removed in future versions
$commandArray = [
'pages' => [
$versionedUid => [
'version' => [
'action' => 'clearWSID',
],
],
],
];
Copied!
New recommended approach
// New way - cleaner and more explicit
$commandArray = [
'pages' => [
$versionedUid => [
'discard' => true,
],
],
];
Copied!
The previous clearWSID and flush actions are still supported for
backward compatibility but are considered deprecated and will be removed in
future versions.
Feature: #107526 - Custom TCA types for scheduler tasks
Scheduler tasks can now be created using custom TCA types instead of the
legacy :AdditionalFieldProvider approach. This enhancement allows
developers to define custom database fields for specific task types,
providing full flexibility of FormEngine and DataHandler for editing and
persistence.
The new approach replaces three major downsides of the traditional method:
Native database fields are now created automatically via TCA instead of
being serialized into a JSON field.
Tasks are registered via TCA record types instead of custom registration
methods.
Field handling is now done through standard TCA configuration, eliminating
the need for AdditionalFieldProviders.
Benefits
Using custom TCA types for scheduler tasks provides several advantages:
FormEngine handles validation automatically, reducing the risk of XSS or
SQL injection vulnerabilities in extension code.
Task configuration follows standard TYPO3 form patterns.
Developers can use familiar TCA configuration instead of implementing
custom field providers.
Access to all TCA field types, validation, and rendering options.
Migration
Existing task types using custom TCA types automatically migrate existing
data through the
getTaskParameters() and
setTaskParameters()
methods:
During migration,
getTaskParameters() is called to extract field
values from the serialized task object.
For new TCA-based tasks,
setTaskParameters() receives the full
database record as an array instead of serialized data from the
parameters field.
The task class name still matches the value of the
tasktype field.
Implementation examples
File storage indexing task
TCA configuration in
Configuration/TCA/Overrides/file_storage_indexing_task.php:
TCA configuration for the file storage indexing task
After migrating your tasks to native TCA types, run the Migrate the
contents of the tx_scheduler_task database table into a more structured
form upgrade wizard.
Impact
Extension developers can create more maintainable and secure scheduler
tasks.
Custom task configuration benefits from full FormEngine capabilities.
Existing tasks are automatically migrated without data loss.
Development follows standard TYPO3 TCA patterns.
Improved validation and security through FormEngine and DataHandler.
Feature: #107528 - PSR-14 event before renderable is removed from form
A new PSR-14 event
\TYPO3\CMS\Form\Event\BeforeRenderableIsRemovedFromFormEvent
has been introduced. It serves as an improved replacement for the now
removed hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRemoveFromParentRenderable'] .
The new event is dispatched immediately before a renderable is deleted from
the form.
The event provides the following public properties:
$renderable: The form element (read-only).
$preventRemoval: A boolean flag that can be set to true
to prevent the removal of the renderable.
The event is stoppable. As soon as
$preventRemoval is set to
true, no further listeners are executed.
Example
An example event listener could look like this:
Example event listener class
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Form\Event\BeforeRenderableIsRemovedFromFormEvent;
classMyEventListener{
#[AsEventListener(
identifier: 'my-extension/before-renderable-is-removed-from-form-event',
)]
publicfunction__invoke(BeforeRenderableIsRemovedFromFormEvent $event): void{
$event->preventRemoval = true;
$renderable = $event->renderable;
// Custom logic before the renderable is removed
}
}
Copied!
Impact
With the new
BeforeRenderableIsRemovedFromFormEvent ,
it is now possible to prevent the deletion of a renderable and to add custom
logic based on the deletion.
Feature: #107537 - System resource API for system file access and public URI generation
TYPO3 allows files to be configured for multiple purposes. For example, a logo
to be shown on the login page, CSS and JavaScript files to be added to web
pages, or icons to be used for records. Most of the time, these are specified
using the EXT syntax to reference a file within an extension. However, it can
sometimes also be useful to reference an external URL or a file from a
FAL storage.
To achieve this, TYPO3 Core code previously needed to parse the specified
configuration, identify the type (extension file, FAL, URL), and then generate a
URL from it. While there was some API available for this purpose, it was
incomplete and consisted of multiple parts that were not clearly named, making
them easy to misuse. As a consequence, code in the TYPO3 Core differed depending
on where the configuration was used.
For users, this could lead to issues when a specific resource syntax worked in
one place (for example, in TypoScript) but not in another (for example, in a
Fluid email template). Moreover, the generated URLs could be inconsistent —
sometimes including a cache-busting addition and sometimes not.
Third-party developers struggled with a scattered API that was hard to use and
understand, especially regarding which methods should be called, in what order,
and how to maintain compatibility between Composer mode and classic mode.
The new System Resource API addresses these shortcomings and enables many
additional features to be built on top of it.
The code for resource resolving and URL generation is now encapsulated in a
precise and centralized API in one place. This not only makes it easier to
maintain and fix bugs, but ensures that such fixes apply automatically to all
parts of the system where resources are configured.
Before diving into the details of the API, some terminology is clarified,
followed by a top-level overview.
Naming conventions and top-level overview
A system resource is a file or folder within a TYPO3 project. This can be a:
package resource – a file within an extension
FAL resource – a file from a FAL storage
app resource – a file in the TYPO3 project folder
URI resource – a URL
Package resource
A package resource can now be specified with a new syntax like this:
It consists of three parts, separated by a colon (:):
PKG prefix
Composer name of the package (also possible in classic mode for extensions
that contain a composer.json)
Relative path to the file within the package
The well-known EXT syntax can still be used for the time being:
EXT:ext_name/Resources/Public/Icons/Extension.svg
This syntax is not yet deprecated, but users are advised to switch to the new
syntax for new projects.
App resource
An app resource is a file or folder within your TYPO3 installation. Such files
can also be specified using the PKG syntax, using the virtual name
typo3/app as package name:
PKG:typo3/app:public/typo3temp/assets/style.css
By default, only access to a fixed set of folders is allowed:
public/_assets, public/typo3temp/assets, and public/uploads.
Additional allowed folders can be configured via:
While public is the default public directory in Composer-based
installations, it can be configured differently (for example web,
htdocs, html, or www). In that case, the correct public directory
must be specified for the resource, for example
PKG:typo3/app:web/typo3temp/assets/style.css.
FAL resource
While users are encouraged to use package and app resources, it is also
possible to specify files from FAL storages
using the following syntax:
FAL:1:/identifier/of/file.svg
Copied!
It consists of three parts, separated by a colon (:):
FAL prefix
Storage ID (UID of the FAL storage record)
Identifier of the file within this storage (for hierarchical storages,
a slash-separated path)
URI resource
A fully qualified URL (including HTTP(S) scheme) can be specified:
https://www.example.com/my/image.svg
URIs relative to the current host can be specified by prefixing them with
URI: like so:
URI:/path/to/my/image.svg
The string after the URI: prefix must be a valid URI. This means,
that TYPO3 will now throw an exception, rather than rendering an invalid URI
to HTML, when an invalid URI is provided.
Legacy resource annotations
The following legacy string representations can still be used, but they are
deprecated and will be removed in future TYPO3 versions:
FAL resource (relative path to the default FAL storage):
fileadmin/identifier/of/file.svg
App resource (relative path to the project’s public directory):
typo3temp/assets/style.css
App resource (relative path to the project’s public directory):
_assets/vite/foo.css
All representations mentioned here are resource identifiers. They are
strings that uniquely identify a resource within the system.
API description
The PHP API consists of two parts:
Resource resolving
URI generation
The result of resource resolving is an object (different objects for different
resource types), which can then be passed to the API for URI generation.
The
SystemResourceFactory and an
implementation of a system resource publisher are injected via dependency
injection (DI). The resource publisher is referenced using the interface
SystemResourcePublisherInterface .
Note
In the future, different publishing strategies might be implemented, for
example publishing resources directly to a CDN. In one TYPO3 installation,
however, there can be only one default implementation, which can be
configured to be used when the interface is referenced.
In the renderUrl() method, the resourceFactory is used to obtain a resource
object from a given resource identifier using createPublicResource(). This
object is then passed to the generateUri() method of the resourcePublisher.
The method has a second required argument for the current request. If the code
has no access to the current request, null can be passed instead. Passing
null is discouraged and might be deprecated in future TYPO3 versions. For CLI
commands, passing null is fine, but absolute URIs (including scheme and
hostname) cannot be generated in this case.
Example Fluid usage
A new <f:resource> ViewHelper converts a resource identifier into an object
that can be passed to other ViewHelpers. Currently only <f:uri.resource>
accepts such resource objects, but more (for example <f:image>) will support
them in the future.
CSS and JavaScript files can be referenced using all resource identifiers
mentioned above.
Impact
All parts of the TYPO3 Core where URLs are generated from specified resources
now use the new API. This means all of those places support the new PKG
syntax and have cache busting applied automatically.
TYPO3 installations using other syntax than the supported ones need to migrate
their resource references. Resource identifiers such as
typo3conf/ext/my_ext/Resources/Public/Image.svg no longer work consistently
throughout the system and must be replaced with EXT or PKG resource
identifiers.
The benefits are:
Consistency throughout the system for system resource resolving and URL
generation.
A more intuitive API that helps avoid mistakes, including security-related
ones.
Consolidation of all code currently resolving system resources and
generating URLs.
A foundation for future features such as publishing system resources to a
CDN and letting TYPO3 generate CDN URLs directly instead of parsing HTML
output.
Outlook
This change is a major step forward, but further improvements are planned:
Implement flexible resource publishing in Composer and classic mode,
allowing different publishing strategies and configuration of additional
public resource folders or files.
Use this API in all areas that consume private resources, most notably
Fluid template files.
Replace FAL storage for system assets completely by leveraging the app
resource to contain templates, CSS files, logos, images, or even complete
themes.
Feature: #107566 - PSR-14 event after current page is resolved
A new PSR-14 event
\TYPO3\CMS\Form\Event\AfterCurrentPageIsResolvedEvent
has been introduced. It serves as an improved replacement for the now
removed hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterInitializeCurrentPage'] .
The new event is dispatched after the current page has been resolved.
The event provides the following public properties:
$currentPage: The current page.
$formRuntime: The form runtime object (read-only).
$lastDisplayedPage: The last displayed page (read-only).
A new PSR-14 event
BeforeRenderableIsValidatedEvent
has been introduced. It serves as an improved replacement for the now
removed hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterSubmit'] .
The new event is dispatched right before a renderable is validated.
The event provides the following public properties:
$value: The submitted value of the renderable.
$formRuntime: The form runtime object (read-only).
With the new
BeforeRenderableIsValidatedEvent ,
it is now possible to modify or validate the value of a renderable element
before TYPO3 Core performs its built-in validation logic. This allows
extensions to inject custom validation rules or preprocessing steps before
standard validation runs.
Feature: #107569 - PSR-14 event before renderable is rendered
A new PSR-14 event
BeforeRenderableIsRenderedEvent
has been introduced. It serves as a replacement for the now
removed hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRendering'] .
The new event is dispatched right before a renderable is rendered.
The event provides the following public properties:
With the new
BeforeRenderableIsRenderedEvent ,
it is now possible to modify a renderable before it is rendered or adjust the
form runtime data before rendering.
Feature: #107581 - Improve handling of FinisherException
\FinisherException instances thrown during
form processing are now caught within their respective finisher. Instead of
resulting in a generic 503 error page, the exception is logged, and a
user-friendly error message is displayed to the user, indicating that the form
could not be submitted successfully.
The error message can be customized for each finisher using the new
errorMessage option. Additionally, the newly introduced Error
template can be overridden and customized.
Impact
Users no longer see a 503 error page if a FinisherException occurs. Instead,
they receive a clear, user-friendly message in the form frontend.
All FinisherExceptions are logged for further analysis and debugging.
This change improves the user experience and makes error handling in forms
more flexible and transparent.
Feature: #107628 - Improved backend module naming and structure
TYPO3 Core's backend module structure has been modernized with clearer,
more intuitive naming conventions that better align with industry
standards used by enterprise content management systems.
Module names should immediately convey their purpose to editors and
administrators. This series of renamings improves clarity,
discoverability, and reduces the cognitive load for both new and
experienced users.
Clear and consistent naming is fundamental to good user experience.
When comparing TYPO3 Core with other enterprise CMS platforms, it became
evident that some of TYPO3 Core's module names were either too technical,
ambiguous, or did not clearly communicate their purpose.
To summarize, the goals of this restructuring are:
Improved clarity: Module names immediately convey their purpose.
Industry alignment: Adopt naming conventions familiar to users of
other enterprise CMS platforms.
Better discoverability: Help new users find functionality without
extensive training.
Reduced cognitive load: Minimize confusion when navigating the
backend.
Module renamings
The following modules have been renamed.
Top-level modules
Web => Content
The top-level Web module has been renamed to
Content.
Rationale: TYPO3 is a content management system. The primary
workspace where editors create and manage content should be clearly
labeled as such. The term "Web" was ambiguous and did not communicate the
module's purpose. Users outside the TYPO3 ecosystem often did not
understand what "Web" meant in this context.
Migration: Update module parent references from
'web' to
'content':
The top-level File module has been renamed to
Media.
Rationale: The term "Media" clearly indicates the module's purpose of
managing digital media files (images, videos, documents, audio files,
etc.) within the CMS. The term "File" was too generic and technical,
while "Media" is widely understood and commonly used in content
management systems to refer to digital content assets.
Migration: Update module parent references from
'file' to
'media':
The top-level Site Management module has been renamed to Sites.
Rationale: The former label "Site Management" was long and formal, and did
not align with TYPO3’s evolving, concise module naming strategy. The
simplified name "Sites" improves scanability in the module menu and matches
the naming of other top-level modules. It also better reflects the purpose
of the module: providing an overview entry point for all configured (web)sites.
Migration: The top-level module identifier
site is kept. No migration
is necessary.
Admin (tools) <=> System
The top-level module formerly known as Admin tools is now
called Administration.
The purpose of this top-level module has changed. It now contains those
modules useful to backend administrators, such as user and permission
management, the Scheduler, and Integrations.
Most modules formerly found in Admin tools are now located in
System.
Rationale: The top-level module Administration now
contains modules that are used by backend administrators in their daily
work. Modules that require system maintainer permissions are found in the
module named System.
Migration: Modules generally accessible to backend administrators
should be moved to the top-level module with the identifier admin.
Modules that require system maintainer permissions or are mainly useful
to system maintainers and DevOps should be moved to system.
For modules, where the module identifier changed, the upgrade wizard
"Migrate module permissions" migrates module level group and user permissions.
Page => Layout
The second-level Page module has been renamed to Layout
to better match its scope.
Rationale: The previous module name "Page" did not clearly convey the
module’s purpose or workflow. TYPO3 provides multiple ways to interact with
a page (e.g. structure, properties, preview), and the term "Page" alone did
not describe which aspect was being managed. The renamed module "Layout"
more accurately reflects what editors do inside the module: maintain the
page layout, manage content elements, and organize them into the correct
columns and grids. This provides clearer expectations, improves usability
for new editors, and aligns the module name with modern TYPO3 workflows
and terminology.
Migration: Since the module is just renamed, there are no migrations
necessary.
List => Records
The second-level List module has been renamed to
Records to better convey its purpose and improve clarity.
Rationale: The term "List" is too generic and does not adequately
communicate the module's purpose. While "List" could refer to any kind of
enumeration or overview, the module actually provides structured access to
database records appearing on a page. The new name "Records" is more
specific and immediately communicates that this module is about working with
data records—viewing, editing, and managing them at the database level.
The term "Records" also aligns with how the module is already described in
its own interface ("List of database records") and better reflects the
technical nature of the module's functionality. For users coming from other
enterprise CMS platforms or database-driven systems, "Records" is a widely
understood term that clearly indicates low-level data management
capabilities.
This renaming reduces ambiguity and helps users—especially those new to
TYPO3—understand that this module provides direct access to the underlying
record structure, distinguishing it from the more content-focused
Layout module.
Migration: The module identifier has been renamed from web_list to
records. An alias is in place. However, use the new identifier when referecing.
The second-level View module has been renamed to
Preview to better match its scope. It has also been moved one
position down after Records, as that module is considered more
important for daily work.
Rationale: The term "Preview" is more precise, as it triggers a
frontend preview and cannot be misunderstood as "viewing" a page in the
backend context.
Migration: Since the module is already internally referred to as
page_preview, no changes in referencing modules are required.
Workspaces => Publish
The second-level Workspaces module has been renamed to
Publish to better match its current scope.
Rationale: The initially introduced "Workspaces administration" tool
has been reworked to move content through a publishing process in past
versions. For this reason, it is now renamed to "Publish" and
is only visible when inside a workspace.
Migration: The module has internally been renamed to workspaces_publish.
A module alias is in place, so references to the old workspaces_admin
identifier keep working as before, but it is recommended to adapt usages.
The upgrade wizard "Migrate module permissions" migrates backend user and
group-level permissions for this module.
Info, Indexing, Check Links => Status
The second-level Info module has been renamed to Status
to better match its scope. It has also been moved into EXT:backend so it is
always available and displayed if it contains at least one module.
Rationale: The module was moved to EXT:backend to make it always available
and to provide a common place for page and site status information.
The renaming to Status better reflects its purpose and removes
unnecessary dependencies between informational extensions.
Migration: The module identifier has been renamed from web_info to
content_status an alias is in place. Use the new identifier to place custom
modules.
Extensions placing third level modules into the module now called
Status do not need to require
typo3/cms-info
anymore.
Filelist => Media
The second-level Filelist module has been renamed to Media
to more accurately reflect its current functionality and scope.
Rationale: The former "Filelist" no longer reflected what the module
actually does. Over the years, its scope has evolved from simply listing files
to offering a full set of media-management capabilities. Today, the module is
used to upload and create files and folders, manage metadata, organize assets,
handle online media, and prepare files for use across the CMS.
To make its purpose clearer and more intuitive for editors and integrators,
the module has been renamed to "Media". The new name better represents its
broader functionality, aligns with modern CMS terminology, and makes the
module easier to understand for new users.
Migration: Since the module is already internally referred to as media_management,
no changes in referencing modules are required.
Sites => Setup
The second-level Sites module has been renamed to Setup.
Rationale: The old submodule name "Sites" would duplicate the new top-level
name and cause confusion. The new name "Setup" therefore makes the purpose
clearer: It is the place where integrators set up their sites. "Setup"
emphasizes the technical nature of the module and better communicates that
this section defines behavior (languages, domains, routes), not content.
Migration: Since the module identifier site_configuration is kept, no
changes in referencing modules are required.
Settings => Setup
The second level Settings module has been integrated into Setup.
Rationale: Combining the "Setup" and "Settings" gives a more concise view,
since managing sites and site settings are often done as one task.
Migration: The module identifier site_settings has been removed, the existing
actions
edit,
save and
dump have been renamed to
editSettings,
saveSettings and
dumpSettings as part of the
site_configuration module identifier.
Link Management
A new second-level Link Management module has been introduced
under the Sites top-level menu to provide a unified location
for managing URL-related features.
This new module serves as a parent for two third-level modules:
Redirects - The existing redirects module for managing URL redirects
Rationale: Grouping these URL management features under a common parent module
creates better organization and discoverability. Both redirects and QR codes deal
with URL handling and link management, making them natural companions in the
module structure. The new parent module provides a logical home for current and
future URL-related functionality.
Migration: The existing Redirects module, previously a second-level
module under Site, has been moved to become a third-level module
under Sites > Link Management. The module identifier site_redirects
has changed to redirects. An alias ensures backward compatibility. Use the new
identifier when registering custom modules.
System > Backend Users => Administration > Users
The second-level System > Backend Users module has been renamed
to Administration > Users to better match its scope.
It has also been moved to the top of the to Administration top level
menu as it is frequently used by administrators.
Rationale: The new name "Users" is shorter and easier to recognize in the
module menu. While "Backend Users" was technically precise, the simpler term
improves readability and usability, making the module easier to find for
administrators performing common user management tasks.
Migration: The identifier backend_user_management is kept unchanged, no
migration needed.
System > DB Check => System > Database
The second-level DB Check module has been renamed to
Database to better reflect its purpose.
Rationale: The name "Database" is clearer and avoids the ambiguity of the
abbreviation "DB". The module now focuses solely on search and query
functionality and no longer performs database integrity checks.
Migration: The module identifier has changed from system_dbint to
system_database. An alias ensures backward compatibility. Use the new
identifier when registering custom modules.
The module now exposes its actions "Search query" and "Raw query" through
the new submodule overview. This gives users clearer, detailed information
about the purpose of each action.
Impact
All renamed module identifiers maintain their previous names as aliases,
ensuring full backward compatibility. Existing code, configurations, and
third-party extensions continue to work without modification.
Developers are encouraged to update their code to use the new identifiers
for consistency and clarity.
The modernized naming improves the overall user experience by making the
backend more intuitive and easier to navigate, particularly for users
familiar with other enterprise CMS platforms.
Backend modules can now declare a dependency on their submodules using the new
appearance['dependsOnSubmodules'] configuration option. When enabled, a
module will automatically hide itself from the module menu if none of its
submodules are available to the current user.
This feature enables container modules to adapt dynamically to the user's
permissions and installed extensions, ensuring the module menu remains clean
and only displays modules that provide actual functionality.
In this example, the Content > Status module will only be shown in the
module menu if at least one of its submodules (Overview,
Translations) is available to the current user.
If all submodules are either disabled, removed, or the user lacks access
permissions to them, the parent module will automatically be hidden from the
module menu.
Impact
Module menus become more intuitive and user-focused. Container modules equipped
with
appearance['dependsOnSubmodules'] intelligently adapt to the current
context, appearing only when they offer actionable functionality to the user.
The Content > Status module leverages this feature to seamlessly disappear
from the module menu when extensions are uninstalled or when users lack
permissions to access its submodules, preventing dead-end navigation paths and
enhancing the overall backend experience.
Feature: #107668 - Improve scheduler task group handling and display
The scheduler module has been enhanced with improved visual organization and
quick editing capabilities for task groups. Task groups can now be assigned
custom colors to improve visual distinction, and the group name has been made
directly editable from the module view.
A new
color field has been added to the
tx_scheduler_task_group table, allowing administrators to assign hex
color values to each task group.
UI improvements
The scheduler module now provides several enhancements for task groups:
Color coding
Task groups can be assigned a color that is displayed as a left border on
the group panel, similar to the page module label. This makes it easy to
visually distinguish between different groups at a glance.
Quick edit link
The group name is now a clickable link that opens the group record for
editing, providing direct access to the group name, color, and description
fields without navigating through the list module.
Description display
Group descriptions are now displayed in the scheduler module directly
below the group name, providing additional context about the purpose of
each group.
Bold group names
Group names are now displayed in bold for better visual hierarchy and
readability in the scheduler module interface.
Implementation details
The color field is implemented as a TCA field of type
'color' with the
following characteristics:
Stores standard hex color values (for example
#FF8700).
Includes a value picker with 11 predefined colors aligned with the TYPO3
brand palette.
Predefined color options:
TYPO3 Orange (#FF8700)
White (#ffffff)
Gray (#808080)
Black (#000000)
Blue (#2671d9)
Purple (#5e4db2)
Teal (#2da8d2)
Green (#3cc38c)
Magenta (#c6398f)
Yellow (#ffbf00)
Red (#d13a2e)
These options can be customized by manipulating the
$GLOBALS['TCA']['tx_scheduler_task_group']['columns']['color']['config']['valuePicker']['items']
array in your TCA overrides file.
These enhancements improve the usability of the scheduler module,
particularly for installations with many task groups. The improvements allow
administrators to:
Quickly identify related task groups through visual color coding.
Edit group properties directly from the scheduler module.
Organize tasks by category, priority, or purpose.
See group descriptions without opening the group record.
Improve visual navigation in large scheduler configurations.
The feature is fully backward compatible. Existing task groups without colors
continue to work as before, displaying without a colored border.
Feature: #107679 - PSR-14 event for custom record retrieval in LinkBuilder
A new PSR-14 event
\TYPO3\CMS\Frontend\Event\BeforeDatabaseRecordLinkResolvedEvent
has been introduced to retrieve a record using custom code in the
\TYPO3\CMS\Frontend\Typolink\DatabaseRecordLinkBuilder .
The event is dispatched with
$record set to
null.
If an event listener retrieves a record from the database, it can assign the
record as an array to
$record. Doing so stops event propagation and skips
the default record retrieval logic in
\TYPO3\CMS\Frontend\Typolink\DatabaseRecordLinkBuilder .
Important
The event is stoppable: Setting
$record to a non-null value stops
propagation and disables the default record lookup.
Custom event listeners must handle all aspects normally performed by
DatabaseRecordLinkBuilder, such as record visibility, language overlay,
or version overlay, if relevant.
The event provides the following public properties (all read-only, except for
$record):
$linkDetails: Information about the link being processed.
$databaseTable: The name of the database table the record belongs to.
$typoscriptConfiguration: The full TypoScript link handler
configuration.
$tsConfig: The full TSconfig link handler configuration.
$request: The current request object.
$record: The database record as an array (initially
null).
Example
An example event listener could look like:
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Frontend\Event\BeforeDatabaseRecordLinkResolvedEvent;
finalclassMyEventListener{
#[AsEventListener(
identifier: 'my-extension/before-database-record-link-resolved',
)]
publicfunction__invoke(BeforeDatabaseRecordLinkResolvedEvent $event): void{
// Retrieve the record from the database as an array
$result = /* ... */;
if ($result !== false) {
// Setting the record stops event propagation and// skips the default record retrieval logic
$event->record = $result;
}
}
}
Copied!
Impact
This new event allows developers to implement custom record retrieval logic for
links created with typolink, for example to apply custom access restrictions or
fetch data from alternative sources before rendering a link.
Feature: #107683 - File storage tree items modification event and label support
Similar to the page tree functionality introduced in TYPO3 v12 and v13, the
file storage tree now supports modification of tree items through the new
PSR-14 event
\TYPO3\CMS\Backend\Controller\Event\AfterFileStorageTreeItemsPreparedEvent .
The event is dispatched in the file storage
TreeController after the
storage tree items have been resolved and prepared. It provides the current
PSR-7 request as well as the collection of file storage tree items.
Additionally, labels can now be added to file storage tree nodes via user
TSconfig, using the combined identifier of the folder:
EXT:my_extension/Configuration/user.tsconfig
options.folderTree.label.1:/campaigns/ {
label = Main Storage
color = #ff8700
}
Copied!
Important
Make sure to always use the full identifier, which also contains a
trailing slash (/).
Labels and status information
Similar to the page tree, labels and status information can be added to file
storage tree nodes. These features significantly improve the clarity and
accessibility of the file storage tree component:
Labels: Each node can support multiple labels, sorted by priority.
The highest priority label takes precedence, and only its marker is
rendered. All additional labels are added to the title attribute of the
node.
Status information: Can be added through the event to provide
additional visual feedback. Like labels, status information is sorted by
priority. Only the highest priority status indicator is displayed, while
all status labels are added to the title attribute.
<?phpdeclare(strict_types=1);
namespaceMyVendor\MyExtension\Backend\EventListener;
useTYPO3\CMS\Backend\Controller\Event\AfterFileStorageTreeItemsPreparedEvent;
useTYPO3\CMS\Backend\Dto\Tree\Label\Label;
useTYPO3\CMS\Backend\Dto\Tree\Status\StatusInformation;
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
#[AsEventListener(
identifier: 'my-extension/backend/modify-file-storage-tree-items',
)]
final readonly classModifyFileStorageTreeItems{
publicfunction__invoke(AfterFileStorageTreeItemsPreparedEvent $event): void{
$items = $event->getItems();
foreach ($items as &$item) {
// Add special label for storage with uid 1if ($item['resource']->getCombinedIdentifier() === '1:/campaigns/') { $item['labels'][] = new Label( label: 'A label', color: '#abcdef',
priority: 10,
);
$item['statusInformation'][] = new StatusInformation(
label: 'An important information',
severity: ContextualFeedbackSeverity::INFO,
priority: 10,
icon: 'content-info',
);
}
}
$event->setItems($items);
}
}
Copied!
Note
The combined identifier used in TSconfig must not be URL-encoded.
For example, use
1:/ instead of
1%3A%2F.
Impact
It is now possible to modify the prepared file storage tree items before they
are returned by the
TreeController ,
using the new PSR-14 event
AfterFileStorageTreeItemsPreparedEvent .
Additionally, labels can be assigned to file storage tree nodes via user
TSconfig.
Using these functionalities helps provide visual cues and improved
accessibility for editors working with file storages and folders.
Feature: #107697 - Unified file creation in Element Browser
The file creation functionality in the TYPO3 backend has been significantly
improved and unified to provide a consistent user experience. Previously,
creating new files required navigating to a separate page, which resulted
in context loss and an inconsistent interface compared to folder creation.
File creation is now fully integrated into the Element Browser pattern,
similar to how folder creation works. This change brings a consistent,
modern, and more intuitive workflow to file management.
Key improvements
Unified interface
File creation now uses the same modal-based Element Browser interface as
folder creation, providing a consistent look and feel across all file
operations.
Single entry point
The new New File button provides access to all file creation methods:
file upload with drag-and-drop, online media (YouTube, Vimeo), and text
file creation — all in one unified interface.
Context preservation
The modal interface keeps users in context with the file storage tree
visible, eliminating the need to navigate away from the current view.
Advanced upload handling
The new implementation uses the drag-uploader component, which provides
sophisticated duplication handling (replace, rename, skip, or use
existing), replacing the previous simple "override existing" checkbox.
File extension filtering
File creation forms are automatically filtered based on allowed file types
and hidden when no valid file extensions are available for the current
context.
Updated button labels
For consistency and clarity, button labels have been updated from
Create Folder / Create File to New Folder / New File, better
reflecting the nature of the actions.
How to use
In the File List module, click the New File button in the document
header toolbar to open a modal dialog. This modal provides three ways to add
files:
Upload files - Click Select & upload files to choose files.
The drag-and-drop uploader supports advanced duplication handling,
allowing you to choose whether to replace, rename, skip, or use existing
files when conflicts occur.
Add online media - Enter a URL from supported online media platforms
(YouTube, Vimeo) to add media files directly from the web.
Create text file - Create a new empty text file with supported
extensions (based on system configuration).
The modal keeps the file storage tree visible, allowing users to navigate
between folders without losing context. All three options are available in a
single, unified interface, streamlining the file creation workflow.
The previous Upload file button has been removed.
Impact
The unified file creation interface provides a significantly improved user
experience through better context preservation, advanced duplication handling,
and a consistent modal-based workflow.
Editors benefit from having all file creation options in one place, eliminating
the need to navigate between different pages for different file operations.
The modal-based approach keeps the file storage tree visible, making it easier
to work with files across multiple folders within a single, continuous
workflow.
Feature: #107710 - Support for XLIFF 2.x translation files
TYPO3 now supports both XLIFF 1.2 and XLIFF 2.x translation file formats.
The XLIFF loader automatically detects which version is used and parses the
file accordingly, making the transition seamless for integrators and extension
authors.
XLIFF (XML Localization Interchange File Format) is an XML-based format for
storing translatable content. While TYPO3 has traditionally used XLIFF 1.2,
the XLIFF 2.x standard brings improvements in structure and simplification.
Version detection
The XLIFF loader automatically detects the file version by examining:
The XML namespace (urn:oasis:names:tc:xliff:document:2.0 for XLIFF 2.0)
The version attribute in the root element
No configuration or manual intervention is required - both formats work
transparently side by side.
Key differences between XLIFF 1.2 and XLIFF 2.x
For integrators working with translation files, here are the main structural
differences:
XLIFF 1.2 structure:
Uses <trans-unit> elements directly within <body>
Translation approval via approved attribute (yes/no)
TYPO3's requireApprovedLocalizations configuration is respected for both
formats:
XLIFF 1.2: Translations with approved="no" are skipped when approval
is required
XLIFF 2.x: Translations with state="initial" or state="translated"
are treated as not approved, while state="final" and state="reviewed"
are considered approved
Impact
Extension authors and integrators can now use either XLIFF 1.2 or XLIFF 2.x
format for their translation files. Existing XLIFF 1.2 files continue to work
without any changes, while new projects can leverage the more modern XLIFF 2.x
standard.
This also improves compatibility with modern translation tools and services
that have adopted the XLIFF 2.0 standard.
Backend modules can now display a card-based overview of their submodules
instead of automatically redirecting to the first available submodule. This
new
showSubmoduleOverview configuration option enables a more
user-friendly navigation experience, similar to the Install Tool's maintenance
card layout.
When enabled, clicking on a second-level module displays an overview page
with cards for each accessible submodule. Each card shows the module's icon,
title, description, and an "Open module" button, allowing users to make an
informed choice about which submodule to access.
In this example, the Content > Status module displays a card-based
overview showing both Pagetree Overview and
Localization Overview submodules. Users can read the description
of each module before deciding which one to open.
The feature works seamlessly with the existing module permission system.
Only submodules that the current user has access to are displayed in the
overview. If no accessible submodules exist, a helpful information message is
shown instead of an empty page.
The
showSubmoduleOverview option modifies the behavior in several key
areas:
Module routing - When set to
true, the module's default route
targets
SubmoduleOverviewController
instead of automatically redirecting to the first available submodule.
Middleware behavior - The
BackendModuleValidator middleware
skips its automatic submodule redirection logic when this option is
enabled, allowing the overview page to be displayed.
Navigation enhancement - The system provides automatic navigation
capabilities:
The
SubmoduleOverviewController
displays the submodule jump menu, allowing quick access to all
available submodules.
When
showSubmoduleOverview is activated, the
ModuleTemplate
automatically adds a "Module Overview" menu item to the submodule
dropdown.
This allows users to easily navigate back to the overview from any
submodule, especially useful when a submodule does not manually
provide a "go back" button.
To provide meaningful descriptions on the overview cards, modules should
define a
description or
shortDescription in their labels
configuration. These are displayed in the card body to help users understand
each submodule's purpose.
Impact
Backend navigation becomes more intuitive and self-documenting. Container
modules using
showSubmoduleOverview provide users with a clear
overview of available functionality, eliminating the confusion of being
automatically redirected to an arbitrary first submodule.
The Content > Status module now uses this feature to present its
submodules in an accessible, visually organized manner. Users can quickly
understand what each submodule offers before navigating to it, improving
discoverability and user experience in the TYPO3 backend.
Feature: #107725 - Support username for authentication in Redis cache backend
Since Redis 6.0, it is possible to authenticate against Redis using both a
username and a password. Prior to this version, authentication was only
possible with a password. With this change, the Redis cache backend in TYPO3
now supports both authentication mechanisms.
You can configure the Redis cache backend as follows:
The
RedisBackend now supports
authentication using both a username and a password.
The
password configuration option is now typed as
array|string. Using an array for this configuration option is
deprecated and will be removed in TYPO3 v15.0.
Feature: #107755 - Add creation information for redirects
The redirects component has been extended with additional meta information
to improve audit trails and team collaboration. Redirects now automatically
capture and display:
Creation timestamp
Creating backend user
After months or even years, teams frequently wonder why a particular redirect
exists and who created it. Displaying the creator (backend user) and creation
date directly in the backend module makes auditing and communication
significantly easier.
Impact
All newly created redirects automatically include creation information,
regardless of whether they are created:
Manually through the redirects backend module
Automatically when updating a page slug
Programmatically through the DataHandler API
Existing redirects created before this feature will only show the creation
date, as the user information was not tracked previously.
The Fluid
<f:translate> ViewHelper now supports the new
translation domain syntax,
providing a more concise and readable way to reference translation labels.
A new domain attribute has been added that accepts both traditional
extension names and the new translation domain names.
The ViewHelper now supports multiple ways to specify translations:
Existing code using
extensionName continues to work without changes.
However, new code should prefer the
domain attribute combined with
translation domain syntax for better readability.
The
<f:translate> ViewHelper now provides a more convenient and
readable way to reference translations using the new translation domain
syntax. This reduces verbosity in Fluid templates and aligns with modern
translation system conventions used in Symfony and other frameworks.
All existing syntax forms remain fully supported, ensuring backward
compatibility. The new syntax can be adopted incrementally within a project,
and both old and new forms can coexist in the same template.
Feature: #107783 - Register metadata extractors via interface
Metadata extractors are service classes that are automatically executed
whenever an asset or file is added to the FAL
storage, or FAL indexing is executed.
Registration of metadata extractors now happens automatically when the required
interface
ExtractorInterface is
implemented by the class, utilizing autoconfigure tagging provided by the
Symfony Dependency Injection framework.
No further manual registration is required.
Additionally, the class
ExtractorRegistry now uses strong
type declarations, which should not affect public consumers. The interface
remains unchanged in its type declarations.
Impact
Instances of
ExtractorInterface are now detected
and registered automatically.
Feature: #107784 - Autoconfigure backend layout data providers
Backend layout providers are now autoconfigured once they implement the required
DataProviderInterface . Each
autoconfigured layout provider is tagged with
page_layout.data_provider in the service container and is automatically added
to the global
DataProviderCollection ,
if autoconfiguration is enabled in Services.yaml or
Services.php.
Since backend layout providers must be identifiable to establish a relation to
a configured backend layout, the corresponding interface has been extended.
It now requires backend layout providers to implement a new method
getIdentifier().
Backend layout data providers are now automatically registered and can be used
without further configuration. This improves developer experience and reduces
configuration overhead. The previous registration method via
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider']
can no longer be used. Instead, existing backend layout providers must
implement the new method
getIdentifier().
Using the new autoconfigure-based approach, developers can still support
multiple TYPO3 Core versions by keeping the legacy array-based approach next
to the new autoconfigure-based configuration.
The Administration > Reports module has been refactored to use
TYPO3 Core’s native submodule system with the showSubmoduleOverview feature
instead of a custom report registration mechanism. This provides a more
consistent user experience and aligns the Reports module with
other TYPO3 backend modules.
The module now displays a card-based overview of available reports, similar to
other modules like the Content > Status module. Each report is registered
as a proper backend submodule in Configuration/Backend/Modules.php.
Note
The top-level backend modules and some second level modules were renamed
in TYPO3 v14.
The Administration > Reports module now provides a cleaner and more
intuitive interface using standard TYPO3 backend navigation patterns.
Extension developers can create custom reports by registering them as backend
submodules, using the same module registration mechanisms already available in
TYPO3, instead of implementing a separate custom report interface.
Feature: #107794 - Improved breadcrumb navigation in backend
The TYPO3 backend now provides contextual breadcrumb navigation in the document
header of backend modules, helping users understand their current location and
navigate back through hierarchies.
Breadcrumbs are automatically displayed when:
Editing records (pages, content elements, etc.)
Creating new records
Browsing file storages and folders
Working with multiple records simultaneously
The breadcrumb navigation includes the following features:
Smart context detection
Breadcrumbs automatically adapt based on what you are working with —
whether it is a page, content element, file, or folder.
Hierarchical navigation
Click any breadcrumb item to navigate back to that level in the hierarchy.
For pages, the complete page tree path is shown.
Module awareness
Breadcrumbs remember which module you are in and keep you in that module
when navigating (for example, staying in the Info module instead of
switching to the Page module).
Route preservation
When navigating through breadcrumbs, the current module action or sub-route
is preserved (for example, remaining in edit view when clicking parent
pages).
Responsive design
On smaller screens, breadcrumb items automatically collapse into a dropdown
to save space while maintaining full functionality.
Impact
Backend users benefit from improved navigation and orientation:
Always know where you are: The breadcrumb trail shows your current
location in the page tree, file system, or record hierarchy.
Quick navigation: Jump back to any parent level with a single click
instead of using the browser’s back button or tree navigation.
Context preservation: Stay within your current module when navigating
through parent items.
Special states visible: When creating new records or editing multiple
items, this is clearly indicated in the breadcrumb trail.
Examples
Page editing
When editing page Contact in a site structure like Home → Company →
Contact, the breadcrumb shows: Home → Company → Contact
Content creation
When creating a new content element on page About, the breadcrumb shows:
Home → About → Create New Content Element
File management
When browsing fileadmin/images/products/ the breadcrumb shows:
fileadmin → images → products
For extension developers
Setting basic breadcrumb context
Custom backend modules can integrate breadcrumb navigation using new
convenience methods on
DocHeaderComponent :
// For page-based modules
$view->getDocHeaderComponent()->setPageBreadcrumb($pageInfo);
// For record editing
$view->getDocHeaderComponent()->setRecordBreadcrumb('tt_content', 123);
// For file/folder browsing
$view->getDocHeaderComponent()->setResourceBreadcrumb($file);
Copied!
These methods automatically generate appropriate breadcrumb trails including:
Page tree hierarchy for page-based modules
Parent pages for content records
Folder structure for file resources
Module hierarchy for third-level modules
Adding suffix nodes for special states
The
addBreadcrumbSuffixNode() method allows appending custom breadcrumb
nodes after the main breadcrumb trail. This is useful for indicating special
states or actions such as:
“Create New” actions when creating records
“Edit Multiple” states when editing multiple records
Custom contextual information specific to the current view
Example: Adding a "Create New" suffix node
useTYPO3\CMS\Backend\Dto\Breadcrumb\BreadcrumbNode;
$view = $this->moduleTemplateFactory->create($request);
$docHeader = $view->getDocHeaderComponent();
// Set main breadcrumb context (for example current page)
$docHeader->setPageBreadcrumb($pageInfo);
// Add suffix node for "Create New" action
$docHeader->addBreadcrumbSuffixNode(
new BreadcrumbNode(
identifier: 'new',
label: 'Create New Content Element',
icon: 'actions-add'
)
);
Copied!
Example: Multiple suffix nodes
useTYPO3\CMS\Backend\Dto\Breadcrumb\BreadcrumbNode;
$docHeader = $view->getDocHeaderComponent();
$docHeader->setRecordBreadcrumb('pages', $pageUid);
// First suffix: editing mode
$docHeader->addBreadcrumbSuffixNode(
new BreadcrumbNode(
identifier: 'edit',
label: 'Edit',
icon: 'actions-document-open'
)
);
// Second suffix: specific field
$docHeader->addBreadcrumbSuffixNode(
new BreadcrumbNode(
identifier: 'field',
label: 'Page Properties'
)
);
Copied!
Example: Clickable suffix nodes
Suffix nodes can also be clickable by providing a URL:
The new Integrations module has been introduced in the TYPO3
backend under the Administration section. This module serves as
the central hub for connecting TYPO3 with external systems and third-party
services.
The module uses the card-based submodule overview feature to provide an intuitive and organized
interface for managing different types of integrations. At present, it
consolidates the following existing modules as third-level submodules:
Webhooks - Manage outgoing HTTP webhooks to external systems
Reactions - Manage incoming HTTP webhooks from external systems
The Integrations module uses a three-level hierarchy structure:
Administration (main module)
Integrations (second-level parent module with card-based overview)
Webhooks / Reactions (third-level modules)
Note
The top-level backend modules were renamed in TYPO3 v14.
The module now called Administration was formerly named
System, and the module now called System was formerly
named Admin Tools.
For details, see:
Feature: #107628 – Improved backend module naming and structure.
Module navigation
The third-level modules (Administration > Integrations > Webhooks
and :guilabel:`Administration > Integrations > Reactions) now
include:
Doc header module menu - Quick navigation dropdown to switch between
submodules or return to the Integrations overview
Go back button - Direct link to return to the Integrations overview
Backward compatibility
The existing module identifiers continue to work through aliases:
webhooks_management redirects to
integrations_webhooks
system_reactions redirects to
integrations_reactions
Impact
The new Administration > Integrations module provides a centralized
location for managing all types of external system integrations in TYPO3. This
improves backend organization and user experience by grouping related
functionality together.
The module is designed to be extensible, allowing future integration types,
such as translation services, AI platforms, or other external tools, to be
added as additional third-level modules within the Integrations hub.
Feature: #107812: Setting to restrict Latest changed Pages Widget to current user's changes
Building upon the configurable dashboard widgets functionality introduced in
Feature: #107036 - Configurable dashboard widgets, the existing Latest changed Pages widget
can now be configured to display only pages that have been changed by the
current TYPO3 user.
Only show my changes
Impact
TYPO3 users can use the Latest changed Pages widget to quickly resume
their recent work.
Existing setups are unaffected unless this option is explicitly enabled.
Feature: #107823 - ComponentFactory for backend components
A new
\TYPO3\CMS\Backend\Template\Components\ComponentFactory
class has been introduced as the central location for all backend component
creation. It provides factory methods for buttons and menu components,
offering both pre-configured buttons for common patterns and basic component
creation methods.
The
ComponentFactory serves multiple purposes:
Pre-configured common buttons – Ready-to-use buttons like back,
close, save, reload, and view with standardized icons, labels, and
behavior.
Basic button creation – Factory methods for creating button
instances (previously available only on
ButtonBar).
Menu component creation – Factory methods for creating Menu and
MenuItem instances (previously available only on
MenuRegistry and
Menu).
The deprecated
ButtonBar::make*(),
Menu::makeMenuItem(),
and
MenuRegistry::makeMenu() methods have been replaced by
ComponentFactory, providing a cleaner separation of concerns where
container classes manage organization and
ComponentFactory handles
component creation.
Additionally, several "add" methods now support fluent interface patterns to
enable method chaining for improved code readability.
Database record list and file list
The
\TYPO3\CMS\Backend\RecordList\DatabaseRecordList and
\TYPO3\CMS\Filelist\FileList classes, which are responsible in the
backend to show all lists of records and files, now make use of the
ComponentFactory. The list of "action buttons" is no longer represented with
plain HTML and allows a clear distinction of primary and secondary actions.
For this, the new enum
\TYPO3\CMS\Backend\Template\Components\ActionGroup and
\TYPO3\CMS\Backend\Template\Components\ComponentGroup are also added to
the Button API to allow this grouping.
The existing PSR-14 events (
\TYPO3\CMS\Backend\RecordList\Event\ModifyRecordListRecordActionsEvent ,
\TYPO3\CMS\Filelist\Event\ProcessFileListActionsEvent ) which are used
within these classes have been streamlined to deal with these API changes.
Details can be found in the related breaking changes document of forge#107884.
The Button API has also been enhanced to allow passing a new
\TYPO3\CMS\Backend\Template\Components\Buttons\ButtonSize enum to
differentiate the buttons for certain icon sizes.
Available Factory Methods
The
ComponentFactory provides two categories of methods:
Pre-configured common buttons:
These methods provide ready-to-use buttons with sensible defaults. The
returned instances are fully mutable and can be further customized using
fluent interface methods (for example,
setDataAttributes(),
setClasses(),
setIcon()).
URL parameters accept both
string and
UriInterface for
convenience.
createBackButton(string|UriInterface $returnUrl) – Standard back
navigation with "Go back" label.
createCloseButton(string|UriInterface $closeUrl) – Close button for
modal-like views.
createSaveButton(string $formName = '') – Standard save button for
forms.
createReloadButton(string|UriInterface $requestUri) – Reload current
view.
createViewButton(array $previewDataAttributes = []) – View/preview
page button with data attributes.
Basic button creation:
createLinkButton() – Creates a new LinkButton instance.
createInputButton() – Creates a new InputButton instance.
createGenericButton() – Creates a new GenericButton instance.
createSplitButton() – Creates a new SplitButton instance.
createDropDownButton() – Creates a new DropDownButton instance.
createFullyRenderedButton() – Creates a new FullyRenderedButton
instance.
createShortcutButton() – Creates a new ShortcutButton instance.
createDropDownDivider() – Creates a new DropDownDivider instance.
createDropDownItem() – Creates a new DropDownItem instance.
Note
All button instances extending the
AbstractButton class support
arbitrary tag attributes via
setAttributes().
Menu component creation:
createMenu() – Creates a new Menu instance.
createMenuItem() – Creates a new MenuItem instance.
The Button API has also been enhanced to allow passing a new enum to
differentiate the buttons for certain icon sizes.
Improvements to Button API types
The following button types can use
getSize() and
setSize()
methods in their instance to set the icon size with the
\TYPO3\CMS\Backend\Template\Components\Buttons\ButtonSize enum,
choosing between a small and medium variant (utilizing CSS classes
internally):
Backend module developers should now inject
ComponentFactory in their
controllers to create buttons. The factory provides:
Pre-configured buttons for common patterns (back, close, save, reload,
view).
Basic button creation methods (formerly only available on
ButtonBar).
The
ButtonBar::make*() methods continue to work but are deprecated and
will be removed in TYPO3 v15. This change provides a cleaner architecture where
ComponentFactory
handles all button creation and
ButtonBar focuses
solely on managing button positioning and organization.
Example – Using pre-configured buttons (inject ComponentFactory):
usePsr\Http\Message\ResponseInterface;
useTYPO3\CMS\Backend\Template\Components\ButtonBar;
useTYPO3\CMS\Backend\Template\Components\ComponentFactory;
// In controller constructorpublicfunction__construct(
protected readonly ComponentFactory $componentFactory,
){}
// In controller actionpublicfunctioneditAction(): ResponseInterface{
$buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
// Use pre-configured back button
$backButton = $this->componentFactory->createBackButton($returnUrl);
$buttonBar->addButton($backButton, ButtonBar::BUTTON_POSITION_LEFT, 1);
// Use pre-configured save button
$saveButton = $this->componentFactory->createSaveButton('editform');
$buttonBar->addButton($saveButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
// ...
}
Copied!
Example – Creating basic buttons via
ComponentFactory :
These changes provide a more fluent API while maintaining backward
compatibility, as the return values were previously ignored (
void).
Design Rationale
Why fluent interface instead of named parameters?
The ComponentFactory intentionally uses a fluent interface approach (chained
method calls) rather than accepting parameters in factory methods. This design
decision was made for several important reasons:
Consistency with TYPO3 patterns
The fluent interface pattern is well-established throughout TYPO3's codebase
and familiar to extension developers. Introducing a different pattern here
would be inconsistent with the rest of the framework.
Diverse button types with different properties
Different button types have vastly different configuration requirements. For
example, InputButton needs name/value/form attributes, LinkButton needs href
attributes, DropDownButton needs items, and SplitButton needs primary and
secondary actions. A unified parameter-based approach does not fit this
diversity well and would lead to confusing method signatures with many
optional parameters.
Pre-configured buttons solve common cases
The factory already provides pre-configured methods like
createSaveButton(),
createBackButton(),
createCloseButton(),
createReloadButton(), and
createViewButton() that handle the most common use cases with minimal
code.
Avoids duplication and maintenance burden
A parameter-based approach would require duplicating all button-specific
configuration knowledge in both the button classes and the factory methods.
This creates a maintenance burden where changes to a button's properties must
be reflected in multiple locations.
Keeps factory simple and maintainable
By keeping factory methods focused on instantiation, the ComponentFactory
remains simple, maintainable, and easy to extend. Each button class maintains
complete ownership of its own configuration logic.
Fluent interface is already concise
// Common case – use pre-configured button
$saveButton = $this->componentFactory->createSaveButton('myform');
// Custom case - fluent interface is clear and flexible
$customButton = $this->componentFactory->createInputButton()
->setName('custom_action')
->setValue('1')
->setTitle('Custom Action')
->setIcon(
$this->iconFactory->getIcon('actions-heart', IconSize::SMALL)
);
Copied!
This design ensures the API remains maintainable, consistent with TYPO3
conventions and suitable for the diverse requirements of different button types
while still providing convenience methods for common patterns.
Backend avatar providers must either use the PHP attribute
#[AsAvatarProvider] or be manually tagged in the service container with
backend.avatar_provider.
When autoconfiguration is enabled in Services.yaml or
Services.php, applying
#[AsAvatarProvider] will automatically add
the
backend.avatar_provider tag. Otherwise, the tag must be configured
manually.
Backend avatar providers are now automatically registered using the PHP
attribute
#[AsAvatarProvider]. This improves the developer experience and
reduces configuration overhead. The previous registration method via
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['avatarProviders'] can
no longer be used.
To support multiple TYPO3 Core versions simultaneously, extensions may still
implement the legacy array-based registration alongside the new
autoconfiguration-based approach.
A new Bookmarks dashboard widget has been added to display the current user's
bookmarks directly in the TYPO3 Dashboard. This allows editors and administrators
to quickly access frequently used pages, records, or modules without navigating
through the backend menu.
The TYPO3 backend document header (DocHeader) has been reorganized to
provide a more consistent and intuitive user experience across all
backend modules.
This feature includes the following improvements:
Unified language selector
All backend modules with multi-language support now use a consistent
language selector in the top-right area of the DocHeader. This includes:
Content > Layout
Content > List
Content > Preview
Record editing (FormEngine)
Note
Some backend modules were renamed in TYPO3 v14.
The top-level module now called Content was formerly named
Web.
The module Content > Layout was formerly called
Web > Page.
The language selector displays the currently selected language as the
button text (for example "English", "German", and the special All
languages) with a descriptive "Language:" prefix for screen readers.
This provides immediate visual feedback about the active language while
maintaining full accessibility.
The language selector now allows creating new page translations directly
from the dropdown (except in Preview mode). The dropdown is organized
with existing translations shown first, followed by a divider and a
"Create new translation" section header. Languages that do not yet have
a translation appear in this separate section.
Selecting a language from the "Create new translation" section will:
Create the page translation via the DataHandler.
Open the FormEngine to edit the newly created translation.
Provide a consistent workflow across all modules.
Unified module actions menu
Modules with submodules or multiple "actions" now consistently display these
options as a dropdown button in the DocHeader button bar. The dropdown button
displays the currently active module or action as the button text, providing
clear visual context to users.
For accessibility, each dropdown includes a descriptive label:
"Display mode" for the Content > Layout module view selector
(layout vs language comparison)
"Module actions" for general module or submodule navigation
Custom labels defined by individual menu configurations
These labels are implemented using visually hidden label elements, ensuring
screen readers announce both the dropdown's purpose (for example,
"Display mode:") and the current selection (for example, "Layout").
The dropdown is automatically hidden when only a single action is available,
reducing visual clutter and showing the dropdown only when navigation choices
are actually available.
Examples include Administration > Users with Overview, Users,
Groups actions, as well as Content > Status with "Pagetree
Overview", "Localization Overview", and more.
This replaces the previous inconsistent mixture of:
Back buttons
Inline button groups
Module menus in different locations
Reorganized DocHeader layout
The DocHeader now has a more logical structure:
Top row (navigation bar):
Left side: Breadcrumb navigation showing the current location
Right side: Language selector (when applicable)
Second row (button bar):
Left side, group 0: Module actions dropdown (when applicable)
Left side, groups 1+: Additional action buttons and context-specific
buttons (Save, Close, and so on)
Right side: Functional buttons such as "Reload", "Bookmark", or
"Clear cache"
Technical details
Module actions dropdown
Controllers can use the existing
ModuleTemplate::makeDocHeaderModuleMenu() method to automatically
create a module actions dropdown based on the module configuration:
Then build the language selector dropdown with language items:
// Get available site languages for the current page
$siteLanguages = $this->site->getAvailableLanguages($pageRecord, $backendUser);
$currentLanguageId = (int)$moduleData->get('language');
// Build dropdown button
$languageDropdown = $this->componentFactory->createDropDownButton()
->setLabel($languageService->sL('core.core:labels.language'))
->setShowLabelText(true)
->setShowActiveLabelText(true);
// Create dropdown items for each languageforeach ($siteLanguages as $siteLanguage) {
$languageId = $siteLanguage->getLanguageId();
$isActive = $currentLanguageId === $languageId;
// Build URL for language selection
$href = $this->uriBuilder->buildUriFromRoute('web_layout', [
'id' => $pageId,
'language' => $languageId,
]);
$item = $this->componentFactory->createDropDownRadio()
->setHref($href)
->setLabel($siteLanguage->getTitle())
->setIcon(
$this->iconFactory->getIcon($siteLanguage->getFlagIdentifier())
)
->setActive($isActive);
$languageDropdown->addItem($item);
}
$view->getDocHeaderComponent()->setLanguageSelector($languageDropdown);
Copied!
The language selector component is automatically rendered in the top-right area
of the DocHeader navigation bar.
Note
For advanced implementations that include "Create new translation"
functionality, refer to the
PageLayoutController in EXT:backend.
The controller demonstrates how to separate existing translations from
languages that can be created, using
DropDownDivider and
DropDownHeader components to organize the dropdown menu.
Accessibility implementation
The
setShowActiveLabelText(true) method enables display of the active
item's label while maintaining accessibility. When enabled with
setShowLabelText(true), the button renders the dropdown's label in a
visually hidden span followed by the active item's label:
When
setShowLabelText(false), the accessibility information is provided
through aria-label and title attributes instead.
This pattern applies to all dropdown buttons in the DocHeader:
$dropdown->setLabel('Display mode') // Descriptive label for context
->setShowLabelText(true) // Show label text
->setShowActiveLabelText(true); // Show active item label
Copied!
Impact
The changes provide a more consistent and intuitive user experience. Users can
now create and switch between page translations directly from the DocHeader
across all relevant modules, without needing to navigate to specific page areas.
The clear separation between existing translations and languages that can be
created improves discoverability and reduces confusion about which actions are
available.
The language selector and module actions dropdowns now display the currently
active selection as the button text, providing immediate visual feedback. Users
can see at a glance which language is selected or which module action is active,
without needing to open the dropdown.
Accessibility has been significantly improved through the use of descriptive
labels rendered as visually hidden elements or aria-label attributes. Screen
reader users now receive proper context about each dropdown's purpose
(e.g., "Language:", "Display mode:", "Module actions") combined with the
current selection, ensuring equal access to navigation functionality.
The module actions dropdown makes it immediately clear which module or action is
currently active, and which alternatives are available. The dropdown is only
shown when multiple options exist, reducing visual clutter. Removal of redundant
back buttons in favor of consistent breadcrumb navigation further reduces visual
noise.
As part of centralizing and improving the processing of items for select,
check, and radio type fields, a new TCA option
itemsProcessors has been introduced as a replacement for
itemsProcFunc. This option is an array, allowing any number of
processors to be called instead of just one. Processors are ordered by their
array key (numerical) and executed in that order. Processors can receive
arbitrary data through the
$context->processorParameters property.
All processors must implement the
ItemsProcessorInterface interface.
Processor methods receive two parameters:
a
SelectItemCollection instance
containing the items, and an
ItemsProcessorContext instance
providing access to table, field, row data, and configuration. The processor
must return a
SelectItemCollection. This means that added items can no
longer be untyped arrays, making the entire process cleaner and safer.
A new Page TSconfig option is also available, mirroring the existing one for
itemsProcFunc. See the example below for syntax.
The
$context->processorParameters property contains any parameters
defined in the TCA declaration.
Using Page TSconfig to pass custom parameters to the processor would look like
this:
TCEFORM.example_table.content.itemsProcessors.100.foo = bar
Copied!
Note that the numerical key of the processor must be reused.
With this setup, the class
SpecialRelationsProcessor receives the PHP
array
['foo' => 'bar'] in the
$context->fieldTSconfig property.
The class
SpecialRelationsProcessor2 receives an empty array
[] (since it is registered with the key
50).
Registration of processors is also possible within FlexForms:
It is still possible to use
itemsProcFunc, but switching to
itemsProcessors is recommended because it offers two main advantages:
Being an array, it allows extensions to add processors on top of existing
ones and to define their execution order.
The processing chain is strictly typed, ensuring safer and more reliable
code.
If both
itemsProcFunc and
itemsProcessors are defined,
both are executed, with
itemsProcFunc executed first.
Tip
The naming itemsProcessors, using a double plural form, was chosen for
two reasons. First, it complements the former plural form
itemsUserFunc. Second, multiple processors can be defined that operate
on the collection of all "items", not just on a single item.
Feature: #107953 - Add "Current logged-in users" filter to the "Backend Users" module
A new filter option Current logged-in users has been added to the
Administration > Users module. This feature allows
administrators to quickly list all backend accounts that are currently active.
The detection mechanism is based on the
lastlogin timestamp in
combination with the global configuration value
$GLOBALS['TYPO3_CONF_VARS']['BE']['sessionTimeout'] , which defines the
maximum lifetime of backend user sessions.
Note
The top-level backend modules were renamed in TYPO3 v14.
The module now called Administration was previously named
System, and the module now called System was
previously named Admin Tools.
For details, see:
Feature: #107628 – Improved backend module naming and structure.
Impact
Administrators can now filter backend users by those who are currently logged in,
providing an immediate overview of active sessions directly within the
Administration > Users module.
Feature: #108002 - Introduce built-in symmetric encryption/decryption cipher service
TYPO3 now provides a built-in symmetric encryption and decryption service using the
modern XChaCha20-Poly1305 AEAD (Authenticated Encryption with Associated Data) cipher.
This service allows extensions and core code to securely encrypt sensitive data such as
API tokens, passwords, or personal information without implementing custom encryption
solutions or relying on third-party libraries.
Benefits
The new
\TYPO3\CMS\Core\Crypto\Cipher\CipherService provides several advantages:
Secure by default: Uses modern, cryptographically secure algorithms provided by
the libsodium library (ext-sodium), which is available by default in all PHP
versions currently supported by TYPO3.
Seamless integration: Secret keys can be derived from TYPO3's existing
$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] , eliminating the need
to manage additional encryption keys.
Authenticated encryption: The AEAD cipher not only encrypts data but also
ensures its integrity, preventing tampering.
Application-specific keys: Different components can derive isolated encryption
keys using seed values, enhancing security through key separation.
Key Derivation
Secret keys are derived from the existing TYPO3 encryption key using the
\TYPO3\CMS\Core\Crypto\Cipher\KeyFactory . This approach leverages the
master encryption key that is already configured in your TYPO3 installation, ensuring
that different applications or contexts can have their own isolated keys while using
the same master key.
Please note that to decrypt data in the future, the encryptionKey must not be
changed and must remain available.
Example for deriving a secret key:
useTYPO3\CMS\Core\Crypto\Cipher\KeyFactory;
// Get the KeyFactory via dependency injectionpublicfunction__construct(
private readonly KeyFactory $keyFactory
){}
// Derive an application-specific secret key from the TYPO3 encryption key// The seed 'my-extension-tokens' identifies this specific use case
$secretKey = $this->keyFactory->deriveSharedKeyFromEncryptionKey('my-extension-tokens');
// You can also derive multiple sub-keys from the same seed
$secretKey1 = $this->keyFactory->deriveSharedKeyFromEncryptionKey('my-extension-tokens', 1);
$secretKey2 = $this->keyFactory->deriveSharedKeyFromEncryptionKey('my-extension-tokens', 2);
Copied!
Each unique seed produces a different encryption key, allowing for key separation
between different features or data types within your extension.
Key Usage/Generation
Instead of deriving a key, it is also possible to generate a new key from a random
value or to use a provided key (e.g., via environment variables).
To encrypt sensitive data, use the
CipherService::encrypt() method:
useTYPO3\CMS\Core\Crypto\Cipher\CipherService;
useTYPO3\CMS\Core\Crypto\Cipher\KeyFactory;
// Get services via dependency injectionpublicfunction__construct(
private readonly CipherService $cipherService,
private readonly KeyFactory $keyFactory
){}
publicfunctionencryptApiToken(string $apiToken): string{
// Derive a secret key for API token encryption
$secretKey = $this->keyFactory->deriveSharedKeyFromEncryptionKey('api-tokens');
// Encrypt the API token
$cipherValue = $this->cipherService->encrypt($apiToken, $secretKey);
// Convert to string for storage in database// The result has the format: {base64url-encoded-json}// Example: "eyJub25jZSI6IlxcXHUwMDEy4oCmIiwiY2lwaGVyIjoi4oCmIn0"// This string contains both the nonce and ciphertext in a JSON structure
$encryptedString = (string)$cipherValue;
return $encryptedString;
}
Copied!
Each encryption generates a unique result due to the random nonce, even when
encrypting the same plaintext multiple times.
Decryption Example
To decrypt data, use the
CipherService::decrypt() method with the same secret
key that was used for encryption:
useTYPO3\CMS\Core\Crypto\Cipher\CipherService;
useTYPO3\CMS\Core\Crypto\Cipher\CipherValue;
useTYPO3\CMS\Core\Crypto\Cipher\KeyFactory;
useTYPO3\CMS\Core\Crypto\Cipher\CipherDecryptionFailedException;
// Get services via dependency injectionpublicfunction__construct(
private readonly CipherService $cipherService,
private readonly KeyFactory $keyFactory
){}
publicfunctiondecryptApiToken(string $encryptedToken): string{
// Derive the same secret key used during encryption
$secretKey = $this->keyFactory->deriveSharedKeyFromEncryptionKey('api-tokens');
// Parse the cipher text (format: {base64url-encoded-json})
$cipherValue = CipherValue::fromSerialized($encryptedToken);
try {
// Decrypt the token
$apiToken = $this->cipherService->decrypt($cipherValue, $secretKey);
return $apiToken;
} catch (CipherDecryptionFailedException $e) {
// Decryption failed - wrong key, tampered data, or invalid formatthrownew \RuntimeException(
'Failed to decrypt API token: ' . $e->getMessage(),
1762465682,
$e
);
}
}
Copied!
Decryption will fail if the wrong key is used, the data has been tampered with,
or the ciphertext format is invalid.
Impact
The
CipherService and
KeyFactory are now available as
autoconfigured services throughout TYPO3 core and extensions. This provides
a standardized, secure way to encrypt and decrypt sensitive data without
requiring additional dependencies beyond ext-sodium, which is already a
requirement of TYPO3 core.
Extensions can now securely store encrypted data in the database or configuration
files using this built-in service, ensuring consistent security practices across
the TYPO3 ecosystem.
Feature: #108008 - Automatic reload and shortcut buttons in backend modules
Backend modules now automatically get reload and shortcut buttons added to
their document header, ensuring consistent display across all backend modules.
Previously, controllers manually added these buttons with varying group
numbers, leading to inconsistent positioning. Now, buttons always appear on
the right side, ensuring they are always the last two buttons regardless of
what other buttons are added.
Controllers provide shortcut information using the new
DocHeaderComponent::setShortcutContext() method:
Buttons are automatically added during rendering before the PSR-14
ModifyButtonBarEvent is dispatched, allowing event listeners to modify
or remove them if needed.
Controllers can disable automatic buttons if custom behavior is required:
Reload and shortcut buttons now appear consistently at the same position
across all backend modules, providing a predictable user experience.
Controllers no longer need to manually create these buttons, reducing
boilerplate code. Use
DocHeaderComponent::setShortcutContext() to
provide shortcut information and remove manual button creation, see
Deprecation: #108008 - Manual shortcut button creation.
The Content > Layout and Content > List modules now
support selecting multiple languages simultaneously for improved comparison
of translated records.
The language selection state is synchronized between both modules, providing
a consistent experience when switching between page and list views.
A fallback mechanism is in place for switching between view modes (Layout
and Language Comparison) as well as navigating to a page not available
in the current language selection.
Note
The Content > Preview module has been migrated to the
new shared language API, too. Therefore, a selected language is also
kept when navigating to or from this module.
Note
Some backend modules were renamed in TYPO3 v14.
The top level module now called Content was formerly named
Web.
The module Content > Layout was formerly called
Web > Page.
It is now possible to define a type-specific
title in the TCA
types section. This value overrides the global table title defined in
ctrl['title'] for the respective record type.
This allows different record types of the same table to display different
titles in the TYPO3 backend user interface, making it clearer which kind of
record is being created or displayed.
Note
In addition to
title, the
previewRenderer property can also
be overridden per type. This was already possible through explicit evaluation
and use of the Schema API, but is now consistently respected by FormEngine
as well.
Implementation
This feature has been implemented in two areas to ensure consistent behavior:
Schema API
The
TcaSchemaFactory merges
type-specific configuration into sub-schemas using
array_replace_recursive(). This makes type-specific titles available
through
$schema->getTitle().
FormEngine
The data provider
TcaTypesCtrlOverrides
merges the type-specific
title and
previewRenderer into
processedTca['ctrl'] during form rendering. This ensures that both
FormEngine and any legacy code accessing
ctrl directly will see the
correct type-specific values.
return [
'ctrl' => [
'title' => 'my_extension.db:tx_my_table',
'type' => 'record_type',
// ... other ctrl configuration
],
'types' => [
'article' => [
'title' => 'my_extension.db:tx_my_table.type.article',
'showitem' => 'title, content, author',
],
'news' => [
'title' => 'my_extension.db:tx_my_table.type.news',
'showitem' => 'title, content, publish_date',
],
'event' => [
// No type-specific title - will use the global ctrl['title']'showitem' => 'title, content, event_date',
],
],
// ... columns configuration
];
Copied!
In this example:
The article type displays the title defined in the language file key
tx_my_table.type.article.
The news type displays the title defined in
tx_my_table.type.news.
The event type falls back to the global table title from
ctrl['title'].
Impact
Tables with multiple record types can now define more specific and descriptive
titles for each type in the backend user interface. This improves usability and
clarity for editors by making it immediately obvious which type of record is
being created or edited.
This feature is especially useful for:
Content element tables such as
tt_content with different
CType
values
Tables with distinct record types serving different purposes
Plugin records with varying functionality per type
Any table where the record type changes the record's purpose or meaning
A modernized and extensible translation workflow has been introduced in the
TYPO3 backend. The new architecture replaces the previous monolithic
localization implementation with a step-based wizard system that guides
editors through the translation process. Previously available only in the
Content > Layout module, the translation wizard is now the default interface
for all translation operations across backend modules.
The backend now consistently opens the new translation wizard whenever users
initiate a translation operation, providing a unified experience across all
modules. The wizard guides users through the translation process in multiple
steps, automatically advancing when no user input is required. This streamlined
approach ensures that users only interact with steps requiring configuration
or confirmation.
Users can choose between two translation modes: Translate (creates a
connected translation) and Copy (creates an independent copy in free mode).
To maintain consistency, the wizard only offers the translation mode that
matches existing translations for a record, preventing mixed modes.
Note
Some backend modules were renamed in TYPO3 v14.
The top level module now called Content was formerly named
Web.
The module Content > Layout was formerly called
Web > Page.
The localization handler and finisher APIs are currently marked as
@internal and may change before the LTS release. While it is
technically possible to implement custom localization handlers and finishers,
the APIs are still being evaluated and refined. Extension developers should
be aware that breaking changes to the interfaces may occur in minor releases
until the APIs are stabilized for LTS.
We are actively seeking feedback from extension developers to ensure that
the APIs meet real-world requirements and can be stabilized for the LTS
release. If you implement custom handlers or finishers, please share your
experience and suggestions with the TYPO3 community.
The new
LocalizationHandlerRegistry
provides a flexible architecture for registering and managing different
translation strategies. Handlers implement the
LocalizationHandlerInterface and
are automatically registered via autoconfiguration with the
backend.localization.handler tag. This makes the localization system
extensible, allowing custom handlers to be added for specialized translation
workflows, such as integration with translation services, AI-powered
translation, or custom business logic.
The
LocalizationFinisherInterface
defines how the wizard completes after a successful translation operation.
Finishers can be customized by localization handlers to provide the most
appropriate completion behavior for their workflow, such as redirecting to
a specific page or reloading the current view.
Custom localization handlers
Extensions can provide custom localization handlers by implementing the
LocalizationHandlerInterface :
namespaceMyVendor\MyExtension\Localization;
useTYPO3\CMS\Backend\Localization\LocalizationHandlerInterface;
useTYPO3\CMS\Backend\Localization\LocalizationInstructions;
useTYPO3\CMS\Backend\Localization\LocalizationMode;
useTYPO3\CMS\Backend\Localization\LocalizationResult;
finalclassMyCustomHandlerimplementsLocalizationHandlerInterface{
publicfunctiongetIdentifier(): string{
return'my-custom-handler';
}
publicfunctiongetLabel(): string{
return'my_extension.messages:handler.label';
}
publicfunctiongetDescription(): string{
return'my_extension.messages:handler.description';
}
publicfunctiongetIconIdentifier(): string{
return'my-extension-icon';
}
publicfunctionisAvailable(LocalizationInstructions $instructions): bool{
// Return true if this handler should be available for the given contextreturn $instructions->mainRecordType === 'my_table';
}
publicfunctionprocessLocalization(
LocalizationInstructions $instructions
): LocalizationResult{
// Implement custom localization logic// Return a LocalizationResult with success status and finisher
}
}
Copied!
The handler will be automatically registered via autoconfiguration when it
implements
LocalizationHandlerInterface .
No manual service configuration is required.
Custom finishers
Extensions can create custom finishers by implementing the
LocalizationFinisherInterface
and providing a corresponding JavaScript module to handle the frontend logic:
The corresponding JavaScript module implements the finisher logic. The finisher
is executed in the final step of the wizard and can define custom rendering and
behavior. Users can skip the finisher step if no interaction is required.
Important
If your finisher provides custom action buttons, bind the main action to the
execute() method. This ensures that the same action is performed both
when users click your custom button and when they click "Finalize" in the
wizard's bottom action bar.
import LocalizationFinisher from'@typo3/backend/localization/localization-finisher';
import { html, type TemplateResult } from'lit';
class MyCustomFinisher extends LocalizationFinisher {
public render(): TemplateResult {
// Define what is rendered in the finisher step// Can return custom UI elements, messages, actions, etc.return html`<p>${this.finisher.labels.successMessage}</p><button @click=${() => this.execute()}>
Perform Action
</button>
`;
}
publicasync execute(): Promise<void> {
// Execute finisher logic (e.g., redirect, reload, show notification)// This is called when the wizard completes or when the user clicks// the custom action button aboveconst customData = this.finisher.data.customData;
console.log('Finisher executing with data:', customData);
// Perform your custom finisher action here// For example: redirect, reload, show notification, etc.
}
}
exportdefault MyCustomFinisher;
Copied!
Impact
The new translation workflow provides a consistent and intuitive user experience
across all backend modules. Editors benefit from clear step-by-step guidance
through the translation process, with the wizard automatically adapting to the
context and showing only relevant options.
Extension developers can register custom localization handlers to integrate
specialized translation workflows, such as connections to external translation
services or AI-powered translation tools.
Feature: #108148 - Alternative Fluid syntax for CDATA sections
A long-standing issue in Fluid templates has been that the Fluid variable
and inline ViewHelper syntax collides with inlined CSS or JavaScript code.
This issue has now been addressed with Fluid 5: A new alternative syntax
has been introduced that makes collisions between CSS/JavaScript and Fluid
far less likely.
The normal inline and variable syntax uses single curly braces { } as
tokens in Fluid templates. In <![CDATA[ ]]> sections, this syntax is now
ignored. Instead, three curly braces {{{ }}} can be used to call Fluid
ViewHelpers or to access variables. The tag-based syntax is disabled
altogether in CDATA sections.
Note that it's still considered bad practice to put inline CSS or JavaScript
code in Fluid templates. Consider using a dedicated API endpoint, data-*
attributes or CSS custom properties (also known as CSS variables) to pass
dynamic values to JavaScript and CSS.
Impact
Inline CSS and JavaScript can now be wrapped in CDATA in Fluid templates,
which prevents syntax collision with Fluid's inline syntax.
Fluid 5 brings support for union types in ViewHelper argument definitions.
Previously, it was necessary to specify an argument as
mixed if more
than one type should be possible. Now it is possible to specify multiple
types separated by a pipe character (|).
The following built-in PHP types can also be used:
iterable
countable
callable
Example:
useTYPO3\CMS\Core\Resource\File;
useTYPO3\CMS\Core\Resource\FileReference;
useTYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
classMyViewHelperextendsAbstractViewHelper{
publicfunctioninitializeArguments(): void{
$this->registerArgument(
'file',
File::class . '|' . FileReference::class,
'a file object'
);
$this->registerArgument(
'items',
'iterable',
'a list of items'
);
}
}
Note that union types disable automatic type conversion by Fluid, so it
might be necessary to specify more types to keep ViewHelpers flexible.
Example:
$this->registerArgument(
'ids',
'array|string|int',
'a list of ids, either comma-separated or as array'
);
Copied!
Impact
Custom ViewHelper implementations have more options to specify an API with
strict type requirements and can avoid manual type checks.
Feature: #108166 - Fluid file extension and template resolving
Fluid 5 introduces a specific file extension for template files.
The extension is used in combination with a generic extension,
which means that existing syntax highlighting in code editors
still works.
Before
After
MyTemplate.html
MyTemplate.fluid.html
MyTemplate.xml
MyTemplate.fluid.xml
...
...
As stated in the Fluid release notes, this new file extension is
entirely optional. Existing template files will continue to work.
A fallback mechanism is in place, which checks for template files in
the following order:
This means that *.fluid.* files are preferred over files without the
new extension if both files exist in the same folder.
Another noteworthy change in Fluid is that template files no longer
need to start with an uppercase character: The user-provided spelling
of the template name will be tried first, the uppercase variant will be
used as a fallback.
Consequences for template overloading
If multiple template paths are provided (for example if an extension
overloads templates or partials of another extension), this new fallback
chain for template file names will be executed per template path.
In practice this means the following:
A TYPO3 community extension can ship *.html files (without the new
file extension), which can be overloaded by a sitepackage extension
that already uses *.fluid.html files.
A TYPO3 community extension can ship *.fluid.html files, which can
still be overloaded by a sitepackage extension that uses *.html
files.
MyTemplate.html will still overload MyTemplate.html like before,
and the same applies to *.fluid.html files.
However, since older Fluid versions do not consider *.fluid.* files,
it is not supported to use the new file extension in a TYPO3
community extension that still supports TYPO3 versions below 14.
For the TYPO3 Core this means that all template files can safely be
renamed to the new file extension because Fluid 5 is always present.
To overload templates from the TYPO3 Core, both the new Fluid file
extension and the old file extension can be used, which allows TYPO3
community extensions to remain compatible with multiple TYPO3 Core
versions.
Edge case: Templates with file extension specified
Note that the described file extension fallback chain only works
if the file extension is not specified explicitly, but rather derived
from the template's format. If the file extension is part of the requested
template name, Fluid can't reliably add the *.fluid.* file extension
automatically and the template needs to be adjusted.
One use case of this would be a template in format json that calls a
partial in format html:
MyTemplate.fluid.json
<!-- Won't work if template file is called MyTemplate.fluid.html: --><f:renderpartial="MyTemplate.html" /><!-- Needs to be adjusted like this: --><f:renderpartial="MyTemplate.fluid.html" />
Copied!
Impact
While the TYPO3 Core can already switch to the new *.fluid.* file
extension, TYPO3 community extensions will probably continue to use
*.html for an extended period. However, on a v14 project, the new
file extension can already be used, both for individual development and
for the integration of TYPO3 community extensions.
Projects and extension authors willing to switch to the new file
extension can use the fluid-rename
utility extension, which has already been used for the TYPO3 Core.
This new file extension opens new possibilities because it is now
easily recognizable which files will be interpreted by Fluid. This will
enable better IDE integration and tooling support in the future.
Feature: #108227 - Allow
#[IgnoreValidation] and
#[Validate] attributes for method parameters
The Extbase attributes
#[IgnoreValidation] and
#[Validate] can
now be used for controller action method parameters.
This extends the current validation behavior of these attributes, where either
(1) the complete method or (2) a single parameter is taken into account for
validation. While this works fine for (1), using these attributes in context
of (2) raises some concerns regarding
duplication of parameter names,
validation of the existence of a configured parameter, and
unnecessary complexity regarding reflection-based handling of these
parameters.
History
When the attributes were originally implemented as Doctrine annotations, the
only possible way to implement behavior (2) was to add the appropriate
annotation to the method-related annotation.
Since forge#107229, annotations are no longer used in Extbase, and PHP
attributes are the only remaining successor in this area. Since PHP attributes
support placement at specific method parameters, the existing attributes can
safely rewritten to be placed (1) at a specific method or (2) at a specific
method parameter.
Usage with method parameters
The capabilities of the existing attributes are expanded to allow placement at
method parameters as well. The previous behavior (defining validation-related
behavior at method level using the existing attribute properties) is now
deprecated and will be removed with TYPO3 v15
(see deprecation notice).
Impact
By allowing the usage of both
#[IgnoreValidation] and
#[Validate]
attributes at method parameter level, the previous error-prone behavior is
now hardened. In addition, this change improves developer experience and pushes
the Extbase ecosystem towards a modernized architecture.
All installations using these attributes on method level need to migrate these as
described in the related section of
Deprecation #108227 - Migration.
TYPO3 v14 leverages the revamped Fluid v5 warmup feature and integrates a
Fluid template warmup directly into the CLI
command
typo3 cache:warmup.
The command finds and compiles all *.fluid.* (for example
Index.fluid.html) files found in extensions.
Fluid warmup can also be called directly using
typo3
fluid:cache:warmup, which will additionally output compile time
deprecations found within Fluid template files.
Verbose output allows to get feedback of warmed up templates
and the number of errors/deprecations (or success).
Impact
The warmup command can be useful to reduce ramp up time after deployments.
The new Fresh theme has been introduced for the TYPO3 backend,
marking the beginning of broader backend customization capabilities. This theme
offers users a modern alternative appearance option with a friendlier purple
accent color palette.
Built on the modern CSS architecture, the "Fresh" theme is now the default for
new users. It complements the existing Modern and Classic themes. All three
themes will be maintained and enhanced equally going forward.
The extensive CSS framework improvements made during the 14.0 cycle now enable
easier theme adaptation with minimal code changes, setting the foundation for
the expanding theme system.
Theme selection
Users can select their preferred theme in the User Settings module
under the Backend appearance section. The "Fresh" theme provides a
more contemporary look and feel while maintaining the familiar TYPO3 backend
structure and usability.
All existing themes remain fully supported:
Fresh (default for new users) - Modern purple accent colors
Modern - Contemporary appearance with traditional accent colors
Classic - Traditional TYPO3 backend appearance
Future development
Additional customization options and modern user interface elements will be
incrementally added to the theme system leading up to the long term support
release. The modular CSS architecture ensures that all themes benefit from
these enhancements equally.
Important
The backend CSS framework is currently considered internal API. No public
CSS variables or settings have been defined yet that are guaranteed to
remain stable across future versions. Areas such as color palette
generation, accent colors, stateful colors, and tinting mechanisms are
still in active development and are subject to change before a long term
solution is established.
Impact
The introduction of the "Fresh" theme demonstrates TYPO3's commitment to
providing a modern, customizable backend experience. The modular CSS
architecture allows for continuous improvement and evolution of the backend
design system.
New TYPO3 installations will automatically use the "Fresh" theme, while
existing users can opt in through their User Settings. The choice
of theme is stored per user, allowing teams with different preferences to work
comfortably within the same TYPO3 installation.
As a result, the method
gif_or_jpg() of
\TYPO3\CMS\Core\Imaging\GraphicalFunctions is no longer needed.
Fallback behavior for image preview generation now follows the configured
file extensions rather than a hardcoded GIF/JPEG switch.
A new method,
GraphicalFunctions->determineDefaultProcessingFileExtension(),
has been introduced.
It accepts a file extension such as
'pdf' as an argument and returns
the corresponding default output extension for preview generation.
This method is currently marked as
@internal, as it may later be moved
to a dedicated service class.
In general, third-party extensions should not determine the output format
manually but rely on TYPO3’s built-in image generation APIs.
Calling this method will trigger a deprecation-level log entry and will stop
working in TYPO3 v15.0.
Affected installations
Instances that directly use the deprecated method.
Migration
useTYPO3\CMS\Core\Imaging\GraphicalFunctions;
useTYPO3\CMS\Core\Utility\GeneralUtility;
$graphicalFunctions = GeneralUtility::makeInstance(GraphicalFunctions::class);
// Before
$filetype = $graphicalFunctions->gif_or_jpg('pdf', 800, 600);
// Returned: 'jpg'// After
$filetype = $graphicalFunctions->determineDefaultProcessingFileExtension('pdf');
// Returns: 'jpg' (for example, now depends on configuration!)
Copied!
This is a temporary migration using an
@internal method, which is subject
to change. Code like the above should generally be avoided in third-party
extensions.
Instead, use
GraphicalFunctions->resize() and specify the argument
$targetFileExtension = 'web' so that actual operations use the configured
target formats.
Deprecation: #97559 - Deprecate passing an array of configuration values to Extbase attributes
Passing an array of configuration values to Extbase attributes has been
deprecated. All configuration values should now be passed as single properties
using constructor property promotion. When an array of configuration values is
passed for the first available property in an attribute, a deprecation notice
will be triggered. The possibility to pass such an array will be removed with
TYPO3 v15.
Impact
The usage of constructor property promotion as an alternative to an array of
configuration values enables type safety and value hardening and moves Extbase
attributes toward a modern configuration element for models,
Data Transfer Objects, and controller actions.
Affected installations
All installations that make use of Extbase attribute configuration are
affected, since this was previously only possible by passing an array of
configuration values.
Migration
Use the available attribute properties instead of an array.
The custom
__inheritances operator, which was available only in
YAML configuration files of EXT:form, has been deprecated.
Previously, this operator was used within form definition files to inherit
and reuse configuration parts between form element definitions.
With native YAML functionality now providing equivalent and more flexible
features, this TYPO3-specific operator is no longer necessary.
Developers are encouraged to migrate to standard YAML features such as
anchors, aliases, and overrides to avoid code duplication and to simplify
form configuration maintenance.
Impact
Using the
__inheritances operator inside a custom YAML form configuration
in EXT:form will trigger a PHP
E_USER_DEPRECATED error.
Affected installations
All installations with custom form definitions or form element configurations
that use the
__inheritances operator in their EXT:form YAML files
are affected and need to update those files accordingly.
Migration
The custom TYPO3 implementation using
__inheritances can be replaced
with standard YAML syntax.
Developers can achieve the same result by using anchors (
&),
aliases (
*), and overrides (
<<:).
The registration of scheduler tasks via
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']
has been deprecated in favor of the new native scheduler task feature using TCA.
Previously, scheduler tasks were registered in ext_localconf.php
using the following syntax:
This approach required a separate
AdditionalFieldProviderInterface
implementation to handle custom task fields.
The AdditionalFieldProvider was responsible for:
Rendering form fields in the scheduler module.
Validating field input.
Saving and loading field values.
The new approach replaces this with native TCA configuration, providing:
Better integration with TYPO3's FormEngine.
Automatic validation through TCA field configuration.
Enhanced security through FormEngine's XSS protection.
Consistency with other TYPO3 backend forms.
Access to all TCA field types and rendering options.
In addition, the class
AbstractAdditionalFieldProvider
and the interface
AdditionalFieldProviderInterface
have been deprecated.
Impact
Using
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']
for registering scheduler tasks will stop working in TYPO3 v15.0.
Custom task classes implementing
AdditionalFieldProviderInterface
should remove this interface implementation.
The interface methods
(
getAdditionalFields(),
validateAdditionalFields(),
saveAdditionalFields()) are no longer needed with the new
TCA-based approach.
Affected installations
All installations with custom scheduler tasks registered via
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']
and using
AdditionalFieldProviderInterface .
Migration
Scheduler tasks should now be registered as native task types using TCA.
This provides a more integrated and maintainable approach to task configuration.
Migration steps:
Remove the registration from ext_localconf.php.
Create a TCA override file in Configuration/TCA/Overrides/scheduler_my_task_type.php.
Update your task class to implement the new parameter methods.
Remove the
\AdditionalFieldProvider class if it exists.
Note
The new TCA-based approach automatically migrates existing task data.
When upgrading, existing task configurations are preserved through the
getTaskParameters() and
setTaskParameters() methods.
Update your (existing) task class to implement the new methods:
EXT:my_extension/Classes/Task/MyTask.php
namespaceMyVendor\MyExtension\Task;
useTYPO3\CMS\Core\Messaging\FlashMessage;
useTYPO3\CMS\Core\Messaging\FlashMessageService;
useTYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
useTYPO3\CMS\Core\Utility\GeneralUtility;
useTYPO3\CMS\Scheduler\Task\AbstractTask;
classMyTaskextendsAbstractTask{
protected string $myField = '';
protected string $emailList = '';
publicfunctionexecute(): bool{
// Your task logic here using $this->myField and $this->emailListreturntrue;
}
/**
* Return current field values as an associative array.
* This method is called during migration from old serialized tasks
* and when displaying task information.
*/publicfunctiongetTaskParameters(): array{
return [
'my_extension_field' => $this->myField,
'my_extension_email_list' => $this->emailList,
];
}
/**
* Set field values from an associative array.
* This method handles both old and new parameter formats for migration.
*
* @param array $parameters Values from either old AdditionalFieldProvider or new TCA fields.
*/publicfunctionsetTaskParameters(array $parameters): void{
// Handle migration: check old parameter names first, then new TCA field names$this->myField = $parameters['myField'] ?? $parameters['my_extension_field'] ?? '';
$this->emailList = $parameters['emailList'] ?? $parameters['my_extension_email_list'] ?? '';
}
/**
* Validate task parameters.
* Only implement this method for validation that cannot be handled by FormEngine.
* Basic validation like 'required' should be done via TCA 'eval' configuration.
*/publicfunctionvalidateTaskParameters(array $parameters): bool{
$isValid = true;
// Example: Custom email validation (beyond basic 'required' check)
$emailList = $parameters['my_extension_email_list'] ?? '';
if (!empty($emailList)) {
$emails = GeneralUtility::trimExplode(',', $emailList, true);
foreach ($emails as $email) {
if (!GeneralUtility::validEmail($email)) {
GeneralUtility::makeInstance(FlashMessageService::class)
->getMessageQueueByIdentifier()
->addMessage(
GeneralUtility::makeInstance(
FlashMessage::class,
'Invalid email address: ' . $email,
'',
ContextualFeedbackSeverity::ERROR
)
);
$isValid = false;
}
}
}
return $isValid;
}
publicfunctiongetAdditionalInformation(): string{
$info = [];
if ($this->myField !== '') {
$info[] = 'Field: ' . $this->myField;
}
if ($this->emailList !== '') {
$info[] = 'Emails: ' . $this->emailList;
}
return implode(', ', $info);
}
}
Copied!
Key methods explained
The new TCA-based approach uses three key methods for parameter handling:
getTaskParameters(): array
This method is already implemented in
AbstractTask
to handle task class properties automatically, but it can be overridden in
task classes for custom behavior.
The method is primarily used:
For migration from the old serialized task format to the new TCA structure.
For non-native (deprecated) task types to store their values in the legacy parameters field.
For native TCA tasks, this method is typically no longer needed in custom
tasks after the migration has been done, since field values are then stored
directly in database columns.
setTaskParameters(array $parameters): void
Sets field values from an associative array. This method handles:
Migration from old
\AdditionalFieldProvider
field names to new TCA field names.
Loading saved task configurations when editing or executing tasks.
Parameter mapping during task creation and updates.
The method should always be implemented, especially for native tasks.
The migration pattern is:
$this->myField = $parameters['oldName'] ?? $parameters['new_tca_field_name'] ?? '';
validateTaskParameters(array $parameters): bool
Optional method. Only implement this for validation that cannot be handled by FormEngine.
Basic validation (required, trim, etc.) should be done via TCA configuration (required property and eval options).
Use this method for complex business logic validation (e.g., email format validation, external API checks).
Return false and add a FlashMessage for validation errors.
FormEngine automatically handles standard TCA validation rules.
For a complete working example, see
SystemStatusUpdateTask
and its corresponding TCA configuration in
EXT:reports/Configuration/TCA/Overrides/scheduler_system_status_update_task.php.
Deprecation: #106393 - Various methods in BackendUtility
Due to the introduction of the Schema API, several methods of
\TYPO3\CMS\Backend\Utility\BackendUtility that retrieve
information from
$GLOBALS['TCA'] have been deprecated:
BackendUtility::getCommonSelectFields()
BackendUtility::getItemLabel()
BackendUtility::isTableLocalizable()
BackendUtility::isTableWorkspaceEnabled()
BackendUtility::isRootLevelRestrictionIgnored()
BackendUtility::isWebMountRestrictionIgnored()
BackendUtility::resolveFileReferences()
Impact
Calling any of the mentioned methods now triggers a deprecation-level log
entry and will stop working in TYPO3 v15.0.
The extension scanner reports usages as a strong match.
Affected installations
Instances or extensions that directly call these methods are affected.
Migration
The migration strategy is the same for all cases:
use the corresponding Schema API methods directly in your code.
In most cases, you'll need to inject
TcaSchemaFactory via dependency injection.
getCommonSelectFields
No substitution is available. The method was marked as @internal already.
If your code depends on this functionality, copy the method into your own
extension.
getItemLabel
useTYPO3\CMS\Backend\Utility\BackendUtility;
useTYPO3\CMS\Core\Schema\TcaSchemaCapability;
useTYPO3\CMS\Core\Schema\TcaSchemaFactory;
// Beforereturn BackendUtility::getItemLabel('pages', 'title');
// After (retrieve an instance of TcaSchemaFactory via dependency// injection of TYPO3\CMS\Core\Schema\TcaSchemaFactory)
$schema = $this->schemaFactory->has('pages')
? $this->schemaFactory->get('pages')
: null;
return $schema !== null && $schema->hasField('title')
? $schema->getField('title')->getLabel()
: null;
Copied!
isTableLocalizable
useTYPO3\CMS\Backend\Utility\BackendUtility;
useTYPO3\CMS\Core\Schema\TcaSchemaCapability;
useTYPO3\CMS\Core\Schema\TcaSchemaFactory;
// Beforereturn BackendUtility::isTableLocalizable('pages');
// After (retrieve an instance of TcaSchemaFactory via dependency// injection of TYPO3\CMS\Core\Schema\TcaSchemaFactory)return$this->schemaFactory->has('pages')
&& $this->schemaFactory->get('pages')
->hasCapability(TcaSchemaCapability::Language);
Copied!
isTableWorkspaceEnabled
useTYPO3\CMS\Backend\Utility\BackendUtility;
useTYPO3\CMS\Core\Schema\TcaSchemaCapability;
useTYPO3\CMS\Core\Schema\TcaSchemaFactory;
// Beforereturn BackendUtility::isTableWorkspaceEnabled('pages');
// After (retrieve an instance of TcaSchemaFactory via dependency// injection of TYPO3\CMS\Core\Schema\TcaSchemaFactory)return$this->schemaFactory->has('pages')
&& $this->schemaFactory->get('pages')
->hasCapability(TcaSchemaCapability::Workspace);
Copied!
isRootLevelRestrictionIgnored
useTYPO3\CMS\Backend\Utility\BackendUtility;
useTYPO3\CMS\Core\Schema\TcaSchemaCapability;
useTYPO3\CMS\Core\Schema\TcaSchemaFactory;
// Beforereturn BackendUtility::isRootLevelRestrictionIgnored('pages');
// After (retrieve an instance of TcaSchemaFactory via dependency// injection of TYPO3\CMS\Core\Schema\TcaSchemaFactory)return$this->schemaFactory->has('pages')
&& $this->schemaFactory->get('pages')
->getCapability(
TcaSchemaCapability::RestrictionRootLevel
)->shallIgnoreRootLevelRestriction();
Copied!
isWebMountRestrictionIgnored
useTYPO3\CMS\Backend\Utility\BackendUtility;
useTYPO3\CMS\Core\Schema\TcaSchemaCapability;
useTYPO3\CMS\Core\Schema\TcaSchemaFactory;
// Beforereturn BackendUtility::isWebMountRestrictionIgnored('pages');
// After (retrieve an instance of TcaSchemaFactory via dependency// injection of TYPO3\CMS\Core\Schema\TcaSchemaFactory)return$this->schemaFactory->has('pages')
&& $this->schemaFactory->get('pages')
->hasCapability(TcaSchemaCapability::RestrictionWebMount);
Copied!
resolveFileReferences
No substitution is available. Copy the method into your own codebase and adapt
it as needed.
The
build() method in
\TYPO3\CMS\Frontend\Typolink\AbstractTypolinkBuilder
has been deprecated in favor of the new
\TYPO3\CMS\Frontend\Typolink\TypolinkBuilderInterface .
When creating custom TypolinkBuilder classes, the traditional approach was to:
This approach is now deprecated.
The new, recommended pattern is to implement
TypolinkBuilderInterface
directly.
The
ContentObjectRenderer property in
AbstractTypolinkBuilder
has also been deprecated and will be removed in TYPO3 v15.0.
It should now be accessed via the
\Psr\Http\Message\ServerRequestInterface
object instead.
Impact
Extension authors who have created custom TypolinkBuilder classes that extend
AbstractTypolinkBuilder will see
deprecation warnings when their link builders are used.
Deprecation warnings occur when:
A custom TypolinkBuilder class still implements the
build() method
Code accesses the deprecated
$contentObjectRenderer property
Dependencies are passed through the constructor instead of DI
Use dependency injection for required services instead of accessing them
through global state or constructor arguments.
Retrieve
ContentObjectRenderer from the request object rather than
the deprecated property.
All implementations of
TypolinkBuilderInterface
are automatically registered as public services in the Dependency Injection
container, so no manual service configuration is necessary.
Extensions can maintain backward compatibility during the transition by
implementing both the old
build() and the new
buildLink() methods.
Deprecation: #106527 - markFieldAsChanged() moved to FormEngine main module
The static method
markFieldAsChanged() in the module
@typo3/backend/form-engine-validation is used to modify the markup in the
DOM to mark a field as changed. Technically, this is unrelated to validation
itself, therefore the method has been moved to
@typo3/backend/form-engine.
Impact
Calling
markFieldAsChanged() from
@typo3/backend/form-engine-validation will trigger a deprecation notice
in the browser console.
Affected installations
All extensions using the deprecated code are affected.
Migration
If not already done, import the main FormEngine module and call
markFieldAsChanged() from there.
Example:
- import FormEngineValidation from '@typo3/backend/form-engine-validation.js';+ import FormEngine from '@typo3/backend/form-engine.js';- FormEngineValidation.markFieldAsChanged(fieldReference);+ FormEngine.markFieldAsChanged(fieldReference);
The method
\TYPO3\CMS\Core\Utility\GeneralUtility::resolveBackPath has
been marked as deprecated and will be removed in TYPO3 v15.0.
It served as a mechanism to remove relative path segments such as ".." when
referencing files or directories. This was particularly important before TYPO3
v7, when every TYPO3 backend module had its own route and entry point PHP file.
Nowadays, it is a relic from the past.
Since TYPO3 v13, this method has become even less relevant, as both the TYPO3
backend and frontend now share the same main entry point file
(index.php).
Impact
TYPO3 no longer resolves the back path of resource references or normalizes
paths when rendering or referencing resources in the HTML output - neither in
the frontend nor in the backend.
However, existing references will continue to work.
Affected installations
TYPO3 installations with custom TypoScript inclusions or backend modules that
reference files using relative paths may be affected.
Such usage is uncommon in modern TYPO3 installations.
Migration
References to resources should now use the EXT: prefix or be written
relative to the public web path of the TYPO3 installation.
References to JavaScript modules (ES6 modules) should be managed through import
maps using module names instead of relative paths.
Deprecation: #106821 - Workspace aware inline child tables are enforced
TCA tables that are used as inline child tables in a standard
foreign_table relationship must be declared as workspace aware if their
parent table is workspace aware.
Impact
The TYPO3 Core may automatically add versioningWS = true to the
ctrl section of inline child tables. In this case, a deprecation-level
log entry will be issued, indicating that the TCA definition should be updated
accordingly.
This TCA definition should be added even if the workspace system extension is
not loaded. The database schema analyzer will then automatically add the
required workspace-related database columns.
Affected installations
TYPO3 instances with extensions that define inline relations where the parent
table is workspace aware but the child table is not are affected.
A typical scenario involves inline child tables attached to the
tt_content table.
Migration
An automatic TCA migration scans TCA configurations for this scenario and adds
versioningWS = true to affected child tables. Developers should add this
declaration manually to their TCA to satisfy the migration and suppress
deprecation log messages.
'ctrl' => [
'versioningWS' => true,
],
Copied!
Note that the automatic migration does not detect children attached to
inline fields within type => 'flex' (flex form) fields. Developers and
integrators should still explicitly declare such child tables as workspace
aware.
In general, the combination of "parent is workspace aware" and "child is not
workspace aware" is not supported by the TYPO3 Core in inline
foreign_table setups—regardless of whether the parent field is a database
column or part of a flex form.
Deprecation: #106947 - Move upgrade wizard related interfaces and attribute to EXT:core
EXT:install provided a couple of interfaces to allow implementing upgrade
wizards, along with the PHP attribute
#[UpgradeWizard(...)] to
register them.
Since TYPO3 v13 it is possible to run TYPO3 without EXT:install being
installed. However, this advantage could often not be utilised sensibly,
since extensions still needed to require EXT:install as a dependency in
order to ship upgrade wizards, because the implemented interfaces needed to
be available.
For this reason the following interfaces, classes and attributes are now moved
into EXT:core, and their former counterparts in EXT:install now extend
these Core classes:
Attribute
\TYPO3\CMS\Install\Attribute\UpgradeWizard to
\TYPO3\CMS\Core\Attribute\UpgradeWizard
Interface
\TYPO3\CMS\Install\Updates\ChattyInterface to
\TYPO3\CMS\Core\Upgrades\ChattyInterface
Interface
\TYPO3\CMS\Install\Updates\ConfirmableInterface to
\TYPO3\CMS\Core\Upgrades\ConfirmableInterface
Interface
\TYPO3\CMS\Install\Updates\PrerequisiteInterface to
\TYPO3\CMS\Core\Upgrades\PrerequisiteInterface
Interface
\TYPO3\CMS\Install\Updates\RepeatableInterface to
\TYPO3\CMS\Core\Upgrades\RepeatableInterface
Interface
\TYPO3\CMS\Install\Updates\UpgradeWizardInterface to
\TYPO3\CMS\Core\Upgrades\UpgradeWizardInterface
Class
\TYPO3\CMS\Install\Updates\Confirmation to
\TYPO3\CMS\Core\Upgrades\Confirmation
Class
\TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite
to
\TYPO3\CMS\Core\Upgrades\DatabaseUpdatedPrerequisite
Class
\TYPO3\CMS\Install\Updates\ReferenceIndexUpdatedPrerequisite
to
\TYPO3\CMS\Core\Upgrades\ReferenceIndexUpdatedPrerequisite
AbstractClass
\TYPO3\CMS\Install\Updates\AbstractListTypeToCTypeUpdate
to
\TYPO3\CMS\Core\Upgrades\AbstractListTypeToCTypeUpdate
The following internal class has been moved without an alternative:
Class :php\TYPO3\CMS\Install\Updates\PrerequisiteCollection
to
\TYPO3\CMS\Core\Upgrades\PrerequisiteCollection
Extension authors are encouraged to migrate their upgrade wizards to the new
core interface, in order to drop their dependency on EXT:install.
Note
EXT:install is still required to execute upgrade wizards either on CLI
or within the TYPO3 Install Tool interface, even when switching to the
new EXT:core interfaces. Moving typo3 upgrade:* commands is planned
for TYPO3 v14 LTS.
Note
Note that not only "Install" is replaced with "Core", but also "Updates"
renamed to "Upgrades" to keep a consistent naming scheme.
Impact
Using the listed interfaces and the attribute in the scope of EXT:core allow extension
authors to optionally provide their extension upgrade wizards.
EXT:install can then be set as a "suggested" dependency, and is no longer
required to be mandatory.
The usage of these old interfaces from EXT:install is now deprecated.
Affected installations
Installations missing EXT:install or having extensions providing upgrade wizards
using the old namespace.
Migration
Upgrade custom upgrade wizards to use the new attribute, interfaces
and/or classes from the EXT:core namespace, instead of EXT:install:
The abstract upgrade wizard
\TYPO3\CMS\Install\Updates\AbstractListTypeToCTypeUpdate
has been introduced in 13.4 and is quite young, but needed to be moved to EXT:core to
provide implementing upgrade wizards utilizing this base class.
Note
Any extension that utilizes this abstract needs to require at least TYPO3 v14.0
when using the new base class.
Using the prerequisite classes from the EXT:install namespace
(using their compatibility alias) can be kept for the time being,
if an extension needs to provide support for two major TYPO3 versions:
TYPO3 v13 and v14 dual version support.
<?phpdeclare(strict_types=1);
namespaceMyVendor\MyExtension\Upgrades;
useTYPO3\CMS\Core\Attribute\UpgradeWizard;
useTYPO3\CMS\Core\Upgrades\ChattyInterface;
useTYPO3\CMS\Core\Upgrades\ConfirmableInterface;
useTYPO3\CMS\Core\Upgrades\RepeatableInterface;
useTYPO3\CMS\Core\Upgrades\UpgradeWizardInterface;
useTYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;useTYPO3\CMS\Install\Updates\ReferenceIndexUpdatedPrerequisite;#[UpgradeWizard('myExtensionCustomUpgradeWizardIdentifier')]classCustomUpgradeWizardimplementsUpgradeWizardInterface, ChattyInterface, RepeatableInterface{
/**
* @return string[] All new fields and tables must exist
*/publicfunctiongetPrerequisites(): array{
return [
DatabaseUpdatedPrerequisite::class, ReferenceIndexUpdatedPrerequisite::class,
];
}
}
Copied!
TYPO3 v14 and newer use core classes
<?phpdeclare(strict_types=1);
namespaceMyVendor\MyExtension\Upgrades;
useTYPO3\CMS\Core\Attribute\UpgradeWizard;
useTYPO3\CMS\Core\Upgrades\ChattyInterface;
useTYPO3\CMS\Core\Upgrades\ConfirmableInterface;
useTYPO3\CMS\Core\Upgrades\RepeatableInterface;
useTYPO3\CMS\Core\Upgrades\UpgradeWizardInterface;
useTYPO3\CMS\Core\Upgrades\DatabaseUpdatedPrerequisite;useTYPO3\CMS\Core\Upgrades\ReferenceIndexUpdatedPrerequisite;#[UpgradeWizard('myExtensionCustomUpgradeWizardIdentifier')]classCustomUpgradeWizardimplementsUpgradeWizardInterface, ChattyInterface, RepeatableInterface{
/**
* @return string[] All new fields and tables must exist
*/publicfunctiongetPrerequisites(): array{
return [
DatabaseUpdatedPrerequisite::class, ReferenceIndexUpdatedPrerequisite::class,
];
}
}
Copied!
Deprecation: #106969 - Deprecate User TSConfig auth.BE.redirectToURL
The method
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue()
has been deprecated.
This method was historically used to assign FlexForm definitions to plugins
registered as subtypes in the
list_type field of
tt_content. With
the removal of subtypes (see Breaking: #105377 - Deprecated functionality removed) and the shift
toward registering plugins as dedicated record types via CType, as well
as the removal of the ds_pointerField and multi-entry ds array
format (see Breaking: #107047 - Remove pointer field functionality of TCA flex), this separate method call is no
longer necessary.
Impact
Calling this method will trigger a deprecation warning. The extension scanner
will also report any usages. The method will be removed in TYPO3 v15.0.
Affected installations
Extensions that call
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue()
to assign FlexForm definitions to plugins or content elements are affected.
Migration
Instead of using this method, define the FlexForm configuration using one of
the following approaches:
The auto-rendering of template sections HeaderAssets and
FooterAssets available in Fluid templates has been marked as deprecated
in TYPO3 v14.0 and will be removed in TYPO3 v15.0.
Impact
Using the deprecated sections will raise a deprecation-level log error and
will stop working in TYPO3 v15.0.
Affected installations
TYPO3 installations using the sections HeaderAssets and
FooterAssets in Fluid templates.
Migration
It is recommended to use the
f:asset.script or
f:asset.css
ViewHelpers from the TYPO3 Asset Collector API to render required assets.
In scenarios where the
f:asset.script or
f:asset.css ViewHelpers
are not suitable, users can use the
f:page.headerData or
f:page.footerData ViewHelpers to render custom HTML header or footer
markup.
Deprecation: #107225 - Boolean sort direction in FileList->start()
The fourth parameter of the method
\TYPO3\CMS\Filelist\FileList::start() has been renamed from
$sortRev to
$sortDirection and now accepts both boolean values
(for backward compatibility) and
SortDirection enum values.
Passing a boolean value for the sort direction (fourth parameter of
FileList::start()) has been deprecated in favor of the new
\TYPO3\CMS\Filelist\Type\SortDirection enum, providing better type
safety and clarity. The parameter name has also changed from
$sortRev to
$sortDirection to more accurately describe its
purpose.
Impact
Calling
FileList::start() with a boolean value as the fourth parameter
triggers a deprecation warning. The functionality will continue to work for
now but will be removed in TYPO3 v15.0.
Affected installations
All installations using
FileList::start() directly with a boolean value
for the sort direction parameter are affected. This mainly applies to custom
file browser implementations or extensions that instantiate and configure the
FileList class directly, even though it is
marked as @internal.
Migration
Replace boolean values with the corresponding
SortDirection enum values:
With the removed support for PHP annotations in Extbase, the class namespace
\TYPO3\CMS\Extbase\Annotation no longer reflects classes using PHP
annotations. It now contains PHP attributes only.
The class namespace of all PHP attributes used with models, DTOs, and controller
actions in the Extbase context has been moved to
\TYPO3\CMS\Extbase\Attribute.
Developers and integrators are advised to migrate existing class usages to the
new namespace. The deprecated namespace will be removed in TYPO3 v15.0.
Impact
Usage of the deprecated class namespace is no longer recommended but will
continue to work until TYPO3 v15.0.
Affected installations
Instances using the deprecated class namespace for Extbase attributes in
models, DTOs, or controller actions are affected.
Migration
Aside from migrating PHP annotations to PHP attributes, existing usages of the class namespace
\TYPO3\CMS\Extbase\Annotation should be updated to
\TYPO3\CMS\Extbase\Attribute:
The method
\TYPO3\CMS\Core\Resource\Collection\FileCollectionRegistry->addTypeToTCA()
has been deprecated in TYPO3 v14.0 and will be removed in TYPO3 v15.0.
This method was originally intended to register additional file collection
types by directly manipulating the global
$GLOBALS['TCA'] array for the
sys_file_collection table. With modern TCA configuration patterns, this
approach is no longer recommended.
Impact
Calling this method will trigger a deprecation-level log entry and will stop
working in TYPO3 v15.0.
Affected installations
Instances using the
FileCollectionRegistry->addTypeToTCA() method
directly are affected.
The extension scanner will report usages as weak match.
Migration
Instead of using this method, configure file collection types directly in your
TCA configuration files. Move the TCA configuration from the method call to
your extension's
Configuration/TCA/Overrides/sys_file_collection.php file.
The global configuration option
$GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['parser']
has been removed and replaced with
$GLOBALS['TYPO3_CONF_VARS']['LANG']['loader'] .
Impact
Using any of the mentioned parser classes will raise a deprecation level log
entry and will stop working in TYPO3 v15.0.
The extension scanner will report usages as strong match.
Affected installations
Instances using the deprecated localization parser classes directly or
configuring custom parsers via the removed global configuration option.
Migration
Replace usage of the deprecated parser classes with Symfony Translation
loaders. Use the new
XliffLoader
class for XLIFF file processing.
The Symfony Translator and its loaders are now responsible for file parsing and
should be used instead of the deprecated TYPO3 parsers.
For custom localization needs, implement Symfony Translation loader interfaces
instead of the deprecated TYPO3 parser interfaces.
Configuration changes:
useTYPO3\CMS\Core\Localization\Loader\XliffLoader;
useTYPO3\CMS\Core\Localization\Parser\XliffParser;
// Before
$GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['parser']['xlf'] = XliffParser::class;
// After
$GLOBALS['TYPO3_CONF_VARS']['LANG']['loader']['xlf'] = XliffLoader::class;
Copied!
Please note: This functionality only affects the internal handling of
translation files ("locallang" files). The public API of the localization
system remains unchanged.
GeneralUtility::createVersionNumberedFilename adds cache busting to a
file URL, when called in a certain and correct order with other legacy API
methods to create URLs from system resources.
This class and its functionality is superseded by the System Resource API.
Impact
Calling this method will trigger a PHP deprecation warning. The method will
continue to work as is, until it is removed in TYPO3 v15.0.
Affected installations
TYPO3 installations with custom extensions or code that directly call this
deprecated method:
The code should always be refactored to receive a request, which must then be passed to the API.
If that is not possible due to restrictions in TYPO3 legacy API (e.g. Events or Hooks not passing the
request, but an URL must be generated in the listener), null can be passed.
Passing null instructs the API to check for
$GLOBALS['TYPO3_REQUEST'] and use it (if it exists).
Be aware, though, that this global variable will deprecated and removed eventually.
On CLI,
$GLOBALS['TYPO3_REQUEST'] is never available, neither is there a request.
In that case, passing null to the API has the consequence that no absolute URLs (containing host
and scheme) can be created. If you need absolute URLs based on a certain site on CLI,
a request must be constructed accordingly and passed to the API.
For more information about this and how to get/construct/mock the request object,
see TYPO3 request object.
The code should always be refactored to receive a request, which must then be passed to the API.
If that is not possible due to restrictions in TYPO3 legacy API (e.g. Events or Hooks not passing the
request, but an URL must be generated in the listener), null can be passed.
Passing null instructs the API to check for
$GLOBALS['TYPO3_REQUEST'] and use it (if it exists).
Be aware, though, that this global variable will deprecated and removed eventually.
On CLI,
$GLOBALS['TYPO3_REQUEST'] is never available, neither is there a request.
In that case, passing null to the API has the consequence that no absolute URLs (containing host
and scheme) can be created. If you need absolute URLs based on a certain site on CLI,
a request must be constructed accordingly and passed to the API.
For more information about this and how to get/construct/mock the request object,
see TYPO3 request object.
The static method
\TYPO3\CMS\Core\Utility\PathUtility::getPublicResourceWebPath($extResource)
was marked internal since its introduction. Since there were no good
alternatives to this API, it is now deprecated first, before being removed with
TYPO3 v15.0.
Impact and affected installations
TYPO3 installations using
PathUtility::getPublicResourceWebPath($extResource) will receive a
deprecation message for each call of this method.
The code should always be refactored to receive a request, which must then be passed to the API.
If that is not possible due to restrictions in TYPO3 legacy API (e.g. Events or Hooks not passing the
request, but an URL must be generated in the listener), null can be passed.
Passing null instructs the API to check for
$GLOBALS['TYPO3_REQUEST'] and use it (if it exists).
Be aware, though, that this global variable will deprecated and removed eventually.
On CLI,
$GLOBALS['TYPO3_REQUEST'] is never available, neither is there a request.
In that case, passing null to the API has the consequence that no absolute URLs (containing host
and scheme) can be created. If you need absolute URLs based on a certain site on CLI,
a request must be constructed accordingly and passed to the API.
For more information about this and how to get/construct/mock the request object,
see TYPO3 request object.
Deprecation: #107550 - Table Garbage Collection Task configuration via $GLOBALS
The
\TYPO3\CMS\Scheduler\Task\TableGarbageCollectionTask has been
migrated to use TYPO3's native TCA-based task configuration system. As part of
this migration, the previous configuration method using
has been deprecated and will be removed in TYPO3 v15.0.
Impact
Using the old configuration method will trigger a PHP deprecation warning.
The functionality continues to work for now, with the deprecated configuration
being merged with the new TCA-based configuration automatically.
Affected installations
Any installation that configures custom tables for the
TableGarbageCollectionTask using the
deprecated
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'] configuration.
The extension scanner will report any usage as a weak match.
Migration
Instead of configuring tables via
$GLOBALS['TYPO3_CONF_VARS'] , tables
should now be configured in TCA using the taskOptions configuration of
the corresponding record type within Configuration/TCA/Overrides/.
The
\TYPO3\CMS\Scheduler\Task\IpAnonymizationTask has been migrated to
use TYPO3's native TCA-based task configuration system. As part of this
migration, the previous configuration method using
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][IpAnonymizationTask::class]['options']['tables']
has been deprecated and will be removed in TYPO3 v15.0.
Impact
Using the old configuration method will trigger a PHP deprecation warning.
The functionality continues to work for now, with the deprecated configuration
being merged with the new TCA-based configuration automatically.
Affected installations
Any installation that configures custom tables for the
IpAnonymizationTask using the deprecated
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'] configuration.
The extension scanner will report any usage as a weak match.
Migration
Instead of configuring tables via
$GLOBALS['TYPO3_CONF_VARS'] , tables
should now be configured in TCA using the
taskOptions configuration of
the corresponding record type within
Configuration/TCA/Overrides/.
The public constants in
\TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper for defining
the state or severity of an infobox have been deprecated:
InfoboxViewHelper::STATE_NOTICE
InfoboxViewHelper::STATE_INFO
InfoboxViewHelper::STATE_OK
InfoboxViewHelper::STATE_WARNING
InfoboxViewHelper::STATE_ERROR
These constants have been superseded by the dedicated enum
\TYPO3\CMS\Core\Type\ContextualFeedbackSeverity , which provides a
single source of truth for severity levels across the entire TYPO3 Core
and improves type safety and maintainability.
Impact
Using these constants will trigger a PHP deprecation warning. The
constants will be removed in TYPO3 v15.0. The extension scanner will
report usages as weak match.
Affected installations
Instances using any of the
STATE_* constants from
InfoboxViewHelper in their PHP
code or Fluid templates.
Migration
Replace the deprecated constants with the corresponding
ContextualFeedbackSeverity enum.
Important
Whenever possible, use the enum directly instead of extracting its
integer value. This provides better type safety and makes the code
more expressive. Only use
->value when you explicitly need
the integer representation.
Example (PHP)
// BeforeuseTYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper;
$state = InfoboxViewHelper::STATE_ERROR;
// After - Recommended: Use the enum directlyuseTYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
$severity = ContextualFeedbackSeverity::ERROR;
// Alternative: Use the integer value when explicitly needed
$stateValue = ContextualFeedbackSeverity::ERROR->value;
Copied!
In Fluid templates, use the enum via
f:constant():
Example (Fluid Template)
<!-- Before --><f:be.infoboxtitle="Error!"state="{f:constant(name: 'TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper::STATE_ERROR')}">
Error message
</f:be.infobox><!-- After --><f:be.infoboxtitle="Error!"state="{f:constant(name: 'TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::ERROR')}">
Error message
</f:be.infobox>
Copied!
The
InfoboxViewHelper has been
updated to accept both the enum directly and integer values for backward
compatibility.
Mapping table
Deprecated constant
Replacement
Value
InfoboxViewHelper::STATE_NOTICE
ContextualFeedbackSeverity::NOTICE->value
-2
InfoboxViewHelper::STATE_INFO
ContextualFeedbackSeverity::INFO->value
-1
InfoboxViewHelper::STATE_OK
ContextualFeedbackSeverity::OK->value
0
InfoboxViewHelper::STATE_WARNING
ContextualFeedbackSeverity::WARNING->value
1
InfoboxViewHelper::STATE_ERROR
ContextualFeedbackSeverity::ERROR->value
2
Deprecation: #107725 - Deprecate usage of array in password for authentication in Redis cache backend
Since Redis 6.0, it is possible to authenticate against Redis using both a
username and a password. Prior to this version, authentication was only
possible with a password.
With this change, TYPO3's Redis cache backend supports username and password
authentication directly. You can now configure the TYPO3 Redis cache backend
as follows:
The password configuration option of the Redis cache backend is now
typed as
array|string.
Setting this configuration option with an array is deprecated and will be
removed in TYPO3 v15.0.
Affected installations
All installations using the Redis cache backend and configuring the
password option as an array containing both username and password
values are affected.
Migration
Use the dedicated configuration options username and password
instead of passing an array to password.
The
\TYPO3\CMS\Backend\Template\Components\MetaInformation
class and related methods in
\TYPO3\CMS\Backend\Template\Components\DocHeaderComponent
have been deprecated in favor of the new breadcrumb component architecture.
These APIs were previously used to display page navigation paths in the
backend document header. This functionality is now handled by the breadcrumb
component, which provides richer context and better navigation capabilities.
Impact
Calling any of the deprecated methods will trigger a PHP
E_USER_DEPRECATED error.
The
MetaInformation
class is now marked as
@internal and should not be used in extensions.
Affected installations
All installations using custom backend modules that call:
or any custom code relying on the
MetaInformation class.
The Extension Scanner will detect usage of the deprecated methods and classes,
making it easy to identify code that needs to be updated.
Migration
Replace calls to the deprecated methods with the new convenience methods on
DocHeaderComponent .
Before:
Example (before)
useTYPO3\CMS\Backend\Template\Components\DocHeaderComponent;
// For page records
$view->getDocHeaderComponent()->setMetaInformation($pageInfo);
// For file or folder resources
$view->getDocHeaderComponent()->setMetaInformationForResource($resource);
Copied!
After:
Example (after)
useTYPO3\CMS\Backend\Template\Components\DocHeaderComponent;
// For page records
$view->getDocHeaderComponent()->setPageBreadcrumb($pageInfo);
// For file or folder resources
$view->getDocHeaderComponent()->setResourceBreadcrumb($resource);
Copied!
An additional convenience method is available for records:
Example (record breadcrumb)
useTYPO3\CMS\Backend\Template\Components\DocHeaderComponent;
// For any record type (by table and UID)
$view->getDocHeaderComponent()->setRecordBreadcrumb('tt_content', 123);
Copied!
For advanced scenarios requiring custom breadcrumb logic (such as conditional
context selection based on controller state), see the implementation in
\TYPO3\CMS\Backend\Controller\EditDocumentController , which uses
\TYPO3\CMS\Backend\Breadcrumb\BreadcrumbFactory
directly with
setBreadcrumbContext().
Deprecation: #107823 - ButtonBar, Menu, and MenuRegistry make* methods deprecated
The factory methods in
\TYPO3\CMS\Backend\Template\Components\ButtonBar
for creating button instances, in
\TYPO3\CMS\Backend\Template\Components\Menu for creating menu item
instances, and in
\TYPO3\CMS\Backend\Template\Components\MenuRegistry for creating
menu instances have been deprecated in favor of using the new
\TYPO3\CMS\Backend\Template\Components\ComponentFactory class directly.
The following methods are now deprecated:
ButtonBar::makeGenericButton()
ButtonBar::makeInputButton()
ButtonBar::makeSplitButton()
ButtonBar::makeDropDownButton()
ButtonBar::makeLinkButton()
ButtonBar::makeFullyRenderedButton()
ButtonBar::makeShortcutButton()
ButtonBar::makeButton()
Menu::makeMenuItem()
MenuRegistry::makeMenu()
Impact
Calling any of the deprecated
make*() methods on
ButtonBar ,
\Menu, or
MenuRegistry will trigger
a PHP deprecation notice.
The methods continue to work in TYPO3 v14 but will be removed in TYPO3 v15.
Affected installations
All extensions using
ButtonBar::make*() methods to create buttons,
Menu::makeMenuItem() to create menu items, or
MenuRegistry::makeMenu() to create menus are affected.
The extension scanner will report any usages.
Migration
Inject
\TYPO3\CMS\Backend\Template\Components\ComponentFactory in your
controller and use its
create*() methods instead of
ButtonBar::make*().
Additionally, consider using the preconfigured button creation methods like
createBackButton(),
createCloseButton(),
createSaveButton(),
createReloadButton(), and
createViewButton() for common button patterns.
For the low-level
makeButton(string $className) method, use
GeneralUtility::makeInstance() directly or the appropriate
ComponentFactory::create*() method:
Example (button instantiation)
useTYPO3\CMS\Core\Utility\GeneralUtility;
// Before:
$button = $buttonBar->makeButton(MyCustomButton::class);
// After (option 1 - direct instantiation):
$button = GeneralUtility::makeInstance(MyCustomButton::class);
// After (option 2 - via factory if it's a standard button):
$button = $this->componentFactory->createLinkButton();
Copied!
For
Menu::makeMenuItem(), use
ComponentFactory::createMenuItem():
The console command vendor/bin/typo3 language:domain:list does not list
deprecated language domains, unless the option --deprecated is used.
Impact
Using a label reference from one of these files triggers a
E_USER_DEPRECATED error.
Affected installations
Third-party extensions and site packages that use labels from the listed
sources will not be able to display the affected labels with TYPO3 v15.0.
Migration
If the desired string is contained in another language domain, consider using
that domain. Otherwise, move the required labels into your extension or site
package.
Deprecation: #107963 - sys_redirect default type name changed to "default"
The default type name for the
sys_redirect table has been changed from
'1' to 'default' in TYPO3 v14.0 to align with TYPO3 naming conventions and
allow for better type extensibility.
Extensions that directly access
$GLOBALS['TCA']['sys_redirect']['types']['1'] to manipulate the redirect
TCA configuration must be updated to use the new 'default' key instead.
Impact
Direct access to $GLOBALS['TCA']['sys_redirect']['types']['1'] will no
longer work, as the type has been renamed to 'default'.
Extensions that manipulate the
sys_redirect TCA type configuration must
be updated accordingly.
The TCA migration layer will automatically migrate any custom
$GLOBALS['TCA']['sys_redirect']['types']['1'] definitions to 'default'
during TCA compilation, but a deprecation message will be logged.
Affected installations
Instances with extensions that directly manipulate
$GLOBALS['TCA']['sys_redirect']['types']['1'] to customize the default
redirect record type.
Migration
Update your TCA override files to use the new type name 'default' instead
of '1'.
// Before - In Configuration/TCA/Overrides/sys_redirect.php
$GLOBALS['TCA']['sys_redirect']['types']['1']['label']
= 'My custom label';
// After - In Configuration/TCA/Overrides/sys_redirect.php
$GLOBALS['TCA']['sys_redirect']['types']['default']['label']
= 'My custom label';
Manually creating and adding
\ShortcutButton
instances to the button bar is deprecated and will trigger a deprecation
warning.
Controllers should use the new
\TYPO3\CMS\Backend\Template\Components\DocHeaderComponent::setShortcutContext()
method instead, which automatically creates and positions the shortcut button.
Impact
Controllers that manually create and add
\ShortcutButton
instances to the button bar will trigger a deprecation warning. The shortcut
button will still work as expected.
Affected installations
Installations with custom backend modules that manually create shortcut buttons.
Migration
Replace manual shortcut button creation with the new API.
Fluid 5.0 deprecates
\TYPO3Fluid\Fluid\Core\ViewHelper\LenientArgumentProcessor ,
which will be removed with Fluid 6.0.
\TYPO3Fluid\Fluid\Core\ViewHelper\StrictArgumentProcessor
is now used instead.
It is still possible to place these attributes at controller methods in
order to apply the validation behavior for a whole method instead of a
single parameter. In addition, the
#[Validate] attribute may still
be used for single properties.
The mentioned deprecation affects only the usage of attributes to
control parameter validation handling. All other validation behaviors
remain unchanged.
Impact
Passing a value other than
null to the mentioned attribute parameters
will trigger a deprecation warning. Validation will still work as expected, but
will stop working with TYPO3 v15.
Affected installations
All installations using the
#[IgnoreValidation] and
#[Validate]
attributes in Extbase context in combination with the mentioned attribute
parameters are affected.
Migration
Developers can easily migrate their implementations by moving parameter-related
attributes next to the method parameters instead of the related method.
Before:
finalclassFooControllerextendsActionController{
#[IgnoreValidation(argumentName: 'something')]publicfunctionbarAction(string $something): ResponseInterface{
// Do something...
}
#[Validate(validator: 'NotEmpty', param: 'anythingNotEmpty')]publicfunctionbazAction(string $anythingNotEmpty): ResponseInterface{
// Do something...
}
}
final class FooController extends ActionController
{
- #[IgnoreValidation(argumentName: 'something')]- public function barAction(string $something): ResponseInterface- {+ public function barAction(+ #[IgnoreValidation]+ string $something,+ ): ResponseInterface {
// Do something...
}
- #[Validate(validator: 'NotEmpty', param: 'anythingNotEmpty')]- public function bazAction(string $anythingNotEmpty): ResponseInterface- {+ public function bazAction(+ #[Validate(validator: 'NotEmpty')]+ string $anythingNotEmpty,+ ): ResponseInterface {
// Do something...
}
}
Copied!
Important: #104027 - New ViewHelper argument "module" to define module context
A new optional argument
module has been added to the following ViewHelpers:
<be:link.editRecord>
<be:link.newRecord>
<be:uri.editRecord>
<be:uri.newRecord>
The module argument allows integrators to explicitly define the backend module context
used when opening the FormEngine to edit or create a record. When set, this module will be
highlighted as active in the backend menu, providing better navigation context.
This is particularly useful in scenarios where the default context cannot be
reliably inferred.
Note
This argument is only necessary if the ViewHelper cannot determine the
module context from the request, for example, when used in an AJAX call.
If the ViewHelper is used within a backend module, setting the
module argument is usually not required unless a specific
module context should be enforced deliberately.
Usage Example
Example usage of module argument
<be:link.editRecordtable="tt_content"uid="{record.uid}"module="web_layout">
Edit this content element
</be:link.editRecord><be:uri.newRecordtable="custom_table"pid="123"module="web_list" />
Copied!
Impact
When used, the module argument ensures a more accurate and predictable
backend editing experience by controlling which module is marked as active when
the FormEngine opens.
When installing TYPO3 for the first time, a .htaccess file is added to
the htdocs or public directory when running TYPO3 via an Apache
web server.
In addition to several TYPO3-specific optimizations, this file mainly contains
rules (using the "mod_rewrite" Apache module) that redirect all URL requests
for non-existent files within a TYPO3 project to the main index.php
entry point file.
For new installations, this file now contains updated configuration that can
also be applied to existing TYPO3 setups to reflect the current default
behavior.
Key changes:
URL requests within /_assets/ and /fileadmin/ are no longer
redirected, as these directories contain resources either managed by TYPO3
or by editors.
The directory /_assets/ has been included since TYPO3 v12 in
Composer-based installations and is now officially added.
The folder /uploads/ is no longer maintained by TYPO3 since v11 and
is now removed from the default .htaccess configuration. This means
that TYPO3 pages can now officially use the URL path /uploads.
It is recommended to apply these adjustments in existing TYPO3 installations as
well, even for other web servers such as nginx or IIS, provided there is no
custom usage of /_assets/ or /uploads/ (for example through a
PSR-15 middleware, custom extension, or custom routing).
TYPO3 parses ext_tables.sql files into a Doctrine
DBAL object schema to define a virtual
database scheme, enriched with
\TYPO3\CMS\Core\Schema\DefaultTcaSchema information for
TCA managed tables and fields.
Fixed- and variable-length variants have been parsed in the past, but failed
to flag the column as
$fixed = true for the fixed-length database
field types
CHAR and
BINARY. This resulted in the wrong
creation of these columns as
VARCHAR and
VARBINARY, which is
now corrected.
ext_tables.sql
created as (before)
created as (now)
CHAR(10)
VARCHAR(10)
CHAR(10)
VARCHAR(10)
VARCHAR(10)
VARCHAR(10)
BINARY(10)
VARBINARY(10)
BINARY(10)
VARBINARY(10)
VARBINARY(10)
VARBINARY(10)
Not all relational database management systems (RDBMS) behave the same way
for fixed-length columns. Implementation differences need to be respected to
ensure consistent query and data behaviour across all supported database
systems.
Warning
Using fixed-length
CHAR and
BINARY column types requires
careful handling of data being persisted to and retrieved from the
database due to different behaviour, especially on PostgreSQL.
Fixed-length
CHAR
Key difference between CHAR and VARCHAR
The main difference between
CHAR and
VARCHAR is how the
database stores character data.
CHAR, which stands for character,
is a fixed-length data type. It always reserves a specific amount of
storage space for each value, regardless of whether the actual data
occupies that space entirely. For example, if a column is defined as
CHAR(10) and the word apple is stored inside of it, it will still
occupy 10 characters (not just 5). Unused characters are padded with
spaces.
On the other hand,
VARCHAR, short for variable character, is a
variable-length data type. It only uses as much storage space as needed to
store the actual data without padding. Thus, storing the word apple in a
VARCHAR(10) column will only occupy 5 characters.
The main difference between PostgreSQL and MySQL, MariaDB or SQLite is
that PostgreSQL also returns the padded spaces for values that do not fill
the full defined length (for example, apple[space][space][space][space]
[space]).
In addition, these padded spaces are respected in query conditions,
sorting or calculations (such as
concat()). These differences make a
significant impact and must be considered when using
CHAR
fields.
Rule of thumb for fixed-length
CHARcolumns
Use only with guaranteed fixed-length values to avoid padding.
For 255 or more characters,
VARCHAR or
TEXT must be used.
More hints for fixed-length
CHARcolumns
Ensure that stored values are fixed-length (non-space characters), for
example by using hash algorithms that produce fixed-length identifiers.
Ensure that query statements use trim or rightPad within
WHERE,
HAVING or
SELECT operations when values are not guaranteed
to be fixed-length.
Tip
Helper expressions from
ExpressionBuilder
can be used.
For example:
ExpressionBuilder->trim() or
ExpressionBuilder->rightPad().
The use of
CHARmust be avoided when the column is used with
the Extbase object-relational mapper (ORM). Fixed-length values cannot be
guaranteed because trimming or padding is not applied within ORM-generated
queries. Only when fixed-length values are guaranteed is usage with
Extbase ORM possible.
Cover custom queries extensively with functional tests executed against
all supported database platforms. Code within public extensions should
ensure that queries and their operations are tested across all officially
supported TYPO3 database platforms.
Example of differences in behaviour of fixed-length
CHAR types
The following examples illustrate how different relational database
management systems handle fixed-length
CHAR values, and why the
behaviour must be carefully considered when storing or querying such data.
Creating a fixed-length field
Example ext_tables.sql defining a fixed-length tt_content field
Because of this, retrieved values need to be trimmed or padded after
fetching query results to ensure identical values across all supported
database systems. Another option is to ensure that persisted data always
has a fixed-length value, for example by using hashing algorithms (though
these values are not human-readable).
Querying trimmed versus padded values
Using a trimmed value in a
WHERE clause can match the row, but the
returned value will differ depending on the database platform.
CHAR and
BINARY fields can be used for storage or performance
adjustments, but only when composed data and queries account for the
differences between database systems.
Otherwise, the safe bet is to consistently use
VARCHAR and
VARBINARY column types.
Important: #105538 - Plugin subtypes removed: Changes to configurePlugin() and TCA handling
Due to the removal of the plugin content element "Plugin" (list) and the
corresponding plugin subtype field
list_type, the fifth parameter
$pluginType of
ExtensionUtility::configurePlugin() is now unused
and can be omitted. It is only kept for backwards compatibility.
Be aware that passing any value other than
CType will trigger an
\InvalidArgumentException.
Please also note that due to the removal of the
list_type field in
tt_content, passing list_type as the second parameter
$field
to
ExtensionManagementUtility::addTcaSelectItemGroup() will now, as for
any other non-existent field, trigger a
\RuntimeException.
Important: #106192 - Add 'center' and 'font' to YAML processing removeTags
The default YAML processing configuration file
EXT:rte_ckeditor/Configuration/RTE/Processing.yaml
has been changed to remove these HTML tags
<font> and
<center> by default when saving RTE field contents
to the database.
This new default is adjusted with the option processing.HTMLparser_db.removeTags,
which now also lists these two tags.
A stored input like
<p><font face="Arial">My text</font></p>
will - when saved - be changed to
<p>My text</p>.
Affected installations
All installations having
<font> and
<center>
stored in their database fields, and where no custom RTE
YAML configuration is in place that allows these tags.
Please note that due to issue forge#104839, this
removeTags option was never properly applied previously, so the chances
are that an installation never had output for font and
center properly working anyway.
Also, note that CKEditor by default uses
<span style="...">
tags to apply font formatting when using the Full preset.
Thus, the real-life impact should be low, but for legacy installations,
you may want to convert existing data to replace font/html tags
with their appropriate modern counterparts.
Migration
Either accept the removal of these tags and use specific
HTML tags like
<span> and
<div> to apply formatting.
Or adapt the RTE processing via TypoScript/YAML configuration
to not have center and font in the processing.HTMLparser_db.removeTags
list.
If the tags center and font have been configured via the editor.conf.style.definitions
YAML option (not set by default), CKEditor would allow the use of these tags,
but they will now be removed both when saving or when being rendered in the
frontend. So these style definitions should be removed and/or adapted to
<span style="..."> configurations.
Important: #106532 - Changed database storage format for Scheduler Tasks
TYPO3's system extension scheduler has stored its tasks to be executed in a
PHP-serialized format in the database since its inception.
This has led to many problems, for example, when changing a class property to be
fully typed, when a class name changed to use PHP 5 namespaces, or when
renaming a class or a class property.
This has now changed: the task object now stores the "tasktype" (typically the
class name) and its options in a "parameters" JSON-encoded value, as well as
the execution details (database field execution_details) in separate fields
of the database table
tx_scheduler_task. This way, the task object can
be reconstituted through a properly defined API, avoiding issues in the future.
All existing tasks are compatible with the new internal format. An upgrade
wizard ensures that the previously serialized objects are transferred into the
new format. If this wizard does not disappear after being executed, it means
there are tasks that failed to migrate and may need manual inspection or
recreation. Inspect all tasks in
tx_scheduler_task where the "tasktype"
column is empty. The old serialized data format is somewhat human-readable (or
can be inspected with PHP deserializers), so recreating a task with its former
configuration options should be possible.
Please note that this upgrade step needs to be performed in the context of
TYPO3 v14. Running the wizard in future TYPO3 versions may not succeed due to
changes in the task objects.
Important: #106649 - Default language binding in layout module
The Content > Layout module now always uses default language binding
(
mod.web_layout.defLangBinding) when displaying
localized content in language comparison mode.
Default language binding makes editing translations easier: Editors can see
what they are translating next to the default language. It also prevents
confusion when localizations are incomplete. Editors can directly see which
content is not yet translated. This improves the user experience in the backend
for multilingual sites, which was also a result of recent "Jobs To Be Done"
(JTBD) interviews.
Editors will now always see the content elements next to each other within
the Content > Layout module when in language comparison mode.
Migration
Because default language binding is now always enabled, the previous Page
TSconfig setting
mod.web_layout.defLangBinding is not evaluated
and can therefore be removed.
Important: #106656 - Allow DEFAULT NULL for varchar fields
In TCA, if an input field is configured to be nullable via
'nullable' => true, the database migration now respects this and creates
or updates existing fields with DEFAULT NULL.
In Extbase, this may cause issues if properties and their accessor methods are
not properly declared as nullable. Therefore, this change is introduced only in
TYPO3 v14.
As stated above, this automatic detection is not provided in TYPO3 versions
earlier than 14.0. Using DEFAULT NULL can be enforced via an extension's
ext_tables.sql instead:
CREATETABLE tx_myextension_table (
title varchar(255) DEFAULTNULL
);
Copied!
Important: #106947 - Allow extensions to ship upgrade wizards without requiring EXT:install
EXT:install is no longer installed per default in composer based
minimal installations.
However, this advantage could often not be utilised sensibly, since
extensions still needed to require EXT:install as a dependency in order to
ship upgrade wizards, because the implemented interfaces needed to be available.
The following changes are required (when requiring TYPO3 14.0+) to make EXT:install
optional:
Let custom upgrade wizards implement
\TYPO3\CMS\Core\Upgrades\UpgradeWizardInterface .
Implement custom list-type to CType migrations extending
\TYPO3\CMS\Core\Upgrades\AbstractListTypeToCTypeUpdate .
Use the attribute
\TYPO3\CMS\Core\Attribute\UpgradeWizard to
register custom upgrade wizards.
Note
The wording has been streamlined to use Upgrade instead of Update to
better align with their intention and with the naming in the TYPO3 command
line interface.
Extension authors supporting two major TYPO3 versions with one extension version can
follow these strategies:
TYPO3 v13 and v14: Use deprecated EXT:install interfaces and
PHP Attribute and require EXT:install as mandatory dependency, or
add it as a suggestion to allow the decision to be made on project-level.
TYPO3 v14: Switch to EXT:core interfaces and the new PHP Attribute.
Note
The related upgrade wizard CLI command and services have been moved to
EXT:core, allowing to execute TYPO3 Core and extension upgrade wizards on
deployments via CLI commands and making EXT:install optional for them.
The backward compatibility for using
$GLOBALS['TCA'] in base TCA files
has been removed. Base TCA files are the first TCA files loaded by the Core and
do not have the fully loaded TCA available yet. Until now, this worked because
the Core temporarily populated this global array during the loading process.
Note that the usage of
$GLOBALS['TCA'] in base TCA files was never
explicitly allowed or disallowed, only discouraged. It worked only due to
internal system knowledge by the user, for example, knowing that the loading
order of extensions affects when those files are loaded. The only place this
array should be used is inside TCA/Overrides/*.php files. For all other cases,
the
TcaSchema should be preferred.
Migration
In the uncommon case that you find usages of
$GLOBALS['TCA'] in base TCA
files, move that access to TCA/Overrides/*.php files instead.
Important: #107399 - Add more common file types to mediafile_ext
The default configuration for $GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext']
has been extended with additional formats commonly uploaded to web-based
CMS systems such as TYPO3:
3gp
aac
aif
avif
heic
ico
m4a
m4v
mov
psd
This list is extended only for systems where the configuration option
has not already been customized.
Adding these formats allows files with these extensions to be uploaded
into the file list (and used in TCA fields with "common-media-types")
when the security flag security.system.enforceAllowedFileExtensions
is enabled.
This may be a relevant change if these file types are now used in
records or content elements that do not expect such files for further
operations (for example, in frontend display).
Integrators must ensure that all uploaded file types are handled
appropriately (for example, embedding "mov" as a video, "ico" as an
image, or "psd" as a download) or have a suitable fallback.
The <f:media> ViewHelper, for example, iterates over possible
Renderer implementations that can handle several of these file
types (and their MIME types) and attempts to render a file as an image
as a fallback.
Important: #107536 - Install Tool now adapts to backend login routing
The Install Tool now integrates with TYPO3's backend routing system instead of
using a separate typo3/install.php file. This modernization improves
consistency while maintaining full backward compatibility.
If the TYPO3 installation is not working properly, the Install Tool can now be
accessed via the ?__typo3_install parameter, ensuring administrators can rely
on it for system maintenance and recovery.
Impact
For Administrators:
All existing workflows continue to work without changes. However, the Install
Tool is now accessible via:
The __typo3_install parameter (for example, https://example.com/?__typo3_install)
Backend routes such as /typo3/install and /typo3/install.php still work.
If the
$GLOBALS['TYPO3_CONF_VARS']['BE']['entryPoint'] is set, the
Install Tool adapts accordingly.
System Recovery:
When the TYPO3 installation is not set up or not working properly, the magic
__typo3_install parameter still redirects to the installer or maintenance tool
as before, ensuring administrators can always access system recovery tools.
Technical Benefits:
The Install Tool now uses the same routing infrastructure as the rest of the
TYPO3 backend, creating a more unified and maintainable architecture while
supporting the long-term goal of simplifying TYPO3's directory structure.
Migration
No migration is required. All existing documentation, scripts, and workflows
using the Install Tool continue to function without modification.
Important: #107629 - Reference Index check moved to Install Tool
The Check and Update Reference Index functionality has been moved
from the System > DB Check module (EXT:lowlevel) to the
Maintenance section of the Install Tool (EXT:install) in the
top level module now called System.
Note
The top-level backend modules were renamed in TYPO3 v14.
This change makes this essential administrative tool more accessible and
better organized alongside other common maintenance tasks such as database
comparison, cache management, and folder structure checks.
Why this change?
The Reference Index is a critical system component that tracks relationships
between records in TYPO3. Checking and updating is a routine maintenance
task that administrators perform regularly, similar to:
Analyzing database structure
Clearing caches
Checking folder permissions
Previously, this functionality was hidden in the DB Check module
of EXT:lowlevel, which made it:
Hard to discover: Administrators had to know where to look
Inconsistent: Other maintenance tools were in the Install Tool
Less accessible: Required an additional system extension
Impact
For Administrators:
The Reference Index check and update functionality is now directly available
in the Install Tool under
Maintenance > Check and Update Reference Index.
Key benefits:
Better visibility: Found alongside other maintenance tools
No extra dependencies: Works out-of-the-box without EXT:lowlevel
Consistent location: All system maintenance tasks in one place
Faster access: Direct access via the Install Tool
Same functionality: Check and update operations work exactly as before
CLI Access:
The command-line interface remains unchanged and continues to work as before:
# Check reference index
vendor/bin/typo3 referenceindex:update --check
# Update reference index
vendor/bin/typo3 referenceindex:update
Copied!
Migration
No migration is required. System Maintainers should use the new location
in the System > Maintenance section instead of the
System > DB Check module.
The functionality works identically to the previous implementation.
Important: #107735 - Internal methods removed from ResourceFactory
The following internal methods have been removed from
\TYPO3\CMS\Core\Resource\ResourceFactory :
getDefaultStorage()
getStorageObject()
createFolderObject()
getFileObjectByStorageAndIdentifier()
These methods were marked as
@internal and are replaced by using
\TYPO3\CMS\Core\Resource\StorageRepository directly for better
separation of concerns. However, some of these methods might have been used
in custom extensions despite being marked as internal.
Migration
Instead of using the removed methods from
ResourceFactory, use the
appropriate methods from
StorageRepository or direct access to
ResourceStorage:
To improve consistency and maintainability of TCA tab labels across TYPO3 Core,
commonly used tab labels from various extensions have been consolidated into
the central EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf
file.
This consolidation allows for better reusability and ensures consistent
translation of common tab labels across all core extensions. It also makes it
easier for extension developers to use standardized tab names.
New labels available in locallang_tabs.xlf
The following new tab labels are now available and should be used via the
core.form.tabs: prefix:
core.form.tabs:audio - "Audio"
core.form.tabs:video - "Video"
core.form.tabs:camera - "Camera"
core.form.tabs:permissions - "Permissions"
core.form.tabs:mounts - "Mounts"
core.form.tabs:personaldata - "Personal Data"
Previously existing labels (already migrated in core):
core.form.tabs:general - "General"
core.form.tabs:access - "Access"
core.form.tabs:categories - "Categories"
core.form.tabs:notes - "Notes"
core.form.tabs:language - "Language"
core.form.tabs:extended - "Extended"
core.form.tabs:appearance - "Appearance"
core.form.tabs:behaviour - "Behavior"
core.form.tabs:metadata - "Metadata"
core.form.tabs:resources - "Resources"
core.form.tabs:seo - "SEO"
core.form.tabs:socialmedia - "Social Media"
core.form.tabs:options - "Options"
Migrated extension-specific labels
The following extension-specific tab labels have been migrated to the
consolidated labels file and are marked as unused since TYPO3 v14.0 with the
attribute
x-unused-since="14.0" in the corresponding XLF files.
Custom extensions using TCA configurations may benefit from using the new
consolidated tab labels instead of creating their own labels for common tab
names.
Extensions that were using any of the migrated extension-specific labels
listed above will continue to work in TYPO3 v14.0, but should migrate to
the consolidated labels. The old labels will be removed in TYPO3 v15.0.
Migration
For custom extensions, consider using the consolidated
core.form.tabs:
labels instead of creating custom labels for common tab names.
Example migration for extensions using old labels:
File metadata tabs
// Before
'--div--;LLL:EXT:filemetadata/Resources/Private/Language/locallang_tca.xlf:tabs.metadata'
// After
'--div--;core.form.tabs:metadata'
Copied!
User and group tabs
// Before
'--div--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:be_users.tabs.personal_data'
'--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:fe_users.tabs.personalData'
// After
'--div--;core.form.tabs:personaldata'
In addition to tab labels, commonly used palette labels have also been
consolidated into the central
EXT:core/Resources/Private/Language/Form/palettes.xlf file.
The following palette labels are now available via the
core.form.palettes: prefix:
The following palette labels have been migrated to use
core.form.palettes:* and are marked as unused since TYPO3 v14.0 (attribute
x-unused-since="14.0" in XLF files):
With forge#103894 the new data processor PageContentFetchingProcessor
has been introduced, to allow fetching page content based on the current page
layout, taking the configured
SlideMode into account.
Fetching content has previously mostly been done via the Content content
object. A common example looked like this:
As mentioned in the linked changelog, using the page-content data processor,
this can be simplified to:
page.20 = page-content
Copied!
This however reduces the possibility to modify the select configuration
(SQL statement), used to define which content should be fetched, as this
is automatically handled by the data processor. However, there might be some
use cases in which the result needs to be adjusted, e.g. to hide specific
page content, like it's done by EXT:content_blocks
for child elements. For such use cases, the new PSR-14
AfterContentHasBeenFetchedEvent
has been introduced, which allows to manipulate the list of fetched page
content.
The following member properties of the event object are provided:
$groupedContent: The fetched page content, grouped by their column - as defined in the page layout
$request: The current request, which can be used to e.g. access the page layout in question
Example
The event listener class, using the PHP attribute
#[AsEventListener] for
registration, removes some of the fetched page content elements based on
specific field values.
Using the new PSR-14
AfterContentHasBeenFetchedEvent, it's possible
to manipulate the page content, which has been fetched by the
PageContentFetchingProcessor, based on the page layout and
corresponding columns configuration.
Important: #92187 - Evaluation of incoming HTTP Header X-Forwarded-Proto
At this point it is not known if the request between the client (the actual
web browser for example) and the reverse proxy was made via HTTP or HTTPS,
mainly because TYPO3 only evaluated the information from the reverse proxy
to TYPO3 - which was typically faked on the TYPO3's webserver by setting
"HTTPS=on" (for example via .htaccess file). In a typical setup, the communication
between the reverse proxy and TYPO3's webserver is done via HTTP and irrelevant
for TYPO3.
When the site owner knows that the reverse proxy acts as a SSL termination point
and only communicates via https to the client, the
$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxySSL'] option
can be set, to identify all reverse proxy IPs that ensure a secure connection
between client and reverse proxy.
In case, it is not known, and
reverseProxyPrefixSSL
is not in use, but
reverseProxyIP
is in use, the incoming HTTP header X-Forwarded-Proto is
now evaluated to determine if the request was made, if the header is sent.
If it is NOT sent, TYPO3 will assume to detect a secure connection between
SSL information as before via various other HTTP Headers or server configuration
settintgs.
Important: #103140 - Allow to configure rate limiters in Message consumer (Symfony Messenger)
This change introduces missing configuration options for Symfony Messenger-based
rate limiters.
A rate limiter controls how frequently a specific event (e.g., HTTP request
or login attempt) is allowed to occur. It acts as a safeguard to prevent services from
being overwhelmed — either accidentally or intentionally — thus helping
to maintain their availability.
Rate limiters are also useful for controlling internal or outbound
processes, such as limiting the simultaneous processing of messages.
Rate limiters can be defined in your service configuration
EXT:yourext/Configuration/Services.yaml. The name specified
in the settings is resolved to a service tagged with messenger.rate_limiter
and the corresponding identifier.
The
\TYPO3\CMS\Core\Log\Writer\DatabaseWriter is used to write logs into
the database table sys_log. Additional log information data is persisted
in the field data and has been prefixed with a - until now.
As this makes it harder to parse the data, which is JSON-encoded anyway,
the prefix has been removed.
Beware that existing log entries are not migrated automatically.
This leads to a mixed structure in the database table until old records are cleaned.
(TYPO3 itself does not interpret the content of the field.)
Important: #105007 - Manipulation of final search query in EXT:indexed_search
By removing the
searchSkipExtendToSubpagesChecking option in
forge#97530, there might have been performance implications for installations
with a lot of sites. This could be circumvented by adjusting the search query
manually, using available hooks. Since those hooks have also been removed with
forge#102937, developers were no longer be able to handle the query
behaviour.
Therefore, the PSR-14
BeforeFinalSearchQueryIsExecutedEvent has been
introduced which allows developers to manipulate the
QueryBuilder
instance again, just before the query gets executed.
Additional context information, provided by the new event:
searchWords - The corresponding search words list
freeIndexUid - Pointer to which indexing configuration should be searched in.
-1 means no filtering. 0 means only regular indexed content.
Important
The provided query (the
QueryBuilder instance) is controlled by
TYPO3 and is not considered public API. Therefore, developers using this
event need to keep track of underlying changes by TYPO3. Such changes might
be further performance improvements to the query or changes to the
database schema in general.
TYPO3 parses ext_tables.sql files into a Doctrine DBAL object schema to define
a virtual database scheme, enriched with
DefaultTcaSchema information for
TCA-managed tables and fields.
Fixed and variable length variants have been parsed already in the past, but missed
to flag the column as
$fixed = true for the fixed-length database field types
CHAR and
BINARY. This resulted in the wrong creation of these columns as
VARCHAR and
VARBINARY, which is now corrected.
ext_tables.sql
created as (before)
created as (now)
CHAR(10)
VARCHAR(10)
CHAR(10)
VARCHAR(10)
VARCHAR(10)
VARCHAR(10)
BINARY(10)
VARBINARY(10)
BINARY(10)
VARBINARY(10)
VARBINARY(10)
VARBINARY(10)
Not all database systems (RDBMS) act the same way for fixed-length columns. Implementation
differences need to be respected to ensure the same query/data behaviour across all supported
database systems.
Warning
Using fixed-length
CHAR and
BINARY column types requires to carefully work
with data being persisted and retrieved from the database due to differently
behaviour specifically of PostgreSQL.
Fixed-length
CHAR
Key Difference Between CHAR and VARCHAR
The main difference between
CHAR and
VARCHAR is how the database
stores character data in a database.
CHAR, which stands for character,
is a fixed-length data type, meaning it always reserves a specific amount of
storage space for each value, regardless of whether the actual data occupies
that space entirely. For example, if a column is defined as
CHAR(10) and
the word apple is stored inside of it, it will still occupy 10 characters worth of
space (not just 5). Unusued characters are padded with extra spaces.
On the other hand,
VARCHAR, short for variable character, is a
variable-length data type. It only uses as much storage space as needed
to store the actual data without padding. So, storing the word apple in a
VARCHAR(10) column will only occupy 5 characters worth of
space, leaving the remaining table row space available for other data.
The main difference from PostgreSQL to MySQL/MariaDB/SQLite is:
PostgreSQL also returns the filler-spaces for a value not having the
column length (returning apple[space][space][space][space][space]).
On top of that, the filled-up spaces are also respected for query conditions, sorting
or data calculations (
concat() for example). These two facts makes a huge
difference and must be carefully taken into account when using
CHAR
field.
Rule of thumb for fixed-length
CHARcolumns
Only use with ensured fixed-length values (so that no padding occurs).
For 255 or more characters
VARCHAR or
TEXT must be used.
More hints for fixed-length
CHARcolumns
Ensure to write fixed-length values for
CHAR (non-space characters),
for example use hash algorithms which produce fixed-length hash identifier
values.
Ensure to use query statements to trim OR rightPad the value within
WHERE,
HAVING or
SELECT operations, when values are
not guaranteed to contain fixed-length values.
Tip
Helper
\TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder
expressions can be used, for example
\TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder->trim() or
\TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder->rightPad() to.
Usage of
CHARmust be avoided when using the column with the
Extbase ORM, because fixed-value length cannot be ensured due to the
lack of using trim/rightPad within the ORM generated queries. Only with ensured
fixed-length values, it is usable with Extbase ORM.
Cover custom queries extensively with functional tests executed against
all supported database platforms. Code within public extensions should ensure to test
queries and their operations against all officially TYPO3-supported database platforms.
Example of difference in behaviour of fixed-length
CHAR types
Example ext_tables.sql defining a fixed-length tt_content field
Because of this, retrieved values need to be trimmed OR padded AFTER
the query results are fetched, to ensure the same retrieved value across all
supported database systems. Another option is to ensure that the persisted
data always has a fixed-value length, like by using the aforementioned hashing
algorithms (making results not human-readable).
To raise the awareness for problems on this topic, using the trimmed value inside
a
WHERE condition will match the record, but the returned value will be different
from the value used in the condition:
CHAR and
BINARY fields can be used (for storage or performance adjustments),
but only when composed data and queries take care of database-system differences.
Otherwise, the "safe bet" is to consistently utilize
VARCHAR and
VARBINARY
columns types.
Important: #105653 - Require a template filename in extbase module template rendering
With the introduction of the FluidAdapter in TYPO3 v13, the dependency between
Fluid and Extbase has been decoupled. As part of this change, the behavior of
the
ModuleTemplate::renderResponse() and
ModuleTemplate::render()
methods has been adjusted.
The
$templateFileName argument is now mandatory for the
ModuleTemplate::renderResponse() and
ModuleTemplate::render()
methods. Previously, if this argument was not provided, the template was
automatically resolved based on the controller and action names. Starting from
TYPO3 13.4, calling these methods with an empty string or without a valid
$templateFileName will result in an
InvalidArgumentException.
Extensions using Extbase backend modules must explicitly provide the
$templateFileName when calling these methods. Existing implementations
relying on automatic template resolution need to be updated to prevent
runtime errors.
Note, that it is already possible to explicitly provide the
$templateFileName in TYPO3 12.4. It is therefore recommended to
implement the new requirement for websites using TYPO3 12.4.
Important: #105703 - Premature end of script headers due to X-TYPO3-Cache-Tags
The X-TYPO3-Cache-Tags header is now split into multiple lines if it exceeds the maximum
of 8000 characters. This change prevents premature end of script headers and ensures
that the header is sent correctly, even if it contains a large number of cache tags.
Affected installations
This change affects all TYPO3 installations that have $GLOBALS['TYPO3_CONF_VARS']['FE']['debug']
enabled and misusing the X-TYPO3-Cache-Tags header for anything else then debugging.
If you have a large number of cache tags, the header is now split into multiple
lines to avoid exceeding the maximum header size limit imposed by some web servers.
As this header is for debugging purposes only, this does not effect any production
environments.
Important: #106401 - Treat 0 as a defined value for nullable datetime fields
For nullable integer-based datetime fields, the value 0 now explicitly
represents the Unix epoch time (1970-01-01T00:00:00Z) instead of being
interpreted as an empty value by FormEngine.
Only an explicit null database value will be considered an empty value.
The default database schema that is generated from TCA has been adapted
to generate datetime columns with
DEFAULT NULL instead of
DEFAULT 0 if they have been configured to be nullable.
Extbase handling of
\DateTimeInterface domain model properties has been
aligned with the persistence and database value interpretation behavior of the
TYPO3 Core Engine (FormEngine and DataHandler).
Since this change addresses bugs and value interpretation differences that
existed since the introduction of Extbase and there are many workarounds in use,
a feature flag
'extbase.consistentDateTimeHandling' is introduced which
allows to enable the new behavior.
Existing TYPO3 v13 instances will use the old behavior by default and are
advised to enable the new feature flag via InstallTool or via:
TYPO3 v14 (and new v13 instances) enable the consistent DateTime handling
by default, but the feature can still be disabled manually, if needed for the
time being.
There are four different behavioural changes that will be activated and are
explained in the following sections.
Align persistence to database to match DataHandler algorithm
Use the DataHandler algorithm for the mapping of DateTime objects
to database values.
This causes non-localtime timezone offsets in
\DateTime objects
(e.g. supplied by a frontend datepicker) to be respected for native
datetime fields, like already done for integer based datetime fields.
Note that the offset is not stored as-is, but mapped to PHP localtime,
but the offset is no longer cropped off.
That means there is no need to force the server timezone on
\DateTime
objects before persisting an extbase model, since all dates will be
normalized to localtime (for native datetime fields) or UTC (for interger based
datetime fields) within the persistence layer.
Before:
publicfunctionsetDatetime(\DateTime $datetime): void{
// Force local datetime zone in order to avoid// cropping non localtime offsets during persistence
$datetime->setTimezone(
new\DateTimeZone(date_default_timezone_get())
);
$this->datetime = $datetime;
}
Copied!
After:
publicfunctionsetDatetime(\DateTime $datetime): void{
// No timezone enforcement needed, persistence layer will// persist correct point in time (UTC for integer, LOCALTIME for native// fields)$this->datetime = $datetime;
}
Copied!
Map date and datetime with named timezone instead of offset
Extbase DataMapper converts dates of integer based database fields to
\DateTime instances that use the current server date timezone
(e.g., Europe/Berlin) and not just the time offset of the current server
timezone (e.g., +01:00).
This prevents timezone shifts when modifying the resulting
\DateTime
object across daylight saving time boundaries.
Previous workarounds that explicitly added the server timezone for properties
can be removed:
publicfunctiongetDatetime(): ?\DateTime{
// object(DateTimeZone)#2 (2) {// ["timezone_type"]=>// int(3)// ["timezone"]=>// string(13) "Europe/Berlin"// }
var_dump($this->datetime);
// No explicit timezone needed for a proper named timezonereturn$this->datetime;
}
Copied!
Interpret integer based time fields as seconds without timezone offset
The Extbase DataMapper will interpret format=time or format=timesec
datetime fields as seconds without timezone offset, like FormEngine and
DataHandler do. The database value is no longer considered as a UNIX timestamp,
but as offset from midnight mapped on 1970-01-01T00:00:00 in PHP localtime.
For european timezones where Central Europe Time (CET) was active on 1970-01-01
that means an integer field value like 7200 (=02:00) will be mapped to
1970-01-01T02:00:00+01:00 instead of 1970-01-01T02:00:00+00:00 and the
DateTime::$timezone property of the
DateTime object will be set to
the named timezone that is configured in PHP ini setting date.timezone instead
of UTC.
That means the datetime value can be combined with explicit dates and is always
using the server timezone.
Interpret 00:00:00 as non empty time value for nullable time properties
Nullable format=time, format=timesec or dbType=time fields can now
use 00:00:00 to represent midnight (this value has been used in
non-nullable fields to represent an empty value). The DateTime mapper now
understands this value instead of misinterpreting it as an empty value.
This behaviour could not be worked around before, that means existing
implementations do not need to change or remove workarounds, but can basically
support 00:00 as a value time field now.
Construct format=time and dbType=time properties based on 1970-01-01
DateTime objects that map to native TIME fields or integer based fields
configured with format=time are now initialized with 1970-01-01 as day-part
instead of the current day which results in consistent mapped values independent
from the day where the mapping is performed.
When dealing with relations to multilingual Extbase entities, these relations should always store
their reference to the "original" (sys_language_uid=0) entity, so that later on,
language record overlays can be properly applied.
For this to work in areas like persistence, internally an "identifier" is established that references
these multilingual objects like [defaultLanguageRecordUid]_[localizedRecordUid].
Internally, this identifier should be converted back to only contain/reference the defaultLanguageRecordUid.
A bug has been fixed with #106494 to deal with this inside the <f:form.select> ViewHelper, which utilized
an <option value="11_42"> (defaultLanguageRecordUid=11, localizedRecordUid=42), and when using an <f:form>
to edit existing records, the currently attached records would NOT get pre-selected.
When such objects with relations were persisted (in frontend management interfaces with Extbase), if the proper
option had not been selected again, the relation would get lost.
Important: Adapt custom ViewHelpers extended from AbstractFormFieldViewHelper or using persistenceManager->getIdentifierByObject()
The bug has been fixed, but it is important that if third-party code created custom ViewHelpers based on
\TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper , these may need adoption too.
Instead of using code like this:
Example ViewHelper code utilizing persistenceManager->getIdentifierByObject()
if ($this->persistenceManager->getIdentifierByObject($valueElement) !== null) {
return$this->persistenceManager->getIdentifierByObject($valueElement);
}
Copied!
the code should be adopted to not rely on getIdentifierByObject() but instead:
Refactored ViewHelper code preferring an object's getUid() method instead
if ($this->persistenceManager->getIdentifierByObject($valueElement) !== null) {
if ($valueElement instanceof DomainObjectInterface) {
return $valueElement->getUid() ?? $this->persistenceManager->getIdentifierByObject($valueElement);
}
return$this->persistenceManager->getIdentifierByObject($valueElement);
}
Copied!
This code ensures that retrieving the relational object's UID is done with the overlaid record,
and only falls back to the full identifier, if it's not set, or not an object implementing the Extbase
DomainObjectInterface.
Also note that the abstract's method
convertToPlainValue() has been fixed to no longer return
a value of format [defaultLanguageRecordUid]_[localizedRecordUid] but instead always use the
original record's ->getUid() return value (=defaultLanguageRecordUid).
If this method
convertToPlainValue() is used in 3rd-party code, make sure this is the
expected result, too.
Important: #106508 - Respect column CHARACTER SET and COLLATE in ext_tables.sql
TYPO3 now reads column based CHARACTER SET and COLLATION from extension
ext_tables.sql files and applies them on column level. This allows
CHARACTER SET and COLLATION column settings different than defaults defined
on table or schema level. This is limited to MySQL and MariaDB DBMS.
Note
Setting different charset and collation comes with some technical impact
during query time and requires for some queries special handling, for instance
when joining field that have different charsets or collations. Setting special
charsets and collations for single columns should only be used in rare
cases. The support is @internal and should be used with care if at all.
For now,
CHARACTER SET ascii COLLATE ascii_bin is used for
sys_refindex.hash to reduce required space for the index using
single bytes instead of multiple bytes per character.
The introduced database change is considerable non-breaking, because:
Not applying the database changes still keeps a fully working state.
Applying database schema change does not require data migrations.
This tree representation is easier to write, but has the
drawback that arbitrary keys like foo.bar and foo.bar.baz
exclude each other, as the subkey baz would be represented
as a value of foo.bar in tree representation.
Note that TypoScript constants can express subkey constants since
TypoScript can store a value and childnodes for every node, which
means that existing extensions – that migrate to site sets – require
this mixture of setting identifiers to be supported in order to
avoid breaking existing settings.
The storage format of settings.yaml is now changed to use
a map (like settings.definitions.yaml already do) to store
setting values, in order to overcome the mentioned limitation.
It is still supported to read from a tree, but the settings editor
will convert the tree to a map when persisting values.
A map will be stored in settings.yaml that is able to store values for
both setting identifiers:
typo3conf/sites/mysite/settings.yaml
foo.bar:'Foo bar value'foo.bar.baz:'Foo Bar baz value'
Copied!
Also site sets are advised to use this format for settings provided in their sets
settings.yaml file.
Existing anonymous settings (pre v13 style, e.g. settings without a
matching settings.definitions.yaml definition) will be preserved as
a tree, since it is not known which tree node is key or a value.
Important: #107062 - Avoid applying Content-Security-Policy nonce sources when not required
Using nonce sources in a Content-Security-Policy (CSP) HTTP header implicitly leads
to having a Cache-Control: private, no-store HTTP response header and internally
requires to renew the nonce value that is present in cached HTML contents, which
has a negative impact on performance.
This change aims for having fully cached pages and tries to avoid nonce sources
in the CSP header when actually feasible.
The
ConsumableNonce class was refactored – it no longer extends
ConsumableString.
Two new counters are introduced:
consumeInline() – the nonce is required for an inline resource.
consumeStatic() – the nonce is optional for a static resource.
Nonce sources are removed from the CSP policy in the following
situations, in case the request is supposed to be fully cacheable
(config.no_cache = 0 and not having any USER_INT or COA_INT items):
The response body is readable and contains no bytes.
The nonce consumption counter for all usages equals zero.
A directive contains a source‑keyword exception (e.g. 'unsafe-inline')
that makes a nonce unnecessary.
The
PolicyPreparedEvent has been dispatched and explicitly tells
the policy to avoid using nonce sources.
When the nonce should be removed, both the frontend and backend
ContentSecurityPolicyHeaders middleware strip the nonce-related
literals from the rendered HTML.
New PSR-14 event
<?phpdeclare(strict_types=1);
namespaceExample\MyPackage\EventListener;
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Core\Security\ContentSecurityPolicy\Disposition;
useTYPO3\CMS\Core\Security\ContentSecurityPolicy\Event\PolicyPreparedEvent;
#[AsEventListener('my-package/content-security-policy/avoid-nonce')]finalclassDropNonceEventListener{
publicfunction__invoke(PolicyPreparedEvent $event): void{
$policyBag = $event->policyBag;
if (
isset($policyBag->dispositionMap[Disposition::enforce])
&& $policyBag->scope->siteIdentifier === 'my-special-site'// YOLO: drop nonce sources, even it is consumed
&& $policyBag->nonce->count() > 0
) {
$policyBag->behavior->useNonce = false;
}
}
}
Copied!
New
useNonce property
The
\TYPO3\CMS\Core\Security\ContentSecurityPolicy\Configuration\Behavior
class now contains the nullable boolean property
useNonce:
true - explicitly allows using nonce sources
null - the default unspecific state (the system will detect and decide automatically)
false - explicitly denies using nonce sources, it also drops constraints like
'strict-dynamic' since that source keyword requires a nonce source
With the upgrade to CKEditor 5 v46.1.0, three relevant changes are made:
The API naming of TypeScript type imports has changed a lot. Any custom
CKEditor 5 plugin using TypeScript type imports in their build chain will
need to be adapted to match these imports.
See https://ckeditor.com/docs/ckeditor5/latest/updating/nim-migration/migrating-imports.html
for a large table of "before->after" renames.
This is not considered a breaking change in context of TYPO3 integration,
because existing JavaScript modules will continue to work, as TypeScript
type imports are not part of the final output. Runtime imports that are
exposed by the @ckeditor5/ckeditor-* modules have not been changed
and will continue to work.
A new opinionated default CSS is used by CKEditor to apply some
improved styling over contents displayed within the RTE interface.
Most of these are overruled by TYPO3's default CSS integration though.
Possible customizations need to respect this.
With this change, the method signature of
listForms(), defined by the
FormPersistenceManagerInterface,
has been extended by two arguments:
$orderField and
$orderDirection.
The new definition is:
public function listForms(array $formSettings, string $orderField = '', ?SortDirection $orderDirection = null): array;
Affected Installations
Some TYPO3 installations may use this interface for their own FormPersistenceManager, even though it is marked as internal.
Possible Migration
If you have implemented your own FormPersistenceManager, you need to update the method signature accordingly.
Important: #107594 - Icon overlay for TCA select items
The ability to define an icon overlay for items in the "New Content Element"
wizard was originally introduced in forge#92942 using Page TSconfig, but was
accidentally removed during the web-component migration in forge#100065 and
then restored in forge#105253.
In the meantime, forge#102834 added auto-registration of wizard items
directly from TCA. Since icon overlays defined in Page TSconfig duplicate
configuration that can now be specified in TCA, the recommended approach is to
define icon overlays directly in TCA using the new
iconOverlay option
for select items.
The
iconOverlay property is now supported in the
SelectItem
component, enabling icon overlays for wizard items that are auto-registered
via TCA.
Impact
Icon overlays for New Content Element Wizard items can now be defined directly
in TCA alongside other item properties like
icon,
label,
description, and
group.
This consolidates configuration in a single location and eliminates the need
for separate Page TSconfig definitions. Page TSconfig icon overlays remain
supported for backward compatibility, but TCA-based configuration is now the
recommended approach.
Migration
Previous approach using Page TSconfig (still works, but no longer recommended):
While Page TSconfig-based icon overlay configuration remains functional for
backward compatibility, it is recommended to migrate to TCA-based
configuration to avoid duplicating configuration across multiple files.
Important: #107649 - Dependency Injection cache is now PHP version dependant
TYPO3 uses the PHP library symfony/dependency-injection to build a dependency
injection container that contains class factories for services used by TYPO3 or
by installed extensions.
With the update to symfony/dependency-injection v7.3 – which may be installed
in TYPO3 v13 composer mode – the created factories are optimized to use certain
PHP language level features, if available, which result in a cache that is
incompatible when used with older PHP versions.
In a scenario where the dependency injection cache is created in a CLI PHP
process (e.g. PHP v8.4), this may result in a cache to be created that is
incompatible with a Web PHP process (e.g. PHP v8.2), if the minor versions
of the CLI and Web environments differ.
For this reason the major and minor PHP version numbers are now hashed into the
dependency injection cache identifier, resulting in a possible cache-miss on the
first web-request after a deployment, if the system was prepared via
bin/typo3 with a CLI PHP process version that is different to the Web
PHP version.
Make sure to configure the PHP CLI process version
php -v to use
the same version number as configured for the Web process. The Web process
version can be introspected in the backend toolbar entry
System Information > PHP Version.
Important: #107681 - Disabled state for ShortcutButton and DropDownButton
The
ShortcutButton and
DropDownButton classes in the TYPO3
backend button bar system have been enhanced with new methods to support
disabled state functionality. This brings them in line with other button
types that already support the disabled state.
Two new methods have been added to both classes:
isDisabled(): bool - Checks if the button is disabled
setDisabled(bool $disabled) - Sets the disabled state of the button
When a button is disabled, it is rendered with appropriate HTML attributes
and CSS classes to indicate its non-interactive state. For
ShortcutButton,
the disabled state is applied to both the simple button rendering and the
dropdown rendering modes.
Impact
Extension developers can now programmatically disable shortcut and dropdown
buttons in the TYPO3 backend, preventing user interaction when needed. This is
particularly useful for:
Preventing operations during form initialization
Disabling buttons during async operations
Conditional button availability based on application state
Improving user experience on slow network connections
The disabled state is properly propagated through the rendering process:
For shortcut buttons rendered as
GenericButton, the disabled
attribute is added to the button element
For shortcut buttons rendered as
DropDownButton, the disabled
state is passed to the dropdown button
Migration
No migration is required. This change is fully backward compatible as it only
adds new optional functionality. Existing code will continue to work without
modifications.
After TYPO3 v13.0, only new functionality with a solid migration path
can be added on top, with aiming for as little as possible breaking changes
after the initial v13.0 release on the way to LTS.
Historically, plugins have been registered using the list content
element and the plugin subtype list_type field. This functionality
has been kept for backwards compatibility reasons. However, since the release
of TYPO3 v12.4, the recommended way to create a plugin is by using a dedicated
content type (CType) for each plugin.
This old "General Plugin" approach has always been ugly from a UX perspective point
of view since it hides plugin selection behind "General plugin" content element,
forcing a second selection step and making such plugins something special.
Therefore, the plugin content element (list) and the plugin sub types
field (
list_type) have been marked as deprecated in TYPO3 v13.4 and will
be removed in TYPO3 v14.0.
Additionally, the related PHP constant
TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_PLUGIN has been
deprecated as well.
Impact
Plugins added using
TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin() where
the second parameter is
list_type (which is still the default) will
trigger a deprecation level log entry in TYPO3 v13 and will fail in v14.
Therefore, the same applies on using
TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin() (to
configure the plugin for frontend rendering), where no fifth parameter is
provided or where the fifth parameter is
list_type
(
ExtensionUtility::PLUGIN_TYPE_PLUGIN), which is still the default.
The extension scanner will report any usage of
configurePlugin(),
where less than the required five arguments are provided. Actually, the
only valid value for the fifth parameter
$pluginType is
CType,
for which the
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT constant
can be used.
Note
addPlugin() is also internally called when registering a plugin
via
TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerPlugin().
In that case the plugin type to use is either the one defined via
TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin()
or also falls back to
list_type.
Affected installations
Extensions registering plugins as list_type plugin sub type.
Migration
Existing plugins must be migrated to use the CType record type.
Extension authors must implement the following changes:
Register plugins using the CType record type
Create update wizard which extends
\TYPO3\CMS\Install\Updates\AbstractListTypeToCTypeUpdate
and add list_type to CType mapping for each plugin to migrate.
The migration wizard for indexed_search in class
\IndexedSearchCTypeMigration
can be used as reference example.
Migrate possible FlexForm registration and add dedicated showitem TCA
configuration
Migrate possible PreviewRenderer registration in TCA
Adapt possible content element wizard items in Page TSConfig, where
list_type is used
Adapt possible content element restrictions in backend layouts or container
elements defined by third-party extensions like
ichhabrecht/content-defender
.
Common example
// Before
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['my_plugin'] = 'pi_flexform';
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist']['my_plugin'] = 'pages,layout,recursive';
// After
$GLOBALS['TCA']['tt_content']['types']['my_plugin']['showitem'] = '<Some Fields>,pi_flexform,<Other Fields>';
The old TypoScript syntax to import external TypoScript files based on
<INCLUDE_TYPOSCRIPT: has been marked as deprecated
with TYPO3 v13 and will be removed in v14.
Integrators should switch to
@import.
There are multiple reasons to finally phase out this old construct:
The
<INCLUDE_TYPOSCRIPT: syntax is clumsy and hard to grasp,
especially when combining multiple options. It is hard to learn for integrators
new to the project.
The implementation has a high level of complexity, is only partially tested
and consists of edge cases that are hard to decide on and even harder to change
since that may break existing usages in hard to debug ways.
The syntax can have negative security impact when not used wisely by loading
TypoScript from editor related folders like fileadmin/. The syntax
based on
@import has been designed more thoughtful in this regard.
Loading TypoScript files from folders relative to the public web folder is
unfortunate and can have negative side effects when switching "legacy" based
instances to composer.
The syntax based on
@import has been introduced in TYPO3 v9 already
and has been designed to stay at this point in time. With TYPO3 v12, the
last missing feature of
<INCLUDE_TYPOSCRIPT: has been made possible
for
@import as well:
@import can be loaded conditionally
by putting them into the body of a casual TypoScript condition.
TYPO3 v12 discouraged using
<INCLUDE_TYPOSCRIPT: within the documentation
and already anticipated a future deprecation and removal.
Impact
When using TypoScript
<INCLUDE_TYPOSCRIPT: syntax a deprecation level
log entry in TYPO3 v13 is emitted. The syntax will stop working with TYPO3 v14 and
will be detected as an "invalid line" in the TypoScript related Backend modules.
Affected installations
Instances TypoScript syntax based on
<INCLUDE_TYPOSCRIPT:.
Migration
Most usages of
<INCLUDE_TYPOSCRIPT: can be turned into
@import
easily. A few examples:
# Before<INCLUDE_TYPOSCRIPT: source="FILE:EXT:my_extension/Configuration/TypoScript/myMenu.typoscript"># After@import 'EXT:my_extension/Configuration/TypoScript/myMenu.typoscript'# Before# Including .typoscript files in a single (non recursive!) directory<INCLUDE_TYPOSCRIPT: source="DIR:EXT:my_extension/Configuration/TypoScript/" extensions="typoscript"># After@import 'EXT:my_extension/Configuration/TypoScript/*.typoscript'# Before# Including .typoscript and .ts files in a single (non recursive!) directory<INCLUDE_TYPOSCRIPT: source="DIR:EXT:my_extension/Configuration/TypoScript/" extensions="typoscript,ts"># After@import 'EXT:my_extension/Configuration/TypoScript/*.typoscript'# Rename all files ending on .ts to .typoscript# Before# Including a file conditionally<INCLUDE_TYPOSCRIPT: source="FILE:EXT:my_extension/Configuration/TypoScript/user.typoscript" condition="[frontend.user.isLoggedIn]"># After[frontend.user.isLoggedIn]@import 'EXT:my_extension/Configuration/TypoScript/user.typoscript'[END]
Copied!
There are a few more use cases that cannot be transitioned so easily since
@import is
a bit more restrictive.
As one restriction
@import cannot include files from arbitrary directories
like fileadmin/, but only from extensions by using the
EXT:
prefix. Instances that use
<INCLUDE_TYPOSCRIPT: with
source="FILE:./someDirectory/..."
should move this TypoScript into a project or site extension. Such instances are also encouraged to
look into the TYPO3 v13 "Site sets" feature and eventually transition towards it along the way.
@import allows to import files with the file ending .typoscript
and .tsconfig. If you used any of the outdated file endings like .ts or
.txt rename those files before switching to the
@import syntax.
The
@import feature does not support recursive directory inclusion, as it does
not allow wildcards in directory paths. Having directories like TypoScript/foo and
TypoScript/bar, each having .typoscript files, could be included using
<INCLUDE_TYPOSCRIPT: source=DIR:EXT:my_extension/Configuration/TypoScript extensions="typoscript">,
which would find such files in foo and bar, and any other directory. This level of complexity
was not wished to allow in the
@import syntax since it can make file includes more intransparent
with too much attached magic. Instances using this should either reorganize their files, or have multiple dedicated
@import statements. The need for recursive includes may also be mitigated by restructuring
TypoScript based functionality using "Site sets".
The transition from
<INCLUDE_TYPOSCRIPT: can be often further relaxed with these features in mind:
TypoScript provider for sites and sets automatically loads TypoScript
per site when located next to site config.yaml files as constants.typoscript and setup.typoscript,
which is a good alternative to files in fileadmin and similar. Note that configured site sets TypoScript
are loaded before, so constants.typoscript and setup.typoscript are designed to adapt site set
TypoScript to specific site needs.
One of the main features of TCA are the record types. This allows to use
a single table for different purposes and in different contexts. The most
known examples of using record types are the "Page Types" of
pages
and the "Content Types" of
tt_content. For every specific type of
such table, it's possible to define the fields to be used and even
manipulate them e.g. change their label.
A special case since ever has been the plugin registration. This for
a long time has been done using the so called "sub types" feature of TCA.
This is another layer below record types and allows to further customize
the behaviour of a record type using another select field, defined via
subtype_value_field as well as defining fields to be added
-
subtypes_addlist - or excluded -
subtypes_excludelist - for
the record type, depending on the selected sub type.
For a couple of version now, it's encouraged to register plugins just
as standard content elements via the
tt_content type field
CType.
Therefore, the special registration via the combination of the
list
record type and the selection of a sub type via the
list_type field
has already been deprecated with Deprecation: #105076 - Plugin content element and plugin sub types.
Since the "sub types" feature was mainly used for this scenario only, it has
now been deprecated as well. Registration of custom types should therefore
always be done by using record types. This makes configuration much cleaner
and more comprehensible.
Impact
Using
subtype_value_field in a TCA types configurations will
lead to a deprecation log entry containing information about where
adaptations need to take place.
Affected installations
All installations using the sub types feature by defining a
subtype_value_field in a TCA types configuration, which
is really uncommon as the feature was mainly used for plugin
registration in the
tt_content table only.
Migration
Replace any
subtype_value_field configuration with dedicated record
types. Please also consider migrating corresponding
subtypes_addlist
and
subtypes_excludelist definitions accordingly.
Class
\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController and
its global instance
$GLOBALS['TSFE'] have been marked as deprecated.
The class will be removed with TYPO3 v14.
Impact
Calling
TypoScriptFrontendController methods, or accessing state from
$GLOBALS['TSFE'] is considered deprecated.
Affected installations
Various instances may still retrieve information from
$GLOBALS['TSFE'] .
Remaining uses should be adapted. The extension scanner will find possible
matches.
To keep backwards compatibility in TYPO3 v13, some calls can not raise
deprecation level log messages.
The backend layout related data object class
\TYPO3\CMS\Backend\View\BackendLayout\DataProviderContext
has been turned into a data object using public constructor property promotion (PCPP).
All
setX() and
getX() methods have been marked as deprecated in TYPO3 v13.4 and
will be removed with TYPO3 v14.0. The class will be declared
readonly in TYPO3 v14.0
which will enforce instantiation using PCPP. The class has been declared final since it is
an API contract that must never be changed or extended. The constructor arguments will be
declared non-optional in TYPO3 v14.0.
Calling the getters or setters raises deprecation level log errors and will stop working
in TYPO3 v14.0.
Affected installations
This data object is only relevant for instances with extensions that add custom backend layout
data providers using
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider'] .
There are few known extensions that do this. The extension scanner is not configured to find
possible usages since the method names are too generic and would lead to too many false positives.
Migration
Create new objects using PCPP with named arguments instead of the setters.
Instances should be created using
new():
// Before
$dataProviderContext = GeneralUtility::makeInstance(DataProviderContext::class);
$dataProviderContext
->setPageId($pageId)
->setData($parameters['row'])
->setTableName($parameters['table'])
->setFieldName($parameters['field'])
->setPageTsConfig($pageTsConfig);
// After
$dataProviderContext = new DataProviderContext(
pageId: $pageId,
tableName: $parameters['table'],
fieldName: $parameters['field'],
data: $parameters['row'],
pageTsConfig: $pageTsConfig,
);
Copied!
Use the properties instead of the getters, example:
// Before
$pageId = $dataProviderContext->getPageId()
// After
$pageId = $dataProviderContext->pageId
Copied!
Deprecation: #105279 - Replace TYPO3 EnumType with Doctrine DBAL EnumType
TYPO3 did provide a custom Doctrine DBAL column type implementation for the native
SQL type
ENUM that was only compatible with MySQL and MariaDB connections.
The Doctrine DBAL Team implemented
ENUM support with
Release 4.2.0 in
class
\Doctrine\DBAL\Types\EnumType, only supporting MySQL and MariaDB
as well.
TYPO3 removed its custom implementation with TYPO3 v13.4.0.
Class
\TYPO3\CMS\Core\Database\Schema\Types\EnumType has been marked as deprecated
and is replaced with an class alias of
\EnumType.
The alias will be removed with TYPO3 v14.
doctrine/dbal
>= 4.2.0 is incompatible with TYPO3 versions before v13.4.0.
Composer-based instances using TYPO3 v13.3 or older should add an according
conflict to their composer.json.
Affected installations
Instances using the the
ENUM type directly or by any third party
extension using TYPO3 13.0 to 13.3 in Composer mode will break, when
the
doctrine/dbal
Composer packages is updated to version 4.2.0
or newer.
Migration
Upgrade (directly) to TYPO3 v13.4 or ensure to avoid updating
Doctrine DBAL to 4.2.x or newer versions in Composer-based instances.
Replace
\TYPO3\CMS\Core\Database\Schema\Types\EnumType type
declarations with
\Doctrine\DBAL\Types\EnumType.
Deprecation: #105297 - tableoptions and collate connection configuration
The possibility to configure default table options like charset and collation for the database
analyzer has been introduced using the array
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['tableoptions'] with
sub-array keys
charset and
collate. These were only used for MySQL and MariaDB
connections.
Since TYPO3 v11 the
tableoptions keys were silently migrated to
defaultTableOptions, which is the proper Doctrine DBAL connection option for
for MariaDB and MySQL.
Furthermore, Doctrine DBAL 3.x switched from using they array key
collate to
collation, ignoring the old array key with Doctrine DBAL 4.x. This was silently
migrated by TYPO3, too.
These options and migration are now deprecated in favor of using the final array
keys and will be removed with TYPO3 v15 (or later) as breaking change.
Note
When migrating, make sure to remove the old
tableoptions array key, otherwise
it will take precedence over setting the new
defaultTableOptions key in TYPO3 v13.
Impact
Instances using the database connection options in
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['tableoptions']
array, or using the
collate key will trigger a
E_USER_DEPRECATED
notification.
Affected installations
All instances using the mentioned options.
Migration
Review
settings.php and
additional.php and adapt the deprecated
configuration by renaming affected array keys.
The internal
\TYPO3\CMS\Frontend\Authentication\FrontendBackendUserAuthentication
class, used for frontend requests while being logged in the backend has been
moved from EXT:backend to EXT:frontend, since its dependencies are limited
to EXT:core and EXT:frontend.
While for v13 a class alias mapping and a legacy notation for IDE's is
available, the class is marked as @internal and therefore does not fall
under TYPO3's Core API deprecation policy.
After TYPO3 v13.0, only new functionality with a solid migration path
can be added on top, with aiming for as little as possible breaking changes
after the initial v13.0 release on the way to LTS.
Some additional fields were added to Page TSconfig
mod.linkvalidator.searchFields:
pages = canonical_link
sys_redirect = target
sys_file_reference = link
Two special fields are currently defined, but are
not checked yet due to their TCA configuration. For forward
compatibility, these are kept in the field configuration:
pages = media has TCA type="file"
tt_content = records has TCA type="group"
The following fields could theoretically be included in
custom configurations, as their type / softref is available,
but they are specifically not added in the default configuration:
sys_webhook = url (webhook should not be invoked)
tt_content = subheader (has softref email[subst]
which is not a supported link type)
Instead of having to use custom route aspect mappers, implementing
\TYPO3\CMS\Core\Routing\Aspect\StaticMappableAspectInterface ,
to avoid having &cHash= signatures
being applied to the generated URL, variables now can be simply declared
static in the corresponding route enhancer configuration.
Impact
By using the new static route configuration directive, custom aspect
mapper implementations can be avoided. However, static route variables
are only applied for a particular variable name if
there is no aspect mapper configured - aspect mappers are
considered more specific and will take precedence
there is a companion requirements definition which narrows the
set of possible values, and should be as restrictive as possible
to avoid potential cache flooding - static routes variables are
ignored, if there is no corresponding requirements definition
Example
routeEnhancers:Verification:type:SimpleroutePath:'/verify/{code}'static:code:truerequirements:# only allows SHA1-like hex values - which still allows lots# of possible combinations - thus, for this particular example# the handling frontend controller should be uncached as well## hint: if `static` is set, `requirements` must be set as wellcode:'[a-f0-9]{40}'
Copied!
As a result, using the URI query parameters &code=11f6ad8ec52a2984abaafd7c3b516503785c2072
would generate the URL https://example.org/verify/11f6ad8ec52a2984abaafd7c3b516503785c2072.
The TYPO3 system extension
typo3/cms-recycler
is now enabled by default for new TYPO3 installations.
Impact
New composer-based TYPO3 installations based on the TYPO3 CMS Base Distribution,
and new legacy installations (tarball / zip download) have the system extension recycler
enabled by default.
Feature: #99510 - Add file embedding option to asset ViewHelpers
The ViewHelpers <f:asset.css>
and <f:asset.script> have
been extended with a new argument
inline. If this argument is set,
the referenced asset file is rendered inline.
Setting the argument will therefore load the file content of the defined
href /
src as inline style or script. This is especially
useful for content elements which are used as first element on a page and
need some custom CSS to improve the Cumulative Layout Shift (CLS).
Impact
To add inline styles and scripts from a referenced file, the new
inline
argument can be set. For example, to add above-the-fold styles, the
priority option can be set, which will put the file contents of
EXT:sitepackage/Resources/Public/Css/my-hero.css as inline styles
to the
<head> section.
The new error handler
\TYPO3\CMS\Core\Error\PageErrorHandler\RedirectLoginErrorHandler
has been added, which makes it possible to redirect the user to a configurable page.
Requesting a login-protected URL would usually return a generic HTTP 403 error
in case of a missing fulfilled access permissions and the configuration
typolinkLinkAccessRestrictedPages = NONE (default)
is set.
By enabling this new handler via the site settings, the 403 response
can be handled and a custom redirect can be performed.
The
RedirectLoginErrorHandler
allows to define a
loginRedirectTarget, which must be configured to the page, where the
login process is handled. Additionally, the
loginRedirectParameter
must be set to the URL parameter that will be used to hand over the original
URL to the target page.
The redirect ensures that the original URL is added to the configured GET
parameter
loginRedirectParameter, so that the user can be redirected
back to the original page after a successful login.
The error handler allows
return_url or
redirect_url as values
for
loginRedirectParameter. Those values are used in extensions like
EXT:felogin or EXT:oidc.
Important
Redirection to the originating URL via URI arguments requires that
extensions like EXT:felogin are configured to allow these redirect modes
(for example via
plugin.tx_felogin_login.settings.redirectMode=getpost,loginError)
The new error handler works (with some minor exceptions) similar to the
"Forbidden (HTTP Status 403)" handler in TYPO3 extension
plan2net/sierrha
.
It will still emit generic 403 HTTP error messages in certain scenarios,
like when a user is already logged in, but the permissions are not
satisfied.
Impact
It is now possible to configure a login redirection process when a user has no
access to a page and a 403 error is thrown, so that after login the
originating URL is requested again. Previously, this required custom
Middlewares or implementations of
PageErrorHandlerInterface .
Feature: #101391 - Add base64 attribute to ImageViewHelper
The ViewHelpers <f:image> and
<f:uri.image> now
support the attribute
base64="true" that will provide
a possibility to return the value of the image's
src attribute
encoded in base64.
It is now possible to configure static routes with the type asset to link to
resources which are typically located in the directory
EXT:my_extension/Resources/Public/.
The
\TYPO3\CMS\Core\Page\AssetCollector options have been extended to
include an external
flag. When set for asset files using
$assetCollector->addStyleSheet()
or
$assetCollector->addJavaScript(), all processing of the asset
URI (like the addition of the cache busting parameter) is skipped and the input
path will be used as-is in the resulting HTML tag.
Example
The following code skips the cache busting parameter ?1726090820 for the
supplied CSS file:
GIFBUILDER, the image manipulation library for TypoScript based on GDlib, a PHP
extension bundled into PHP, now also supports generating resulting files of
type "avif".
AVIF is an image format, that is supported by most modern browsers, and usually
has a better compression (= smaller file size) than jpg files.
Important
Before using this feature, please check whether the used operating system
actually supports de/encoding AVIF files. Especially Debian 11 (Bullseye)
and older or systems forked from that may lack AVIF support.
Impact
If defined via format=avif within a GifBuilder setup, the generated files are
now AVIF files instead of png (the default).
It is possible to define the quality of a AVIF image similar to jpg images
globally via
$TYPO3_CONF_VARS['GFX']['avif_quality'] or via TypoScript's
"quality" property on a per-image basis. Via TypoScript it is also possible
to use the new property "speed" - see https://www.php.net/manual/en/function.imageavif.php
for more details.
A new test in the Environment module / Install Tool can be used to check if the
bundled GDlib extension of your PHP version supports the AVIF image format.
Feature: #102422 - Introduce CacheDataCollector Api
A new API has been introduced to collect cache tags and their corresponding
lifetime. This API is used in TYPO3 to accumulate cache tags from page cache and
content object cache.
The API is implemented as a new PSR-7 request attribute
'frontend.cache.collector', which makes this API independent from TSFE.
Every cache tag has a lifetime. The minimum lifetime is calculated
from all given cache tags. By default, the lifetime of a cache tag is set to
PHP_INT_MAX, so it expires many years in the future. API users must
therefore define the lifetime of a cache tag individually.
The current TSFE API is deprecated in favor of the new API, as the
current cache tag API implementation does not allow to set lifetime and
extension authors had to work around it.
Example
Add a single cache tag with 24 hours lifetime
useTYPO3\CMS\Core\Cache\CacheTag;
$cacheDataCollector = $request->getAttribute('frontend.cache.collector');
$cacheDataCollector->addCacheTags(
new CacheTag('tx_myextension_mytable', 86400)
);
Copied!
Add multiple cache tags with different lifetimes
useTYPO3\CMS\Core\Cache\CacheTag;
$cacheDataCollector = $request->getAttribute('frontend.cache.collector');
$cacheDataCollector->addCacheTags(
new CacheTag('tx_myextension_mytable_123', 3600),
new CacheTag('tx_myextension_mytable_456', 2592000)
);
Copied!
Remove a cache tag
useTYPO3\CMS\Core\Cache\CacheTag;
$cacheDataCollector = $request->getAttribute('frontend.cache.collector');
$cacheDataCollector->removeCacheTags(
new CacheTag('tx_myextension_mytable_123')
);
Copied!
Remove multiple cache tags
useTYPO3\CMS\Core\Cache\CacheTag;
$cacheDataCollector = $request->getAttribute('frontend.cache.collector');
$cacheDataCollector->removeCacheTags(
new CacheTag('tx_myextension_mytable_123'),
new CacheTag('tx_myextension_mytable_456')
);
Copied!
Get minimum lifetime, calculated from all cache tags
The following event should only be used in code that has no access to the
request attribute
'frontend.cache.collector', it is marked
@internal
and may vanish: It designed to allow passive cache-data signaling, without
exactly knowing the current context and not having the current request at hand.
It is not meant to allow for cache tag interception or extension.
Add cache tag without access to the request object
$this->eventDispatcher->dispatch(
new AddCacheTagEvent(
new CacheTag('tx_myextension_mytable_123', 3600)
)
);
Copied!
Feature: #103090 - Make link type label configurable
It is now possible to provide a translated label for custom link types.
For this, a new interface
\TYPO3\CMS\Linkvalidator\Linktype\LabelledLinktypeInterface has been
created, which offers the method
getReadableName for implementation.
That method can return the translated label.
The default abstract implementation
\TYPO3\CMS\Linkvalidator\Linktype\AbstractLinktype has been enhanced
to implement that interface. Any custom class extending this abstract is
able to override the method
getReadableName to provide the
custom translation.
Custom linktype classes should now configure a label by implementing the method
LabelledLinktypeInterface::getReadableName().
All existing custom implementations of the
AbstractLinktype class or the
LabelledLinktypeInterface
will continue to work as before, and will just continue to use the internal name of
the link type, instead of a translated label.
Feature: #103511 - Introduce Extbase file upload and deletion handling
TYPO3 now provides an API for file upload- and deletion-handling in Extbase
extensions, which allows extension developers to implement file uploads
more easily into Extbase Domain Models.
The scope of this API is to cover some of the most common use cases and to
keep the internal file upload and deletion process in Extbase as simple as
possible.
The API supports mapping and handling of file uploads and deletions for the
following scenarios:
Property of type
FileReference
in a domain model
Property of type
\TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference>
in a domain model
File uploads can be validated by the following rules:
minimum and maximum file count
minimum and maximum file size
allowed MIME types
image dimensions (for image uploads)
Additionally, it is ensured, that the filename given by the client is valid,
meaning that no invalid characters (null-bytes) are added and that the file
does not contain an invalid file extension. The API has support for custom
validators, which can be created on demand.
To avoid complexity and maintain data integrity, a file upload is only
processed if the validation of all properties of a domain model is successful.
In this first implementation, file uploads are not persisted/cached temporarily,
so this means in any case of a validation failure ("normal" validators and file upload
validation) a file upload must be performed again by users.
Possible future enhancements of this functionality could enhance the existing
#[FileUpload] attribute/annotation with configuration like a temporary storage
location, or specifying additional custom validators (which can be done via the PHP-API as
described below)
Nesting of domain models
File upload handling for nested domain models (e.g. modelA.modelB.fileReference)
is not supported.
File upload configuration with the FileUpload attribute
File upload for a property of a domain model can be configured using the
newly introduced
\TYPO3\CMS\Extbase\Annotation\FileUpload attribute.
All configuration settings of the
\TYPO3\CMS\Extbase\Mvc\Controller\FileUploadConfiguration object can
be defined using the
FileUpload
attribute. It is however not possible
to add custom validators using the
FileUpload attribute, which you
can achieve with a manual configuration as shown below.
The currently available configuration array keys are:
validation (
array with keys required, maxFiles, minFiles,
fileSize, fileExtension, allowedMimeTypes, mimeType, imageDimensions,
see File upload validation)
uploadFolder (
string, destination folder)
duplicationBehavior (
object, behaviour when file exists)
addRandomSuffix (
bool, suffixing files)
createUploadFolderIfNotExist (
bool, whether to create missing
directories)
It is also possible to use the
FileUpload annotation to configure
file upload properties, but it is recommended to use the
FileUpload attribute due to better readability.
Manual file upload configuration
A file upload configuration can also be created manually and should be
done in the
initialize*Action.
The configuration for a file upload is defined in a
FileUploadConfiguration object.
This object contains the following configuration options.
Hint
The appropriate setter methods or configuration
keys can best be inspected inside that class definition.
Property name:
Defines the name of the property of a domain model to which the file upload
configuration applies. The value is automatically retrieved when using
the
FileUpload attribute. If the
FileUploadConfiguration object
is created manually, it must be set using the
$propertyName
constructor argument.
Validation:
File upload validation is defined in an array of validators in the
FileUploadConfiguration object.
The validators
\TYPO3\CMS\Extbase\Validation\Validator\FileNameValidator ,
(ensures that no executable PHP files can
be uploaded) and
\TYPO3\CMS\Extbase\Validation\Validator\FileExtensionMimeTypeConsistencyValidator
(ensuring that the file extension matches the expected mime-type assumptions),
are enforced and executed by default.
In addition, Extbase includes the following validators to validate an
UploadedFile object:
Those validators can either be configured with the
FileUpload attribute or added
manually to the configuration object
with the
addValidator method.
Required:
Defines whether a file must be uploaded. If it is set to true, the
minFiles configuration is set to 1.
Minimum files:
Defines the minimum amount of files to be uploaded.
Maximum files:
Defines the maximum amount of files to be uploaded.
Upload folder:
Defines the upload path for the file upload. This configuration expects a
storage identifier (e.g.
1:/user_upload/folder/). If the given target
folder in the storage does not exist, it is created automatically.
Upload folder creation, when missing:
The default creation of a missing storage folder can be disabled via the
configuration attribute
createUploadFolderIfNotExist
(
bool, default
true).
Add random suffix:
When enabled, the filename of an uploaded and persisted file will contain a
random 16 char suffix. As an example, an uploaded file named
job-application.pdf will be persisted as
job-application-<random-hash>.pdf in the upload folder.
The default value for this configuration is
true and it is recommended
to keep this configuration active.
This configuration only has an effect when uploaded files are persisted.
Duplication behavior:
Defines the FAL behavior, when a file with the same name exists in the target
folder. Possible values are
DuplicationBehavior::RENAME (default),
DuplicationBehavior::REPLACE and
DuplicationBehavior::CANCEL.
Modifying existing configuration
File upload configuration defined by the
FileUpload attribute can be
changed in the
initialize*Action.
The example shows how to modify the file upload configuration for the argument
item and the property
file. The minimum amount of files to be
uploaded is set to
2 and a custom validator is added.
To remove all defined validators except the
FileNameValidator, use
the
resetValidators() method.
Using TypoScript configuration for file uploads configuration
When a file upload configuration for a property has been added using the
FileUpload attribute, it may be
required make the upload folder or
other configuration options configurable with TypoScript.
Extension authors should use the
initialize*Action to apply settings
from TypoScript to a file upload configuration.
Each uploaded file can be validated against a configurable set of validators.
The
validation section of the
FileUpload attribute allows to
configure commonly used validators using a configuration shorthand.
The following validation rules can be configured in the
validation
section of the
FileUpload attribute:
Extbase will internally use the Extbase file upload validators for
fileExtensionMimeTypeConsistency,
fileExtension,
fileSize,
mimeType and
imageDimensions validation.
Custom validators can be created according to project requirements and must
extend the Extbase
AbstractValidator .
The value to be validated is
always a PSR-7
UploadedFile object.
Custom validators can however not
be used in the
FileUpload attribute
and must be configured manually.
Shorthand notation for allowedMimeTypes
Using the
mimeType configuration array, all options of the MimeTypeValidator
can be set as sub-keys (since TYPO3 13.4.1):
The shorthand notation via
'allowedMimeTypes' continues to
exist, in case only the mime type validation is needed. However, it is recommended
to utilize the full
'mimeType' configuration array.
Example for an object with an
TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference>
property, containing multiple files and allowing to delete the first one
(iteration is possible within Fluid, to do that for every object of the collection):
Extbase will then handle file deletion(s) before persisting a validated
object. It will:
validate that minimum and maximum file upload configuration for the affected
property is fulfilled (only if the property has a
FileUpload )
delete the affected
sys_file_reference record
delete the affected file
Internally, Extbase uses
FileUploadDeletionConfiguration objects to track
file deletions for properties of arguments. Files are deleted directly without
checking whether the current file is referenced by other objects.
Apart from using this ViewHelper, it is of course still possible to manipulate
FileReference properties with custom logic before persistence.
New PSR-14 events
The following new PSR-14 event has been added to allow customization
of file upload related tasks:
ModifyUploadedFileTargetFilenameEvent
The
ModifyUploadedFileTargetFilenameEvent
allows event listeners to
alter a filename of an uploaded file before it is persisted.
Event listeners can use the method getTargetFilename() to retrieve the filename
used for persistence of a configured uploaded file. The filename can then be
adjusted via setTargetFilename(). The relevant configuration can be retrieved
via getConfiguration().
Impact
Extension developers can use the new feature to implement file uploads and
file deletions in Extbase extensions easily with commonly known Extbase
property attributes/annotations.
Feature: #103521 - Change table restrictions UI to combine read and write permissions
The tables_select and tables_modify fields of the be_groups table store
information about permissions to read and write into selected database tables.
Due to TYPO3's internal behavior, when write permissions are granted for some
tables, those tables are also automatically available for reading.
To make managing table permissions much easier and more efficient for
integrators, the separate form fields for Tables (listing) [tables_select] and
Tables (modify) [tables_modify] have been combined into a single UI element.
This field now offers separate radio buttons to define which tables the backend
user group should have permission to read and / or write. This is done by
selecting one of the "No Access", "Read" or "Read & Write" options.
To further improve the user experience, it is also possible to use the
"Check All", "Uncheck All" and "Toggle Selection" options for each permission.
Under the hood, when these permissions are processed, they are still saved
separately in the tables_select and tables_modify columns in the
be_groups table, as they were before.
To render this new table view and handle its behavior, a dedicated form
renderType tablePermission has been introduced, which is now set for
the tables_modify column. The tables_select column has been changed
to TCA type passthrough.
The new form element is defined through:
\TYPO3\CMS\Backend\Form\Element\TablePermissionElement .
It uses a dedicated data provider defined in:
\TYPO3\CMS\Backend\Form\FormDataProvider\TcaTablePermission .
The JavaScript code is handled by a new web component:
@typo3/backend/form-engine/element/table-permission-element.js.
When the
TcaTablePermission
data provider handles the configuration, it
reads table lists from both the tables_select and tables_modify
columns and combines them into a single array with unique table names.
Impact
Managing table permissions for backend user groups has been improved by
visually combining the Tables (listing) [tables_select] and
Tables (modify) [tables_modify] options, as well as by adding the
multi record selection functionality.
Note
These changes might affect custom integrations and modifications made to
the tables_select or tables_modify columns in the be_groups TCA.
Integrators who have modified the configuration for these fields should
verify if their code works and adapt it if needed.
Feature: #103576 - Allow defining opacity in TCA type=color element
A new boolean property opacity has been added to the TCA configuration of
a TCA type color element to allow defining colors with an opacity using
the RRGGBBAA color notation.
With forge#103783 the new
\TYPO3\CMS\Core\Domain\Record object has been
introduced. It is an
object representing a raw database record, based on TCA and is usually used in
the frontend (via Fluid Templates), when fetching records with the
RecordTransformationProcessor
(
record-transformation) or by collecting content elements with the
PageContentFetchingProcessor
(
page-content).
The Records API - introduced together with the Schema API in forge#104002 -
now expands the record's values for most common field types (known
from the TCA Schema) from their raw database value into "rich-flavored" values,
which might be
Record ,
FileReference ,
\TYPO3\CMS\Core\Resource\Folder or
\DateTimeImmutable objects.
This works for the following "relation" TCA types:
category
file
folder
group
inline
select with
MM and
foreign_table
In addition, the values of following TCA types are also resolved and
expanded automatically:
datetime
flex
json
link
select with a static list of entries
Each of the fields receives a full-fledged resolved value, based on the field
configuration from TCA.
In case of relations (
category,
group,
inline,
select with
MM and
foreign_table), a collection
(
LazyRecordCollection) of new
Record objects is attached as
value. In case of
file, a collection (
LazyFileReferenceCollection)
of
FileReference objects and in case of type
folder, a collection
(
LazyFolderCollection) of
Folder objects are attached.
Note
The relations are only resolved once they are accessed - also known as
"lazy loading". This allows for recursion and circular dependencies to be
managed automatically. It is therefore also possible that the collection
is actually empty.
In order to define cardinality on TCA level, the option
relationship is
introduced for all "relation" TCA types listed above. If this option is set to
oneToOne or
manyToOne, then relations are resolved directly
without being wrapped into collection objects. In case the relation can
not be resolved,
NULL is returned.
The TCA option
maxitems does not influence this behavior. This means
it is possible to have a
oneToMany relation with maximum one value
allowed. This way, overrides of this value will not break functionality.
Field expansion
For TCA type
flex, the corresponding FlexForm is resolved and therefore
all values within this FlexForm are processed and expanded as well.
Fields of TCA type
datetime will be transformed into a full
\DateTimeInterface object.
Fields of TCA type
json will provide the decoded JSON value.
Fields of TCA type
link will provide the
\TYPO3\CMS\Core\LinkHandling\TypolinkParameter object,
which is an object oriented representation of the corresponding TypoLink
parameter configuration.
Fields of TCA type
select without a
relationship will always provide
an array of static values.
Note
TYPO3 tries to automatically resolve the
relationship for type
select fields, which use
renderType=selectSingle and
having a
foreign_table set. This means, in case no
relationship has been defined yet, it is set to either
manyToOne
as the default or
manyToMany for fields with option
MM.
Impact
When using
Record objects through the
\TYPO3\CMS\Core\Domain\RecordFactory API, e.g. via
RecordTransformationProcessor
(
record-transformation) or
PageContentFetchingProcessor
(page-content), the corresponding
Record
objects are now automatically processed and enriched.
Those can not only be used in the frontend but also for Backend Previews in
the page module. This is possible by configuring a Fluid Template via Page
TSconfig to be used for the page preview rendering:
In such template the newly available variable
{record} can be used to
access the resolved field values. It is advised to migrate existing preview
templates to this new object, as the former values will probably vanish in the
next major version.
By utilizing the new API for fetching records and content elements, the need
for further data processors, e.g.
FilesProcessor (
files),
becomes superfluous since all relations are resolved automatically when
requested.
Feature: #103789 - Add "close"-button to page layout, if returnUrl is set
A "close"-button is now displayed in the page module, if the returnUrl
argument is set. When this button is clicked, the previous module
leading to the page module (or a custom link defined in returnUrl) will be displayed
again.
In order to utilize this, backend module links set in extensions must pass the returnUrl
argument. If returnUrl is not set, the "close"-button will not be displayed.
Examples
Here is an example, using the Fluid
<be:moduleLink> ViewHelper:
The behaviour is similar to the
<be:uri.editRecord> ViewHelper,
where setting the returnUrl argument will also cause a "close"-button to
be displayed.
Important
When using the
<be:uri.editRecord> ViewHelper, returnUrl is
passed directly as argument. However, using
<be:moduleLink>, the
returnUrl argument must be passed as an additional parameter via the Fluid
ViewHelper's argument
arguments or
query.
The returnUrl should usually return to the calling (originating) module.
You can build the returnUrl with the Fluid ViewHelper
be:uri:
Fluid example for building returnUrl to module "linkvalidator"
The change has no impact, unless the functionality is being used. Extension
authors can make use of the new functionality to also conveniently link back to an originating
or custom module for a streamlined linear backend user-experience.
Feature: #104126 - Add configuration setting to define backend-locking file
TYPO3 supports the ability to lock the backend for maintenance reasons. This
is controlled with a LOCK_BACKEND file that was previously stored in
typo3conf/.
When empty, it falls back to a file LOCK_BACKEND, which is now stored
by default in:
var/lock/ for Composer Mode
config/ for Legacy Mode
If you previously manually maintained the LOCK_BACKEND file (for example via
deployment or other maintenance automation), please either adjust
your automations to the new file location, or change the setting to the desired file location,
or at best use the CLI commands
vendor/bin/typo3 backend:lock and
vendor/bin/typo3 backend:unlock.
The backend locking functionality is now contained in a distinct service class
\TYPO3\CMS\Backend\Authentication\BackendLocker to allow future flexibility.
When upgrading an installation to Composer Mode with a locked backend in effect,
please ensure your backend can remain locked by moving (or copying) the file to the new
location var/lock/.
Remember, if you want locked backend state to persist between deployments, ensure that the
used directory (var/lock by default) is shared between deployment releases.
Impact
The location for LOCK_BACKEND to lock (and unlock) the backend can now be controlled
by maintainers of a TYPO3 installation, and has moved outside of typo3conf/ by default
to either var/lock/ (Composer) or config/ (Legacy).
Feature: #104168 - PSR-14 event for modifying countries
A new PSR-14 event
\TYPO3\CMS\Core\Country\Event\BeforeCountriesEvaluatedEvent
has been introduced to modify the list of countries provided by
\TYPO3\CMS\Core\Country\CountryProvider .
This event allows to add, remove and alter countries from the list used by the
provider class itself and ViewHelpers like
<f:form.countrySelect />.
Note
The DTO
\TYPO3\CMS\Core\Country\Country
uses EXT:core/Resources/Private/Language/Iso/countries.xlf for translating
the country names.
If additional countries are added, add translations to countries.xlf
via locallangXMLOverride.
Example
An example corresponding event listener class:
<?phpdeclare(strict_types=1);
namespaceMyVendor\MyExtension\EventListener;
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Core\Country\Country;
useTYPO3\CMS\Core\Country\Event\BeforeCountriesEvaluatedEvent;
final readonly classEventListener{
#[AsEventListener(identifier: 'my-extension/before-countries-evaluated')]publicfunction__invoke(BeforeCountriesEvaluatedEvent $event): void{
$countries = $event->getCountries();
unset($countries['BS']);
$countries['XX'] = new Country(
'XX',
'XYZ',
'Magic Kingdom',
'987',
'🔮',
'Kingdom of Magic and Wonders'
);
$event->setCountries($countries);
}
}
<?xml version="1.0" encoding="utf-8" standalone="yes" ?><xliffversion="1.0"><filesource-language="en"datatype="plaintext"date="2024-01-08T18:44:59Z"product-name="my_extension"><body><trans-unitid="XX.name"approved="yes"><source>Magic Kingdom</source></trans-unit><trans-unitid="XX.official_name"approved="yes"><source>Kingdom of Magic and Wonders</source></trans-unit></body></file></xliff>
Copied!
Impact
Using the PSR-14 event
BeforeCountriesEvaluatedEvent allows
modification of countries provided by
CountryProvider .
Feature: #104221 - PSR-14 events for RTE <-> Persistence transformations
When using an RTE HTML content element, two transformations
take place within the TYPO3 backend:
From database: Fetching the current content from the database (persistence) and
preparing it to be displayed inside the RTE HTML component.
To database: Retrieving the data returned by the RTE and preparing it to
be persisted into the database.
This takes place in the
\TYPO3\CMS\Core\Html\RteHtmlParser class, by utilizing the
methods
transformTextForRichTextEditor and
transformTextForPersistence.
With forge#96107 and forge#92992, the former hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['transformation']
was removed, which took care of applying custom user-transformations. The suggested replacement
for this was to use the actual RTE YAML configuration and API like
allowAttributes.
Now, four PSR-14 Events are introduced to allow more granular control over data for
persistence -> RTE and RTE -> persistence. This allows developers to apply
more customized transformations, apart from the internal and API ones:
Modify data when saving RTE content to the database (persistence):
The event is meant to be used so that developers can change the HTML content
either before the internal TYPO3 modifications, or after those.
The before events are executed before TYPO3 applied any kind of internal transformations,
like for links. Event Listeners that want to modify output so that
TYPO3 additionally operates on that, should listen to those before-Events.
When Event Listeners want to perform on the final result, the corresponding after-Events
should be utilized.
Event listeners can use
$value = $event->getHtmlContent() to get the current contents,
apply changes to $value and then store the manipulated data via $event->setHtmlContent($value),
see example:
Example
An event listener class is constructed which will take an RTE input TYPO3 and internally
store it in the database as [tag:typo3]. This could allow a content element data processor
in the frontend to handle this part of the content with for example internal glossary operations.
The workflow would be:
Editor enters "TYPO3" in the RTE instance.
When saving, this gets stored as "[tag:typo3]".
When the editor sees the RTE instance again, "[tag:typo3]" gets replaced to "TYPO3" again.
So: The editor will always only see "TYPO3" and not know how it is internally handled.
The frontend output receives "[tag:typo3]" and could do its own content element magic,
other services accessing the database could also use the parseable representation.
<?phpdeclare(strict_types=1);
namespaceMyVendor\MyExtension\EventListener;
useTYPO3\CMS\Core\Attribute\AsEventListener;
classTransformListener{
/**
* Transforms the current value the RTE delivered into a value that is stored (persisted) in the database.
*/#[AsEventListener('rtehtmlparser/modify-data-for-persistence')]publicfunctionmodifyPersistence(AfterTransformTextForPersistenceEvent $event): void{
$value = $event->getHtmlContent();
$value = str_replace('TYPO3', '[tag:typo3]', $value);
$event->setHtmlContent($value);
}
/**
* Transforms the current persisted value into something the RTE can display
*/#[AsEventListener('rtehtmlparser/modify-data-for-richtexteditor')]publicfunctionmodifyRichTextEditor(AfterTransformTextForRichTextEditorEvent $event): void{
$value = $event->getHtmlContent();
$value = str_replace('[tag:typo3]', 'TYPO3', $value);
$event->setHtmlContent($value);
}
}
There are various
TCA table
ctrl settings that define fields used
to enable certain TYPO3 table capabilities and to specify the database column
to store this row state.
An example is
$GLOBALS['TCA']['ctrl']['enablecolumns']['starttime'] = 'starttime' , which
makes the table "start time aware", resulting in the automatic exclusion of a record
if the given start time is in the future, when rendered in the frontend.
Such
ctrl settings require TCA
columns definitions. Default definitions
of such
columns are now automatically added to
TCA if not manually
configured. Extension developers can now remove and avoid a significant amount
of boilerplate field definitions in
columns and rely on TYPO3 Core to create
them automatically. Note the Core does not automatically add such columns to TCA
types or
palettes definitions: Developers still need to place them,
to show the columns when editing record rows, and need to add according access
permissions.
Let us have a quick look on what happened within TCA and its surrounding code lately,
to see how this feature embeds within the general TYPO3 Core strategy in this area
and why the above feature has been implemented at this point in time:
TCA has always been a central cornerstone of TYPO3. The TYPO3 Core strives to maintain
this central part while simplifying and streamlining less desirable details.
TYPO3 version v12 aimed to simplify single column definitions by implementing new
column types like
file,
category,
email, and more. These are
much easier to understand and require far fewer single property definitions than
previous solutions. With this in place, auto-creation of database column
definitions derived from TCA has been established with TYPO3 v13, making the
manual definition of database table schemas in ext_tables.sql largely
unnecessary. Additionally, an object-oriented approach called
TcaSchema has
been introduced to harmonize and simplify information retrieval from TCA.
With the step described in this document - the auto-creation of TCA columns from
ctrl properties - the amount of manual boilerplate definitions is
significantly reduced, and the TYPO3 Core gains more control over these columns to
harmonize these fields throughout the system. Note that the TYPO3 Core has not yet
altered the structure of TCA
types and
palettes. This will be one of
the next steps in this area, but details have not been decided upon yet.
All these steps streamline TCA and its surrounding areas, simplify the system,
and reduce the amount of details developers need to be aware of when defining
their own tables and fields.
This document details the "column auto-creation from 'ctrl' fields" feature: It
first lists all affected settings with their derived default definitions. It
concludes with a section relevant for instances that still need to override
certain defaults of these columns by explaining the order of files and classes
involved in building TCA and the available options to change defaults and where
to place these changes.
Auto-created columns from 'ctrl'
The configuration settings below enable single table capabilities. Their values
are a database column name responsible for storing the row data of the capability.
If a setting is defined in a "base" TCA table file (Configuration/TCA, not
in Configuration/TCA/Overrides), the TYPO3 Core will add default
columns
definition for this field name if no definition exists in a base file.
This setting makes database table rows "frontend group aware": A row can be defined
to be shown only to frontend users who are a member of selected groups.
$GLOBALS['TCA']['ctrl']['languageField'] and transOrigPointerField`
These setting make database table rows "localization aware": Backend editors
can create localized versions of a record. Note when
languageField is
set, and
transOrigPointerField is not, the TYPO3 Core will automatically set
transOrigPointerField to
l10n_parent since both fields must be
always set in combination.
This setting makes database table rows "parent language record change aware": Backend
editors can have an indicator when the parent column has been changed.
To understand if and when TCA column auto-creation from
ctrl definitions
kicks in, it is important to have an overview of the order of the single loading
steps:
Load single files from extension Configuration/TCA files
NEW - Enrich
columns from
ctrl settings
Load single files from extension Configuration/TCA/Overrides files
Apply TCA migrations
Apply TCA preparations
As a result of this strategy,
columns fields are not auto-created, when
a
ctrl capability is added in a Configuration/TCA/Overrides
file, and not in a Configuration/TCA "base" file. In general, such
capabilities should be set in base files only: Adding them at a later point - for
example in a different extension - is brittle and there is a risk the main
extension can not deal with such an added capability properly.
Overriding definitions from auto-created TCA columns
I most cases, developers do not need to change definitions of
columns
auto-created by the TYPO3 Core. In general, it is advisable to not actively do this.
Developers who still want to change detail properties of such columns should
generally stick to "display" related details only.
There are two options to have own definitions: When a column is already defined
in a "base" TCA file (Configuration/TCA), the TYPO3 Core will not override it.
Alternatively, a developer can decide to let the TYPO3 Core auto-create a column, to
then override single properties in Configuration/TCA/Overrides files.
As example, "base"
pages file defines this (step 1 above):
When an editor creates a new page, it should be "disabled" by default to
avoid having a new page online in the website before it is set up completely.
A Configuration/TCA/Overrides/pages.php file does this:
<?php// New pages are disabled by default
$GLOBALS['TCA']['pages']['columns']['hidden']['config']['default'] = 1;
Copied!
Impact
Extension developers can typically remove
columns definitions of all the
above fields and rely on TYPO3 Core creating them with a good default
definition.
It is only required to define the desired table capabilities in
ctrl with
its field names, and the system will create the according
columns
definitions automatically.
Feature: #104321 - Allow handling of argument mapping exceptions in ActionController
A new method
handleArgumentMappingExceptions has been introduced in
Extbase
\TYPO3\CMS\Extbase\Mvc\Controller\ActionController to improve
handling of exceptions that occur during argument mapping.
The new method supports optional handling of the following exceptions:
\TYPO3\CMS\Extbase\Property\Exception\TargetNotFoundException ,
which occurs, when a given object UID can not be resolved to an existing record.
\TYPO3\CMS\Extbase\Mvc\Controller\Exception\RequiredArgumentMissingException ,
which occurs, when a required action argument is missing.
Handling of the exceptions can be enabled globally with the following TypoScript
configuration.
By default, these options are set to 0, which will lead to exceptions
being thrown (and would lead to errors, if not caught). This is the current
behavior of TYPO3.
When setting one of these values to 1, the configured exceptions will not be thrown.
Instead, a
pageNotFound response is propagated, resulting in a 404 error being
shown.
Additionally, extension authors can extend or override the method
handleArgumentMappingExceptions in relevant Controllers in order
to implement custom argument mapping exception handling.
Impact
Extension authors can now handle exceptions in implementations of a
ActionController ,
which are thrown during argument mapping.
Feature: #104451 - Redis backends support for key prefixing
It is now possible to add a dedicated key prefix for all invocations of a Redis
cache or session backend. This allows to use the same Redis database for multiple
caches or even for multiple TYPO3 instances if the provided prefix is unique.
Possible use cases are:
Using Redis caching for multiple caches, if only one Redis database is available
Pre-fill caches upon deployments using a new prefix (zero downtime deployments)
additional.php example for using Redis as session backend
The new feature allows to use the same Redis database for multiple caches or even
for multiple TYPO3 instances while having no impact on existing configuration.
Attention
If you start using the same Redis database for multiple caches or
using the same database also for session storage, make sure any involved
cache configuration uses a unique key prefix.
If only one of the caches does not use a key prefix, any cache flush
operation will always flush the whole database, hence also all other caches/sessions.
Feature: #104482 - Add if() support to ExpressionBuilder
The TYPO3
\TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder
provides a new method to phrase "if-then-else" expressions. Those are translated
into
IF or
CASE statements depending on the used database engine.
ExpressionBuilder::if()
Creates an IF-THEN-ELSE expression.
Method signature
/**
* Creates IF-THEN-ELSE expression construct compatible with all supported database vendors.
* No automatic quoting or escaping is done, which allows to build up nested expression statements.
*
* **Example:**
* ```
* $queryBuilder
* ->selectLiteral(
* $queryBuilder->expr()->if(
* $queryBuilder->expr()->eq('hidden', $queryBuilder->createNamedParameter(0, Connection::PARAM_INT)),
* $queryBuilder->quote('page-is-visible'),
* $queryBuilder->quote('page-is-not-visible'),
* 'result_field_name'
* ),
* )
* ->from('pages');
* ```
*
* **Result with MySQL:**
* ```
* SELECT (IF(`hidden` = 0, 'page-is-visible', 'page-is-not-visible')) AS `result_field_name` FROM `pages`
* ```
*/publicfunctionif(
CompositeExpression|\Doctrine\DBAL\Query\Expression\CompositeExpression|\Stringable|string $condition,
\Stringable|string $truePart,
\Stringable|string $falsePart,
\Stringable|string|null $as = null
): string{
$platform = $this->connection->getDatabasePlatform();
$pattern = match (true) {
$platform instanceof DoctrineSQLitePlatform => 'IIF(%s, %s, %s)',
$platform instanceof DoctrinePostgreSQLPlatform => 'CASE WHEN %s THEN %s ELSE %s END',
$platform instanceof DoctrineMariaDBPlatform,
$platform instanceof DoctrineMySQLPlatform => 'IF(%s, %s, %s)',
default => thrownew \RuntimeException(
sprintf('Platform "%s" not supported for "%s"', $platform::class, __METHOD__),
1721806463
)
};
$expression = sprintf($pattern, $condition, $truePart, $falsePart);
if ($as !== null) {
$expression = $this->as(sprintf('(%s)', $expression), $as);
}
return $expression;
}
Copied!
Impact
Extension authors can use the new expression method to build more advanced
queries without the requirement to deal with the correct implementation for
all supported database vendors.
Note
No automatic quoting or escaping is done for the condition and true/false
part. Extension authors need to ensure proper quoting for each part or use
API calls doing the quoting, for example the TYPO3 CompositeExpression or
ExpressionBuilder calls.
Feature: #104493 - Add castText() expression support to ExpressionBuilder
The TYPO3
\TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder
provides a new method to cast expression results to text like datatypes. This
is done to large
VARCHAR/CHAR types using the
CAST/CONVERT or similar
methods based on the used database engine.
Note
This should not be mixed with
TEXT,
CHAR or
VARCHAR
data types for column (fields) definition used to describe the structure
of a table.
ExpressionBuilder::castText()
Creates a
CAST expression.
Method signature
/**
* Creates a cast for the `$expression` result to a text datatype depending on the database management system.
*
* Note that for MySQL/MariaDB the corresponding CHAR/VARCHAR types are used with a length of `16383` reflecting
* 65554 bytes with `utf8mb4` and working with default `max_packet_size=16KB`. For SQLite and PostgreSQL the text
* type conversion is used.
*
* Main purpose of this expression is to use it in a expression chain to convert non-text values to text in chain
* with other expressions, for example to {@see self::concat()} multiple values or to ensure the type, within
* `UNION/UNION ALL` query parts for example in recursive `Common Table Expressions` parts.
*
* This is a replacement for {@see QueryBuilder::castFieldToTextType()} with minor adjustments like enforcing and
* limiting the size to a fixed variant to be more usable in sensible areas like `Common Table Expressions`.
*
* Alternatively the {@see self::castVarchar()} can be used which allows for dynamic length setting per expression
* call.
*
* **Example:**
* ```
* $queryBuilder->expr()->castText(
* '(' . '1 * 10' . ')',
* 'virtual_field'
* );
* ```
*
* **Result with MySQL:**
* ```
* CAST((1 * 10) AS CHAR(16383) AS `virtual_field`
* ```
*
* @throws \RuntimeException when used with a unsupported platform.
*/publicfunctioncastText(CompositeExpression|\Stringable|string $expression, string $asIdentifier = ''): string{
$platform = $this->connection->getDatabasePlatform();
if ($platform instanceof DoctrinePostgreSQLPlatform) {
return$this->as(sprintf('((%s)::%s)', $expression, 'text'), $asIdentifier);
}
if ($platform instanceof DoctrineSQLitePlatform) {
return$this->as(sprintf('(CAST((%s) AS %s))', $expression, 'TEXT'), $asIdentifier);
}
if ($platform instanceof DoctrineMariaDBPlatform) {
// 16383 is the maximum for a VARCHAR field with `utf8mb4`return$this->as(sprintf('(CAST((%s) AS %s(%s)))', $expression, 'VARCHAR', '16383'), $asIdentifier);
}
if ($platform instanceof DoctrineMySQLPlatform) {
// 16383 is the maximum for a VARCHAR field with `utf8mb4`return$this->as(sprintf('(CAST((%s) AS %s(%s)))', $expression, 'CHAR', '16383'), $asIdentifier);
}
thrownew \RuntimeException(
sprintf(
'%s is not implemented for the used database platform "%s", yet!',
__METHOD__,
get_class($this->connection->getDatabasePlatform())
),
1722105672
);
}
Copied!
Impact
Extension authors can use the new expression method to build more advanced
queries without the requirement to deal with the correct implementation
for all supported database vendors - at least to some grade.
Feature: #104526 - Provide validators for PSR-7 UploadedFile objects in Extbase
4 new Extbase validators have been added to allow common validation tasks of a
PSR-7
\TYPO3\CMS\Core\Http\UploadedFile object or an
\TYPO3\CMS\Extbase\Persistence\ObjectStorage containing PSR-7
UploadedFile objects.
Note, that the new validators can only be applied to the TYPO3 implementation
of the PSR-7
\Psr\Http\Message\UploadedFileInterface because they validate the uploaded
files before it has been moved.
Custom implementations of the
\UploadedFileInterface must continue to
implement their own validators.
FileNameValidator
This validator ensures, that files with PHP executable file extensions can not
be uploaded. The validator has no options.
FileSizeValidator
This validator can be used to validate an uploaded file against a given minimum
and maximum file size.
Validator options:
minimum - The minimum size as string (e.g. 100K)
maximum - The maximum size as string (e.g. 100K)
MimeTypeValidator
This validator can be used to validate an uploaded file against a given set
of accepted MIME types. The validator additionally verifies, that the given
file extension of the uploaded file matches allowed file extensions for the
detected mime type.
Validator options:
allowedMimeTypes - An array of allowed MIME types
ignoreFileExtensionCheck - If set to "true", it is checked, the file
extension check is disabled
ImageDimensionsValidator
This validator can be used to validate an uploaded image for given image
dimensions. The validator must only be used, when it is ensured, that the
uploaded file is an image (e.g. by validating the MIME type).
Validator options:
width - Fixed width of the image as integer
height - Fixed height of the image as integer
minWidth - Minimum width of the image as integer. Default is 0
maxWidth - Maximum width of the image as integer. Default is PHP_INT_MAX
minHeight - Minimum height of the image as integer. Default is 0
maxHeight - Maximum height of the image as integer. Default is PHP_INT_MAX
Impact
TYPO3 extension autors can now use the new validators to validate a given
UploadedFile object.
Feature: #104631 - Add UNION Clause support to the QueryBuilder
The
UNION clause is used to combine the result sets of two or more
SELECT statements, which all database vendors support, each with their
own specific variations.
However, there is a commonly shared subset that works across all of them:
SELECT column_name(s) FROM table1
WHERE ...
UNION <ALL | DISTINCT>
SELECT column_name(s) FROM table2
WHERE ...
ORDERBY ...
LIMIT x OFFSET y
Copied!
with shared requirements:
Each SELECT must return the same fields in number, naming and order.
Each SELECT must not have ORDER BY, expect MySQL allowing it to be used as sub
query expression encapsulated in parentheses.
Generic
UNION clause support has been contributed to Doctrine DBAL and
is included since Release 4.1.0
which introduces two new API method on the
\QueryBuilder:
union(string|QueryBuilder $part) to create first UNION query part
addUnion(string|QueryBuilder $part, UnionType $type = UnionType::DISTINCT)
to add additional
UNION (ALL|DISTINCT) query parts with the selected union
query type.
TYPO3 decorates the Doctrine DBAL
\QueryBuilder
to provide for most API methods automatic
quoting of identifiers and values and to apply database restrictions automatically
for
SELECT queries.
The Doctrine DBAL API has been adopted now to provide the same surface for the
TYPO3
\TYPO3\CMS\Core\Database\Query\QueryBuilder and the intermediate
\TYPO3\CMS\Core\Database\Query\ConcreteQueryBuilder to make it easier to
create
UNION clause queries. The API on both methods allows to provide
dedicated
QueryBuilder instances
or direct queries as strings in case it is needed.
Note
Providing
UNION parts as plain string requires the developer to take
care of proper quoting and escaping within the query part.
In queries containing subqueries, only named placeholders (such as :username)
can be used and must be registered on the outermost
QueryBuilder object,
similar to advanced query creation with
SUB QUERIES.
Warning
QueryBuilder can be used create
UNION clause queries not compatible with all database providers,
for example using
LIMIT/OFFSET in each part query or other stuff.
UnionType::DISTINCT and UnionType::ALL
Each subsequent part needs to be defined either as
UNION DISTINCT or
UNION ALL which could have not so obvious effects.
For example, using
UNION ALL for all parts in between except for the last
one would generate larger result sets first, but discards duplicates when adding
the last result set. On the other side, using
UNION ALL tells the query
optimizer not to scan for duplicates and remove them at all which can be a
performance improvement - if you can deal with duplicates it can be ensured that
each part does not produce same outputs.
Example: Compose a
UNION clause query
Custom service class using a UNION query to retrieve data.
useDoctrine\DBAL\Query\UnionType;
useTYPO3\CMS\Core\Database\Connection;
useTYPO3\CMS\Core\Database\ConnectionPool;
final readonly classMyService{
publicfunction__construct(
private ConnectionPool $connectionPool,
){}
publicfunctionexecuteUnionQuery(
int $pageIdOne,
int $pageIdTwo,
): ?array{
$connection = $this->connectionPool->getConnectionForTable('pages');
$unionQueryBuilder = $connection->createQueryBuilder();
$firstPartQueryBuilder = $connection->createQueryBuilder();
$secondPartQueryBuilder = $connection->createQueryBuilder();
// removing automatic TYPO3 restriction for the sake of the example// to match the PLAIN SQL example when executed. Not removing them// will generate corresponding restriction SQL code for each part.
$firstPartQueryBuilder->getRestrictions()->removeAll();
$secondPartQueryBuilder->getRestrictions()->removeAll();
$expr = $unionQueryBuilder->expr();
$firstPartQueryBuilder
// The query parts **must** have the same column counts, and these// columns **must** have compatible types
->select('uid', 'pid', 'title')
->from('pages')
->where(
$expr->eq(
'pages.uid',
// !!! Ensure to use most outer / top / main QueryBuilder// instance for creating parameters and the complete// query can be executed in the end.
$unionQueryBuilder->createNamedParameter($pageIdOne, Connection::PARAM_INT),
)
);
$secondPartQueryBuilder
->select('uid', 'pid', 'title')
->from('pages')
->where(
$expr->eq(
'pages.uid',
// !!! Ensure to use most outer / top / main QueryBuilder instance
$unionQueryBuilder->createNamedParameter($pageIdTwo, Connection::PARAM_INT),
)
);
// Set first and second union part to the main (union)// QueryBuilder and return the retrieved rows.return $unionQueryBuilder
->union($firstPartQueryBuilder)
->addUnion($secondPartQueryBuilder, UnionType::DISTINCT)
->orderBy('uid', 'ASC')
->executeQuery()
->fetchAllAssociative();
}
}
Copied!
This would create the following query for MySQL with
$pageIdOne = 100 and
$pageIdTwo = 10:
A new CLI command
typo3 upgrade:mark:undone has been
introduced. It allows to mark a previously executed upgrade wizard as "undone",
so it can be run again.
This makes the existing functionality from the install tool also available on
CLI.
Note
Bear in mind that wizards theoretically can cause data inconsistencies when
being run again. Also, a wizard may not run properly again when its
pre-requisites no longer apply after its first run.
Impact
You can now mark an already executed upgrade wizard as "undone" with
typo3 upgrade:mark:undone <wizardIdentifier>
Class
\TYPO3\CMS\Core\View\ViewFactoryInterface has been added as a
generic view interface to create views that return an instance of
\TYPO3\CMS\Core\View\ViewInterface . This implements the "V" of "MVC"
in a generic way and is used throughout the TYPO3 Core.
This obsoletes all custom view instance creation approaches within the TYPO3 Core
and within TYPO3 extensions. Extensions should retrieve view instances based
on this
ViewFactoryInterface .
Impact
Instances of this interface should be injected using dependency injection. The
default injected implementation is a Fluid view, and can be reconfigured using
dependency injection configuration, typically in a Services.yaml file.
A casual example to create and render a view looks like this.
ContentArgumentName has been a feature on Fluid ViewHelpers for some time now.
It allows ViewHelpers to link a ViewHelper argument to the children of the
ViewHelper name. As a result, an input value can either be specified as an
argument or as the ViewHelper's children, leading to the same result.
Previously, this feature was only available to ViewHelpers using the trait
\CompileWithContentArgumentAndRenderStatic .
It is now available to all ViewHelpers since it has been integrated into the
\AbstractViewHelper . The
trait is no longer necessary.
To use the new feature, all the ViewHelper implementation needs to do is to define
a method getContentArgumentName() which returns the name of the argument to be
linked to the ViewHelper's children:
ViewHelpers using the trait
\CompileWithContentArgumentAndRenderStatic
should be migrated to the new feature.
\CompileWithContentArgumentAndRenderStatic
will continue to work in Fluid v4, but will log a deprecation level error message.
It will be removed in Fluid v5.
A new Site Settings editor has been introduced that allows to configure per-site
settings in file:config/sites/*/settings.yaml.
The new backend module Site Management > Settings
provides an overview of sites which offer configurable settings and makes
them editable based on
Site Set provided Settings Definitions.
The editor shows a list of settings categories and respective settings. It will
persist all settings into config/sites/*/settings.yaml. The module will
only persist settings that deviate from the site-scoped default value. That
means it will only change the minimal difference to the settings set defined
by the active sets for the respective site.
The backend module is currently available for administrators only, but will
likely be extended to be made available for editors in future.
Anonymous (undefined) site settings – as supported since TYPO3 v10 –
will not be made editable, but will be preserved as-is when persisting changes
through the settings editor.
Categorization
Sets can define categories and settings definitions can reference the category
by ID in order to assign a setting to a specific category.
These definitions are placed in settings.definitions.yaml
next to the site set file config.yaml.
categories:myCategory:label:'My Category'settings:my.example.setting:label:'My example setting'category:myCategorytype:stringdefault:''my.seoRelevantSetting:label:'My SEO relevant setting'# show in EXT:seo provided category "seo"category:seotype:intdefault:5
Copied!
The settings ordering is defined through the loading order of extensions and by
the order of categories. Uncategorized settings will be grouped into a virtual
"Other" category and shown at the end of the list of available settings.
Readonly
Site settings can be made readonly. They can be overridden only by editing
the config/sites/my-site/settings.yaml but not from within the editor.
The value of the field is displayed in a readonly field in the settings editor.
settings:my.enumSetting:label:'My setting with options'type:stringenum:valueA:'Label of value A'valueB:'Label of value B'
Copied!
Impact
Site-scoped settings will most likely be the place to configure site-wide
configuration, which was previously only possible to modify via Constant Editor,
modifying TypoScript constants.
It is recommended to use site-sets and their UI configuration in favor of
TypoScript Constants in the future.
Feature: #104814 - Automatically add system fields to content types
All content elements types (
CType) are usually equipped with the same
system fields (language, hidden, etc.) - see also Feature: #104311 - Auto created system TCA columns.
Adding them to the editor form has previously been done by adding those fields
to each content types'
showitem definition.
In the effort to simplify content element creation, to unify the available
fields and position for the editor and to finally reduce configuration effort
for integrators, those system fields are now added automatically based
on the
ctrl definition.
Note
The fields are added to the
showitem through their corresponding
palettes. In case such palette has been changed by extensions, the required
system fields are added individually to corresponding tabs.
The following tabs / palettes are now added automatically:
The General tab with the general palette at the very beginning
The Language tab with the language palette after custom fields
The Access tab with the hidden and access palettes
The Notes tab with the rowDescription field
As mentioned, in case one of those palettes has been changed to no longer
include the corresponding system fields, those fields are added individually
depending on their definition in the table's
ctrl section:
The
ctrl[type] field (usually
CType)
The
colPos field
The
ctrl[languageField] (usually
sys_language_uid)
The
ctrl[editlock] field (usually
editlock)
The
ctrl[enablecolumns][disabled] field (usually
hidden)
The
ctrl[enablecolumns][starttime] field (usually
starttime)
The
ctrl[enablecolumns][endtime] field (usually
endtime)
The
ctrl[enablecolumns][fe_group] field (usually
fe_group)
The
ctrl[descriptionColumn] field (usually
rowDescription)
By default, all custom fields - the ones still defined in
showitem - are
added after the general palette and are therefore added to the
General tab, unless a custom tab (e.g. Plugin,
or Categories) is defined in between. It is also possible to start
with a custom tab by defining a --div-- as the first item in the
showitem. In this case, the General tab will be omitted.
All those system fields, which are added based on the
ctrl section are
also automatically removed from any custom palette and from the customized
type's
showitem definition.
If the content element defines the Extended tab, it will be
inserted at the end, including all fields added to the type via API methods,
without specifying a position, e.g. via
ExtensionManagementUtility::addToAllTcaTypes().
Impact
Creating content elements has been simplified by removing the need to
define the system fields for each element again and again. This shrinks
down a content element's
showitem to just the element specific fields.
A usual migration will therefore look like the following:
Since all fields, palettes and tabs, which are defined in the
showitem
are added after the
general palette, also the Categories tab
- if defined - is displayed before the system tabs / fields. The only special
case is the Extended tab, which is always added at the end.
Important
For consistency reasons, custom labels for system fields are no
longer preserved.
Feature: #104832 - PSR-14 Event to alter the results of PageTreeRepository
Until TYPO3 v9, it was possible to alter the rendering of one of TYPO3's
superpowers — the page tree in the TYPO3 Backend User Interface.
This was done via a "Hook", but was removed due to the migration towards an
SVG-based tree rendering.
As the Page Tree Rendering has evolved, and the hook system has been replaced
in favor of PSR-14 Events, a new event
\TYPO3\CMS\Backend\Tree\Repository\AfterRawPageRowPreparedEvent
has been introduced.
Example
The event listener class, using the PHP attribute
#[AsEventListener] for
registration, will remove any children for displaying for the page with the
UID 123:
To make it easier for TYPO3 users to view all the internal notes (EXT:sys_note) in
their TYPO3 system, TYPO3 now offers dashboard widgets for each internal note type.
The backend user must have access to the sys_note table and view permission to the
page where the record is located.
Impact
TYPO3 users who have access to the Dashboard module and are
granted access to the new widgets can now add and use these widgets.
Feature: #104846 - Custom field transformations for new records
With forge#103783 the new
\TYPO3\CMS\Core\Domain\Record object has been introduced, which
is an object representing a raw database record based on TCA and is usually
used in the frontend (via Fluid Templates).
Since Feature: #103581 - Automatically transform TCA field values for record objects the properties of those
Record
objects are transformed / expanded from their raw database value into
"rich-flavored" values. Those values might be relations to e.g.
Record ,
FileReference ,
Folder or
\DateTimeImmutable objects.
However, TYPO3 does not know about custom field meanings, e.g. latitude and
longitude information, stored in an input field or user settings stored as
JSON in an TCA type json field. For such custom needs, the new
PSR-14
\TYPO3\CMS\Core\Domain\Event\RecordCreationEvent has been
introduced. It is dispatched right before a
Record is created and
therefore allows to fully manipulate any property, even the ones already
transformed by TYPO3.
The new event is stoppable (implementing
\StoppableEventInterface ), which
allows listeners to actually create a
Record object completely on their
own.
Important
The event operates on the
RecordInterface instead of an actual
implementation. This way, extension authors are able to set custom records,
implementing the interface.
The new event features the following methods:
setRecord() - Manually adds a
RecordInterface
object (stops the event propagation)
hasProperty() - Whether a property exists
setProperty() - Add or overwrite a property
setProperties() - Set properties for the
RecordInterface
unsetProperty() - Unset a single property
getProperty() - Get the value for a single property
getProperties() - Get all properties
getRawRecord() - Get the
RawRecord object
getSystemProperties() - Get the calculated
SystemProperties
getContext() - Get the current
Context (used to fetch the raw database row)
isPropagationStopped() - Whether the event propagation is stopped
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.
Options have been added to switch between the available color schemes in TYPO3. A set of buttons
for each available color scheme in the user dropdown at the top right and a setting in User Settings.
As the dark color scheme is currently regarded experimental until further notice, color scheme switching logic is
currently hidden behind the UserTS setting
setup.fields.colorScheme.disabled.
Impact
Warning
If you don't want the automatic switching and don't include the setup core extension in your environment,
you need to manually disable the feature yourself using the UserTS configuration
setup.fields.colorScheme.disabled = 1!
It is now possible to switch to an automatic, light or dark color scheme for use in the backend.
Feature: #104878 - Introduce dashboard widget for pages with latest changes
To make it easier for TYPO3 users to view the latest changed pages in their
TYPO3 system, TYPO3 now offers a dashboard widget that lists the latest
changed pages.
Widget Options:
- limit The limit of pages, displayed in the widget, default is 10
- historyLimit The maximum number of history records to check, default 1000
Impact
TYPO3 users who have access to the Dashboard module and are
granted access to the new widgets can now add and use this widget.
TYPO3 13 now uses Fluid 4 as the new base version. Old TYPO3 versions
will keep using Fluid 2, which will still receive bugfixes if necessary.
For detailed information about this release, please refer to the
dedicated release notes on GitHub.
With the update to Fluid 4, tag-based ViewHelpers now have proper
support for boolean attributes. Before this change, it was very
cumbersome to generate these with Fluid, now it is implemented similar
to popular JavaScript frameworks by using the newly introduced
boolean literals:
Of course, any variable containing a boolean can be supplied as well:
<my:viewhelperasync="{isAsync}" />
Copied!
This can also be used in combination with variable casting:
<my:viewhelperasync="{myString as boolean}" />
Copied!
For compatibility reasons empty strings still lead to the attribute
being omitted from the tag.
Impact
For existing installations, negative consequences of this update should be
minimal as deprecated features will still work. Users are however advised
to look into the already announced deprecations and to update their code
accordingly. This update helps with this by now writing log messages to the
deprecation log (if activated) if any deprecated feature is used in the
TYPO3 instance.
Feature: #104904 - Ignore Fluid syntax error in <f:comment>
Fluid 4 brings a new template processor
\TYPO3Fluid\Fluid\Core\Parser\TemplateProcessor\RemoveCommentsTemplateProcessor
which removes Fluid comments created with the
Debug ViewHelper <f:debug> from the template source
string before the parsing process starts. It retains the original line breaks to ensure
that error messages still refer to the correct line in the template.
By applying this template processor to all Fluid instances in the Core, it is now
possible to use invalid Fluid code inside
<f:comment> without triggering a Fluid error.
Impact
This feature is helpful during template development because developers don't need to
take care for commented-out code being valid Fluid code.
Feature: #104914 - Updated HTTP headers for frontend rendering and new TypoScript setting for proxies
In a typical frontend rendering scenario, TYPO3 sends HTTP response headers to
deny caching to clients (= browsers) when e.g. a frontend user is logged in,
a backend user is previewing a page, or a non-cacheable plugin is on a page.
When a frontend page is "client-cacheable", TYPO3 does not send any HTTP headers
by default, but only when
config.sendCacheHeaders = 1 is set
via TypoScript.
In this case, TYPO3 sends the following HTTP Headers (example):
Expires: Thu, 26 Aug 2024 08:52:00 GMT
ETag: "d41d8cd98f00b204ecs00998ecf8427e"
Cache-Control: max-age=86400
Pragma: public
Copied!
However, in the past, this could lead to problems, because recurring website
users might see outdated content for up to 24 hours (by default) or even longer,
even if other website visitors already see new content, depending on various
cache_timeout settings.
Thus,
config.sendCacheHeaders = 1 should be used with extreme care.
However, this option was also used when a proxy / CDN / shared cache such as
Varnish was put in between TYPO3 / the webserver and the client. The reverse
proxy can then evaluate the HTTP Response Headers sent by TYPO3 frontend, put
the TYPO3 response from the actual webserver into its "shared cache" and send
a manipulated / adapted response to the client.
However, when working with proxies, it is much more helpful to take load
off of TYPO3 / the webserver by keeping a cached version for a period of
time and answering requests from the client, while still telling the
client to not cache the response inside the browser cache.
This is now achieved with a new option
config.sendCacheHeadersForSharedCaches = auto.
With this option enabled, TYPO3 now evaluates if the current TYPO3 frontend
request is executed behind a Reverse Proxy, and if so, TYPO3 sends the following
HTTP Response Headers at a cached response:
Expires: Thu, 26 Aug 2024 08:52:00 GMT
ETag: "d41d8cd98f00b204ecs00998ecf8427e"
Cache-Control: max-age=0, s-maxage=86400
Pragma: public
With
config.sendCacheHeadersForSharedCaches = force the reverse
proxy evaluation can be omitted, which can be used for local webserver internal
caches.
"s-maxage" is a directive to tell shared caches - CDNs and reverse proxies - to keep
a cached version of the HTTP response for a period of time (based on various
cache settings) in their shared cache, while max-age=0 is evaluated at the
client level. See
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control for more
details and if your reverse proxy supports this directive.
The new option takes precedence over
config.sendCacheHeaders = 1
if running behind a reverse proxy.
Impact
By utilizing the new TypoScript setting, TYPO3 caches cacheable contents,
and also instructs shared caches such as reverse proxies or CDNs to cache
the HTTP Response, while always delivering fresh content to the client,
if certain routines for cache invalidation are in place. The latter is
typically handled by TYPO3 extensions which hook into the cache invalidation
process of TYPO3 to also invalidate cache entries in the reverse proxies.
In addition, compared to previous TYPO3 versions, client-cacheable HTTP Responses
now send "Cache-Control: private, no-store" if no option applies.
Feature: #104935 - Allow moving content elements via page tree
To make managing content across pages easier, a backend user may now drag
content elements from the Web > Page module into a page in the page tree.
Once dropped, a modal window opens, allowing the backend user to select the
position for placing the content element and to change the target page if needed.
Impact
Content elements can be moved from the Web > Page module into the
page tree to initiate the moving process.
Feature: #104973 - Activate the shipped LintYaml executable for TYPO3
The
typo3 executable received a new command lint:yaml to ease and encourage
linting of YAML files before deploying to production and therefore avoid failures.
Usage as follows:
# Validates a single file
bin/typo3 lint:yaml path/to/file.yaml
# Validates multiple files
bin/typo3 lint:yaml path/to/file1.yaml path/to/file2.yaml
# Validates all files in a directory (also in sub-directories)
bin/typo3 lint:yaml path/to/directory
# Validates all files in multiple directories (also in sub-directories)
bin/typo3 lint:yaml path/to/directory1 path/to/directory2
# Exclude one or more files from linting
bin/typo3 lint:yaml path/to/directory --exclude=path/to/directory/foo.yaml --exclude=path/to/directory/bar.yaml
# Show help
bin/typo3 lint:yaml --help
Copied!
The help argument will list possible usage elements.
Impact
Integrate easy made linting of YAML files from Core, custom extensions or
any other source into your quality assurance workflow in the known format
of the
typo3 executable.
When database records are used in the frontend, and the rendered result is put
into caches like the page cache, the TYPO3 frontend now automatically tags cache
entries with lists of used records.
When changing such records in the backend, affected cache entries are dropped,
leading to automatic cache eviction.
This is a huge improvement to previous TYPO3 versions where tagging and cache
eviction had to configured manually.
This feature - automatically tagging cache entries - is the final solution to
consistent caches at any point in time. It is however a bit tricky to get right
in a performant way: There are still details to rule out, and the core will
continue to improve in this area. The basic implementation in TYPO3 v13 however
already resolves many use cases. Core development now goes ahead to see how this
features behaves in the wild.
This feature is encapsulated in the feature toggle
frontend.cache.autoTagging:
It is enabled by default with new instances based on TYPO3 v13, and needs to be
manually enabled for instances being upgrades from previous versions.
Impact
Instances configured with the feature toggle automatically tag caches. Affected
cache entries will be removed when changing records.
The default view of ext:extbase now returns a view that implements
\TYPO3\CMS\Core\View\ViewInterface and not only
\TYPO3Fluid\Fluid\View\ViewInterface anymore. This allows
implementing any view that implements
ViewInterface ,
and frees the direct dependency to Fluid.
The default return object is an instance of
\TYPO3\CMS\Fluid\View\FluidViewAdapter which implements all
special methods tailored for Fluid. Extbase controllers should
check for instance of this object before calling these methods,
especially:
getRenderingContext()
setRenderingContext()
renderSection()
renderPartial()
Method calls not being part of
ViewInterface or the above
listed method names have been marked as deprecated and will be removed in TYPO3 v14.
Impact
Extbase controllers that extend
ActionController
and call methods not part of
ViewInterface , should
test for
$view instanceof FluidViewAdapter before calling
getRenderingContext(),
setRenderingContext(), php:renderSection()
and
renderPartial().
All other Fluid related methods called on
$view have been marked as
deprecated and will log a deprecation level error message.
Affected installations
Instances with Extbase based extensions that call
$view methods without
testing for
FluidViewAdapter .
Migration
Methods on "old" Fluid instances were wrapper methods for
RenderingContext . Controllers
should call
$view->getRenderingContext()
to perform operations instead.
Deprecation: #102422 - TypoScriptFrontendController->addCacheTags() and ->getPageCacheTags()
The methods
\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->addCacheTags() and
\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->getPageCacheTags()
have been marked as deprecated.
Impact
Calling the methods
\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->addCacheTags()
and
\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->getPageCacheTags()
will trigger a PHP deprecation warning.
Affected installations
TYPO3 installations calling
\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->addCacheTags()
or
\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->getPageCacheTags().
Migration
// Before
$GLOBALS['TSFE']->addCacheTags([
'tx_myextension_mytable_123',
'tx_myextension_mytable_456'
]);
// AfteruseTYPO3\CMS\Core\Cache\CacheTag;
$request->getAttribute('frontend.cache.collector')->addCacheTags(
new CacheTag('tx_myextension_mytable_123', 3600),
new CacheTag('tx_myextension_mytable_456', 3600)
);
Copied!
// Before
$GLOBALS['TSFE']->getPageCacheTags();
// After
$request->getAttribute('frontend.cache.collector')->getCacheTags();
The method
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPItoST43()
has been marked as deprecated in TYPO3 v13 and will be removed with TYPO3 v14.
Impact
Using the
ExtensionManagementUtility::addPItoST43() will raise a deprecation
level log entry and a fatal error in TYPO3 v14.
Affected installations
Extensions using
ExtensionManagementUtility::addPItoST43() are affected:
Using
ExtensionManagementUtility::addPItoST43() triggers a deprecation level log message.
The extension scanner will find usages of
ExtensionManagementUtility::addPItoST43() as strong match.
Calling these methods is discouraged. They will log a deprecation level
error when used with Fluid standalone v4.
Affected installations
Instances with extensions calling above methods.
Migration
registerUniversalTagAttributes()
Within tag based ViewHelpers, calls to
registerUniversalTagAttributes() should be removed.
This method has been marked as
@deprecated with Fluid standalone 2.12, will
log a deprecation level error with Fluid standalone v4, and will be removed with v5.
When removing the call, attributes registered by the call are now available in
$this->additionalArguments, and no longer in
$this->arguments. This may need
adaption within single ViewHelpers, if they handle such attributes on their own. For example,
the common ViewHelper
f:image was affected within the TYPO3 Core. The following attributes
may need attention when removing
registerUniversalTagAttributes():
class,
dir,
id,
lang,
style,
title,
accesskey,
tabindex,
onclick.
registerTagAttribute()
Within tag based ViewHelpers, calls to
registerTagAttribute() should be removed.
This method has been marked as
@deprecated with Fluid standalone 2.12, will
log a deprecation level error with Fluid standalone v4, and will be removed with v5.
The call be often simply removed since arbitrary attributes not specifically registered
are just added as-is by
\TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper .
This only needs attention
if single view helpers deal with such attributes within the
render() method:
When removing the call, those arguments are no longer available in
$this->arguments,
but in
$this->additionalArguments. Additional attention is needed with
attributes registered with type
boolean: Those usually have some handling
within
render(). To stay compatible, it can be helpful to not simply
remove the
registerTagAttribute() call, but to turn it into a call to
registerArgument().
The method
\TYPO3\CMS\Backend\Utility\BackendUtility::getTcaFieldConfiguration was introduced back
in 2010 to add a simple abstraction to access "TCA" definitions of a field.
However, apart from the set up that it is not part of a flexible API without
knowing the context, it was used seldom in TYPO3 Core.
The method has now been deprecated, as one could and can easily write the same
PHP code with
$GLOBALS['TCA'] in mind already (which the TYPO3 Core already did
in several other places).
Now that Schema API was introduced, the last parts have been migrated to use
the new API.
Impact
Calling the PHP method
BackendUtility::getTcaFieldConfiguration will
trigger a PHP deprecation warning.
Affected installations
TYPO3 installations with custom extensions using this method.
Migration
Either access
$GLOBALS['TCA'] directly (in order to support TYPO3 v12 and TYPO3 v13),
or migrate to the new Schema API:
Method
\TYPO3\CMS\Core\Utility\DiffUtility->makeDiffDisplay()
and class property
DiffUtility->stripTags have been
deprecated in favor of new method
DiffUtility->diff().
The new method no longer applies
strip_tags() to the input strings.
This change makes class
DiffUtility stateless: Property
$stripTags will vanish in v14.
Impact
Using method
DiffUtility->makeDiffDisplay() will trigger a
deprecation level error message.
Affected installations
Instances with extensions calling
DiffUtility->makeDiffDisplay().
Migration
If
DiffUtility->stripTagsis not explicitly set to false, a typical
migration looks like this:
// before
$diffUtility->DiffUtility->makeDiffDisplay($from, $to);
// after
$diffUtility->DiffUtility->diff(strip_tags($from), stripTags($to));
Copied!
If
DiffUtility->stripTags = false is set before calling
DiffUtility->makeDiffDisplay(), method
diff() can be called
as before, and
DiffUtility->stripTags = false can be removed.
Fluid standalone method
\TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper->overrideArgument()
has been marked as deprecated.
Impact
Using
overrideArgument() in ViewHelpers logs a deprecation level error message in Fluid standalone v4,
and will be removed with Fluid standalone v5. The method continues to work without deprecation level
error message in Fluid standalone v2.
With Fluid standalone v2.14,
registerArgument() no longer throws an exception if an
argument is already registered. This allows to override existing arguments transparently
without using
overrideArgument().
Affected installations
Instances with custom ViewHelpers using
overrideArgument() are affected.
Migration
Update typo3fluid/fluid to at least 2.14 and use
registerArgument().
Method
\TYPO3\CMS\Core\Authentication\BackendUserAuthentication::returnWebmounts() has
been marked as deprecated and will be removed with TYPO3 v14.
Method
BackendUserAuthentication::getWebmounts() was
introduced as substitution. It returns a unique list of integer uids
instead of a list of strings, which is more type safe.
Superfluous calls to array_unique() can be removed since the uniqueness
is now guaranteed by BackendUserAuthentication::getWebmounts().
Impact
Calling
BackendUserAuthentication::returnWebmounts() will trigger a PHP
deprecation warning.
Affected installations
All installations using
BackendUserAuthentication::returnWebmounts()
are affected.
Migration
Existing calls to
BackendUserAuthentication::returnWebmounts() should
be replaced by
BackendUserAuthentication::getWebmounts().
If third party extensions convert the previous result array from an array of
strings to an array of integers, this can be skipped. In addition
superfluous calls to array_unique() can be removed since the uniqueness
is now guaranteed by BackendUserAuthentication::getWebmounts().
The method
\TYPO3\CMS\Backend\Utility\BackendUtility::thumbCode() has been deprecated since the
method is no longer used in TYPO3 anymore. Additionally, due to multiple changes
to file processing over the years, e.g. introducing of FAL, the method's
signature changed a couple of times leading to a couple of method arguments
are being unused, which is quite a bad API.
Impact
Calling the PHP method
BackendUtility::thumbCode() will
trigger a PHP deprecation warning.
Affected installations
TYPO3 installations with custom extensions using this method. The extension
scanner will report any usage as strong match.
Migration
Remove any usage of this method. In case you currently rely on the
functionality, you can copy it to your custom extension. However, you might
want to consider refactoring the corresponding code places.
The method basically resolved given
FileReference objects. In case
a file could not be resolved, a special icon has been rendered. Otherwise,
the cropping configuration has been applied and the file's
process()
has been called to get the thumbnail, which has been wrapped in corresponding
thumbnail markup. This might has been extended to also open the information
modal on click.
This means the relevant parts are:
// Get file references
$fileReferences = BackendUtility:resolveFileReferences($table, $field, $row);
// Check for existence of the file
$fileReference->getOriginalFile()->isMissing()
// Render special icon if missing
$iconFactory
->getIcon('mimetypes-other-other', IconSize::MEDIUM, 'overlay-missing')
->setTitle(static::getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.file_missing') . ' ' . $fileObject->getName())
->render()
// Process file with cropping configuration if not missing
$fileReference->getOriginalFile()->process(
ProcessedFile::CONTEXT_IMAGEPREVIEW,// ProcessedFile::CONTEXT_IMAGECROPSCALEMASK if cropArea is defiend
[
'width' => '...',
'height' => '...',
'crop'// If cropArea is defined
]
)
// Use cropped file and create <img> tag
<img src="' . $fileReference->getOriginalFile()->process()->getPublicUrl() . '"/>
// Wrap the info popup via <a> around the thumbnail
<a href="#" data-dispatch-action="TYPO3.InfoWindow.showItem" data-dispatch-args-list="_FILE,' . (int)$fileReference->getOriginalFile()->getUid() . '">
Calling above methods triggers a deprecation level log entry in TYPO3 v13 and
will trigger a fatal PHP error with TYPO3 v14.
Affected installations
RenderingContext->getRequest() is a relatively common call in custom
view helpers. Instances with extensions that deliver custom view helpers may
be affected. The extension scanner is not configured to find potential
places since the method names are common and would lead to too many false
positives.
Migration
Class
\TYPO3\CMS\Fluid\Core\Rendering\RenderingContext of the Core
extension Fluid extends class
\TYPO3Fluid\Fluid\Core\Rendering\RenderingContext
of Fluid standalone and adds the methods
setRequest() and
getRequest().
These methods are however not part of
\TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface .
Fluid standalone will not add these methods, since the view of this library should
stay free from direct PSR-7
\ServerRequestInterface
dependencies. Having those
methods in ext:fluid
RenderingContext
however collides with
\RenderingContextInterface ,
which is type hinted in Fluid view helper method signatures.
Fluid standalone instead added three methods to handle arbitrary additional data
in
\RenderingContextInterface :
setAttribute(),
hasAttribute()
and
getAttribute(). Those should be used instead.
Method
\TYPO3\CMS\Fluid\View\TemplatePaths->fillDefaultsByPackageName()
has been marked as deprecated in TYPO3 v13 and will be removed in TYPO3 v14.
Fluid template paths should be set directly using the methods
setTemplateRootPaths(),
setLayoutRootPaths() and
setPartialRootPaths(), or - even better - be handled by
ViewFactoryInterface, which comes as new feature in TYPO3 v13.
Calling
fillDefaultsByPackageName() triggers a deprecation level
log level entry in TYPO3 v13 and will be removed in TYPO3 v14.
Affected installations
The method is relatively rarely used by extensions directly, a usage in
Extbase
ActionController has been refactored away. The extension
scanner will find candidates.
Note class
TemplatePaths is marked @internal and should not be
used by extensions at all.
Migration
Use
\TYPO3\CMS\Core\View\ViewFactoryInterface to create views with
proper template paths instead. The TYPO3 system extensions come with plenty
of examples on how to do this.
Deprecation: #104773 - Custom Fluid views and Extbase
Using one of the above classes triggers a deprecation level log entry.
Affected installations
Instances with extensions that create view instances of
\StandaloneView or
\TemplateView are affected. The extension
scanner will find possible candidates.
Migration
Extensions should no longer directly instantiate own views, but should get
\TYPO3\CMS\Core\View\ViewFactoryInterface injected and use
create()
to retrieve a view.
Within Extbase,
ActionController->defaultViewObjectName should only be
set to Extbase
JsonView if needed, or not set at all. Custom view implementations
should implement an own
ViewFactoryInterface and configure
controllers to inject an instance, or can set
$this->defaultViewObjectName = JsonView::class
in a custom
__construct().
Method
\TYPO3\CMS\Backend\LoginProvider\LoginProviderInterface->render() has been marked as deprecated
and is substituted by
LoginProviderInterface->modifyView() that will
be added to the interface in TYPO3 v14, removing
render() from the
interface in v14.
Related to this, event
\TYPO3\CMS\Backend\LoginProvider\Event\ModifyPageLayoutOnLoginProviderSelectionEvent
has been changed to deprecate
getController() and
getPageRenderer(),
while
getRequest() has been added.
getView() now typically returns
an instance of
ViewInterface.
The default
LoginProviderInterface
implementation is
UsernamePasswordLoginProvider
provided by ext:core. This consumer has been adapted.
Using
LoginProviderInterface->render() in TYPO3 v13 will trigger a
deprecation level log entry and will fail in v14.
Affected installations
Instances with custom login providers that change the TYPO3 backend login
field rendering may be affected. The extension scanner is not configured to
find usages, since method name
render() is too common. A deprecation
level log message is triggered upon use of the old method.
Migration
Consumers of
LoginProviderInterface
should implement
modifyView() instead, the transition should be smooth.
Consumers that need the
PageRenderer
for JavaScript magic, should use dependency injection
to receive an instance.
The default implementation in
UsernamePasswordLoginProvider
is a good example. Extensions that need to configure additional template, layout or
partial lookup paths can extend them:
Consumers of
ModifyPageLayoutOnLoginProviderSelectionEvent
should use the request instead, and/or should get an instance of
PageRenderer injected as well.
Deprecation: #104778 - Instantiation of IconRegistry in ext_localconf.php
Since TYPO3 v11 it is possible to automatically register own icons via
Configuration/Icons.php. Prior to this, extension authors used to register
icons manually via instantiating the php:\TYPO3\CMS\Core\Imaging\IconRegistry
in their ext_localconf.php
files. This method has now been deprecated. It is recommended to switch to
the newer method introduced with forge#94692.
Impact
Instantiating
IconRegistry inside
ext_localconf.php files will trigger a deprecation-level log entry.
Affected installations
All installations, which instantiate
IconRegistry
before the
\TYPO3\CMS\Core\Core\Event\BootCompletedEvent . This includes
ext_localconf.php files as well as TCA/Overrides.
Migration
The most common use-cases can be accomplished via the Configuration/Icons.php
file.
For more complex tasks, it is recommended to register an event listener for the
BootCompletedEvent . At this stage the system
is fully booted and you have a completely configured IconRegistry at hand.
In case the registry was used in TCA/Overrides files to retrieve icon
identifiers, then this should be replaced completely with static identifiers.
The reason behind this is, that the registry isn't even fully usable at this
stage. TCA isn't fully built yet and icons can still be registered at a later
point.
Fluid standalone will add proper language syntax for booleans and null
with Fluid v4, which will be used in TYPO3 v13. Thus, user-defined variables
named true, false and null are no longer allowed.
Impact
Assigning variables with name true, false or null will throw
an exception in Fluid v4. In preparation of this change, Fluid v2.15 logs a
deprecation level error message if any of these variable names are used.
Affected installations
Instances with Fluid templates using true, false or null as user-defined variable names.
This should rarely happen, as it would involve using
$view->assign('true', $someVar).
Migration
Template code using these variables should be adjusted to use different variable names.
In Fluid v4, the variables will contain their matching PHP counterparts.
Deprecation: #104789 - renderStatic() for Fluid ViewHelpers
The usage of
renderStatic() for Fluid ViewHelpers has been deprecated.
Also, Fluid standalone traits
\TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic
and
\TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic
have been marked as deprecated.
Impact
Using one of the mentioned traits or
renderStatic() in ViewHelpers
logs a deprecation level error message in Fluid standalone v4.
renderStatic()
will no longer be called in Fluid standalone v5.
renderStatic() and both
traits continue to work without deprecation level error message in
Fluid standalone v2.
Affected installations
Instances with custom ViewHelpers using any variant of
renderStatic() are affected.
Migration
ViewHelpers should always use
render() as their primary rendering method.
ViewHelpers using just
renderStatic() without any trait or with the trait
\CompileWithRenderStatic
can be migrated by converting the static rendering method to a non-static method:
ViewHelpers using
\TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic
can use the new contentArgumentName feature added with Fluid v2.15:
The database table tt_content contains all necessary fields for rendering
content elements.
Back with TYPO3 v4.7, a major feature to render certain Content Types in a more
accessible way, funded by the German Government (BLE_) with the
"Konjunkturpaket II" was merged into CSS Styled Content.
In this procedure, certain Content Types received new fields and rendering definitions, which
were stored in the database fields accessibility_title, accessibility_bypass
and accessibility_bypass_text.
When CSS Styled Content was removed in favor of Fluid Styled Content in TYPO3 v8, the DB
fields continued to exist in TYPO3 Core, so a migration from CSS Styled Content was possible.
However, the DB fields are not evaluated anymore since then, and are removed, along with
their TCA definition in tt_content.
If these fields are still relevant for a custom legacy installation, these DB fields need to be
re-created via TCA for further use in a third-party extension.
Important: #104126 - Drop "typo3conf" directory from system status check and backend locking
The directory typo3conf is no longer needed in Composer Mode.
Checking for the existence of this directory is no longer performed in the
Environment and Install Tool.
Previously it contained:
extensions (which are now Composer packages stored in vendor/),
the configuration files (which are now part of the config/ tree)
language labels and some artifact states (now part of var/)
After TYPO3 v13.0, only new functionality with a solid migration path
can be added on top, aiming for as few as possible breaking changes
after the initial v13.0 release on the way to LTS.
Adds a configuration option to adapt the environment check in the Install Tool
for a list of sanctioned
disable_functions.
With the new configuration option
$GLOBALS['TYPO3_CONF_VARS']['SYS']['allowedPhpDisableFunctions'] ,
a system maintainer can add native PHP function names to this list,
which are then reported as environment warnings instead of errors.
You can also define this in your settings.php file manually
or via Admin Tools > Settings > Configure options.
Impact
Native php function names can be added as an array of function names, which will
not trigger an error but only a warning, if they can also be found in the php.ini
setting
disable_functions.
Feature: #92009 - Provide backend modules in LiveSearch
The backend LiveSearch is now capable of listing backend modules, a user has
access to, offering the possibility of alternative navigation to different
parts of the backend.
Impact
Backend users now have another possibility to quickly access a backend module.
Feature: #99203 - Streamline FE/versionNumberInFilename to 'EXT:' resources
Local resources are currently not "cache-busted", for example, have no version
in URL. TypoScript has no possibility to add the cache buster. When replacing
them a new filename must be used (which feels little hacky).
getText "asset" to cache-bust assets in TypoScript
The listing of resources in the TYPO3 backend, e.g. in the
File > Filelist module or the FileBrowser can be switched
between list and tiles. TYPO3 serves tiles by default.
A new User TSconfig option
options.defaultResourcesViewMode has
been introduced, which allows the initial display mode to be defined. Valid
values are therefore list and tiles, e.g.:
options.defaultResourcesViewMode = list
Copied!
Impact
Integrators can now define the default display mode for resources via
User TSconfig.
Feature: #102326 - Allow custom translations for Extbase validators
All validation messages from Extbase validators can now be overwritten
using validator options. It is possible to provide either a translation key or
a custom message as string.
Extbase validators providing only one validation message can be overwritten by a
translation key or message using the validator option
message. Validators
providing multiple validation messages (e.g.
Boolean,
NotEmpty or
NumberRange) use different validator options keys. In general,
translation keys or messages for validators are registered in the validator
property
translationOptions.
In this example, translation option keys for the
NotEmptyValidator are
overwritten for the property
$myProperty. The
locallang.xlf
translation file from the extension
my_extension will be used to lookup
translations for the newly provided translation key options.
In this example, translation option keys for the
FloatValidator are
overwritten for the property
$myProperty. The message string is
shown if validation fails.
Impact
The new validator translation option keys allow developers to define unique
validation messages for TYPO3 Extbase validators on validator usage basis. This
may result in a better user experience, since validation messages now can refer
to the current usage scope (e.g. "The field 'Title' is required" instead of
"The given subject was empty.").
Feature: #102337 - PSR-14 event for modifying record list export data
A new PSR-14 event
\TYPO3\CMS\Backend\RecordList\Event\BeforeRecordDownloadIsExecutedEvent
has been introduced to modify the result of a download / export initiated in
the Web > List module.
This replaces the
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList']['customizeCsvHeader']
and
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList']['customizeCsvRow'] ,
hooks, which have been deprecated.
The event allows body and header sections of the data dump to be modified,
so that they can e.g. be used to redact specific data for GDPR compliance,
transform / translate specific data, trigger creation of archives or web hooks,
log export access and more.
The event offers the following methods:
getHeaderRow(): Return the current header row of the dataset.
setHeaderRow(): Sets the modified header row of the dataset.
getRecords(): Returns the current body rows of the dataset.
setRecords(): Sets the modified body rows of the dataset.
getRequest(): Returns the PSR request context.
getTable(): Returns the name of the database table of the dataset.
getFormat(): Returns the format of the download action (CSV/JSON).
getFilename(): Returns the name of the download filename (for browser output).
getId(): Returns the page UID of the download origin.
getModTSconfig(): Returns the active module TSconfig of the download origin.
getColumnsToRender(): Returns the list of header columns for the triggered download.
isHideTranslations(): Returns whether translations are hidden or not.
Example
The corresponding event listener class:
<?phpdeclare(strict_types=1);
namespaceVendor\MyPackage\Core\EventListener;
useTYPO3\CMS\Backend\RecordList\Event\BeforeRecordDownloadIsExecutedEvent;
useTYPO3\CMS\Core\Attribute\AsEventListener;
#[AsEventListener(identifier: 'my-package/record-list-download-data')]final readonly classDataListener{
publicfunction__invoke(BeforeRecordDownloadIsExecutedEvent $event): void{
// List of redactable fields.
$gdprFields = ['title', 'author'];
$headerRow = $event->getHeaderRow();
$records = $event->getRecords();
// Iterate header to mark redacted fields...foreach ($headerRow as $headerRowKey => $headerRowValue) {
if (in_array($headerRowKey, $gdprFields, true)) {
$headerRow[$headerRowKey] .= ' (REDACTED)';
}
}
// Redact actual content...foreach ($records as $uid => $record) {
foreach ($gdprFields as $gdprField) {
if (isset($record[$gdprField])) {
$records[$uid][$gdprField] = '(REDACTED)';
}
}
}
$event->setHeaderRow($headerRow);
$event->setRecords($records);
}
}
Copied!
Migration
The functionality of both hooks
customizeCsvHeader and
customizeCsvRow are now handled by the new PSR-14 event.
Migrating
customizeCsvHeader
The prior hook parameter/variable
fields is now available via
$event->getColumnsToRender(). The actual record data
(previously
$this->recordList, submitted to the hook as its object
reference) is accessible via
$event->getHeaderRow().
Migrating
customizeCsvRow
The prior hook parameters/variables have the following substitutes:
databaseRow is now available via
$event->getRecords() (see note below).
tableName is now available via
$event->getTable().
pageId is now available via
$event->getId().
The actual record data
(previously
$this->recordList, submitted to the hook as its object
reference) is accessible via
$event->getRecords().
Please note that the hook was previously executed once per row retrieved
from the database. The PSR-14 event however - due to performance reasons -
is only executed for the full record list after database retrieval,
thus allowing post-processing on the whole dataset.
Impact
Using the PSR-14 event
BeforeRecordDownloadIsExecutedEvent it is
now possible to modify all of the data available when downloading / exporting
a list of records via the Web > List module.
Feature: #102337 - PSR-14 event for modifying record list download presets
A new PSR-14 event
\TYPO3\CMS\Backend\RecordList\Event\BeforeRecordDownloadPresetsAreDisplayedEvent
has been introduced to manipulate the list of available download presets in
the Web > List module.
getPresets(): Returns a list of presets set via TSconfig
setPresets(): Sets a modified list of presets.
getDatabaseTable(): Returns the database table name that a preset applies to.
getRequest(): Returns the PSR Request object for the context of the request.
getId(): Returns the page ID of the originating page.
Note that the event is dispatched for one specific database table. If
an event listener is created to attach presets to different tables, the
listener method must check for the table name, as shown in the example below.
If no download presets exist for a given table, the PSR-14 event can still
be used to modify and add presets to it via the
setPresets() method.
The array passed from
getPresets() to
setPresets() can contain
an array collection of
\TYPO3\CMS\Backend\RecordList\DownloadPreset
objects with the array key using the preset label.
The existing presets can be retrieved with these getters:
$preset->getLabel(): Name of the preset (can utilize LLL translations), optional.
$preset->getColumns(): Array of database table column names.
$preset->getIdentifier(): Identifier of the preset (manually set or calculated based on label and columns)
The event listener can also remove array indexes or columns of existing
array entries by passing a newly constructed
DownloadPreset object with the
changed label and columns constructor properties.
Using the PSR-14 event
BeforeRecordDownloadPresetsAreDisplayedEvent
it is now possible to modify the presets of each table for
downloading / exporting a list of such records via the Web > List
module.
Feature: #102337 - Presets for download of record lists
In the Web > List backend module, the data of records for each
database table (including pages and content records) can be downloaded.
This export takes the currently selected list of columns into consideration and
alternatively allows all columns to be selected.
A new feature has been introduced adding the ability to pick the exported
data columns from a list of configurable presets.
Those presets can be configured via page TSconfig, and can also be
overridden via user TSconfig (for example, to make certain presets
only available to specific users).
Each entry of
mod.web_list.downloadPresets
defines the table name on the first level (in this case pages), followed by
any number of presets.
Each preset contains a
label (the displayed name of the preset,
which can be a locallang key), a comma-separated list of each column that
should be included in the export as
columns and optionally
an
identifier. If
identifier is not provided,
the identifier is generated as a hash of the
label and
columns.
This can be manipulated with user TSConfig by adding the
page.
prefix. User TSConfig is loaded after page TSConfig, so you can overwrite
existing keys or replace the whole list of keys:
Since any table can be configured for a preset, any extension
can deliver a defined set of presets through the
EXT:my_extension/Configuration/page.tsconfig file and
their table name(s).
The backend LiveSearch is now able to list workspaces a backend user has access
to, offering the possibility to switch to a workspace quickly outside the
Workspaces module.
Impact
Backend users now have another, quickly accessible way to access a workspace.
With proper permissions, a backend user may also switch to the edit interface of
a workspace to configure its settings.
Feature: #102951 - Provide PSR-7 request in Extbase validators
Extbase
abstractValidator now provides a getter and a setter
for the PSR-7 Request object. Validators extending
AbstractValidator
will include the PSR-7 request object, if the validator has been instantiated
by Extbase
ValidationResolver.
Impact
Extension developers can now create custom validators which consume data
from the PSR-7 request object (e.g. request attribute
frontend.user).
This feature introduces the new PSR-14 event
ModifyRedirectUrlValidationResultEvent in the felogin extension to
provide developers the possibility and flexibility to implement custom
validation for the redirect URL. This may be useful if TYPO3 frontend login
acts as an SSO system or if users should be redirected to an external URL after
login.
Developers now have the possibility to modify the validation results for the
redirect URL, allowing redirects to URLs not matching existing validation
constraints.
Feature: #103493 - Edit full record in "Check Links" module
Previously, the listing in the Check Links backend module
provided the possibility to edit the field of a record that has been identified
as having a broken link. However, in some cases relevant context might be missing,
e.g. when editing redirect records.
Therefore, a new button has been introduced which allows the full record of the
broken link to be edited. The new button is placed next to the
existing - single field - edit button.
Impact
A new button is now displayed in the Check Links backend module,
allowing the full record of a broken link to be edited.
Feature: #103706 - Default search level for record search
When searching for records in the Web > List module as well as
the database browser, it's possible to select the search levels (page tree
levels to respect in the search).
An editor is therefore able to select between the current page, a couple of
defined levels (e.g. 1, 2, 3) as well as the special "infinite levels".
Those options can already be extended using the TSconfig option
mod.web_list.searchLevel.items.
Next to this, a new TSconfig option
mod.web_list.searchLevel.default
has been introduced, which allows to define one of the available level options
as the default level to use.
Example
EXT:my_sitepackage/Configuration/page.tsconfig
# Set the default search level to "infinite levels"
mod.web_list.searchLevel.default = -1
Copied!
Impact
It's now possible to define a default search level using the new page
TSconfig option
mod.web_list.searchLevel.default.
Feature: #103783 - RecordTransformation Data Processor
A new TypoScript data processor for
FLUIDTEMPLATE and
PAGEVIEW has been added.
The
record-transformation Data Processor can typically be used in
conjunction with the DatabaseQuery Data Processor. The DatabaseQuery Data
Processor typically fetches records from the database, and the
record-transformation will take the result and transform
the objects into
Record objects, which contain relevant data from
the TCA table, which has been configured in the TCA columns fields for this
record.
This is especially useful for TCA tables, which contain "types" (such as pages
or tt_content database tables), where only relevant fields are added to the
record object. In addition, special fields from "enableColumns" or deleted
fields, as well as language and version information are extracted so they can be
dealt with in a unified way.
The "type" property contains the database table name and the actual type based
on the record, such as "tt_content.textmedia" for Content Elements.
Note
The Record object is available but details are still to be finalized in
the API by TYPO3 v13 LTS. Right now only the usage in Fluid is public
API.
Impact
Example of the data processor being used in conjunction with DatabaseQuery
processor.
Transform the current data array of
FLUIDTEMPLATE to a Record
object. This can be used for Content Elements of Fluid Styled Content or
custom ones. In this example the FSC element "Text" has its data transformed for
easier and enhanced usage.
tt_content.text {
templateName = Text
dataProcessing {
10 = record-transformation
10 {
as = data
}
}
}
Copied!
Usage in Fluid templates
The
f:debug output of the Record object is misleading for integrators,
as most properties are accessed differently than would be expected. The debug view
is a better organized overview of all the available information. E.g.
the property properties lists all relevant fields for the current Content
Type.
We are dealing with an object here. You can, however, access your record
properties as you are used to with
{record.title} or
{record.uid}. In addition, you gain special, context-aware properties
like the language
{record.languageId} or workspace
{data.versionInfo.workspaceId}.
Overview of all possibilities:
<!-- Any property, which is available in the Record (like normal) -->
{record.title}
{record.uid}
{record.pid}
<!-- Language related properties -->
{record.languageId}
{record.languageInfo.translationParent}
{record.languageInfo.translationSource}
<!-- The overlaid uid -->
{record.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) -->
{record.mainType}
<!-- "textpic" (this is the CType) -->
{record.recordType}
<!-- "tt_content.textpic" (Combination of mainType and record type, separated by a dot) -->
{record.fullType}
<!-- System related properties -->
{data.systemProperties.isDeleted}
{data.systemProperties.isDisabled}
{data.systemProperties.isLockedForEditing}
{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}
Copied!
Note
The
{record} object contains only properties relevant for
the current record type (e.g. CType for
tt_content). If
you need to access properties which are not defined for the record
type, which is usually the case for fields of TCA type passthrough,
the "raw" record can be used by accessing it via
{record.rawRecord}.
Note that those properties are not transformed (Feature: #103581 - Automatically transform TCA field values for record objects).
Available options
The variable that contains the record(s) from a previous data processor,
or from a FLUIDTEMPLATE view. Default is :typoscript:`data`.
variableName = items
# the name of the database table of the records. Leave empty to auto-resolve# the table from context.
table = tt_content
# the target variable where the resolved record objects are contained# if empty, "record" or "records" (if multiple records are given) is used.
as = myRecords
Copied!
Feature: #103894 - Additional properties for columns in Page Layouts
Backend Layouts were introduced in TYPO3 v6 in order to customize the view of
the Page module in TYPO3 backend for pages, but has since grown, also in
frontend rendering, to select e.g. Fluid template files via TypoScript for a page,
commonly used via
data:pagelayout.
In order to use a single source for backend and frontend representation, the
definition of a "Backend Layout" or "Page Layout" is expanded to also include
more information for a specific content area. The Content Area was previously
defined via "name" (for the label in the Page module) and "colPos",
the numeric database field in which content is grouped in.
A definition can now optionally also contain a "slideMode" property and an
"identifier" property next to each colPos, in order to simplify frontend
rendering.
Whereas "identifier" is a speaking representation for the colPos, such as
"main", "sidebar" or "footerArea", the "slideMode" can be set to one of the
three options:
slideMode = slide - if no content is found, check the parent
pages for more content
slideMode = collect - use all content from this page, and the
parent pages as one collection
slideMode = collectReverse- same as "collect" but in the
opposite order
With this information added, a new DataProcessor
page-content
(
\TYPO3\CMS\Frontend\DataProcessing\PageContentFetchingProcessor )
is introduced for the frontend rendering,
which fetches all content for a page and respecting the settings from the
page layout.
Enriching the backend layout information for each colPos enables a TYPO3
integrator to write less TypoScript in order to render content on a page.
The DataProcessor fetches all content elements from all defined columns with an
included "identifier" in the selected backend layout and makes the resolved
record objects available in the Fluid template via
{content."myIdentifier".records}.
Example of an enriched backend layout definition:
mod.web_layout.BackendLayouts {
default {
title = Default
config {
backend_layout {
colCount = 1
rowCount = 1
rows {
1 {
columns {
1 {
name = Main Content Area
colPos = 0
identifier = main
slideMode = slide
}
}
}
}
}
}
}
}
The
f:cObject ViewHelper above uses the rendering definition of the
tt_content table
{record.mainType} to render the Content Element from
the list. The attribute
data expects the raw database record, which is
retrieved from
{record}.
A new Schema API is introduced to access information about TCA structures
in a unified way.
The main goal of this architecture is to reduce direct access to
$GLOBALS['TCA'] after the Bootstrap process is completed.
The Schema API implements the following design goals:
An object-oriented approach to access common TCA information such as if a
database table is localizable or workspace-aware, if it has a "deleted" field
("soft-delete"), or other common functionality such as "enableFields" / "enablecolumns",
which can be accessed via "Capabilities" within a Schema.
A unified way to access information which "types" a TCA table has available,
such as "tt_content", where the "CType" field is the divisor for types, thus,
allowing a Schema to have sub-schemata for a TCA Table.
The API in turn then handles which fields are available for a specific "CType".
An example is "tt_content" with type "textpic": The sub-schema "tt_content.textpic"
only contains the fields that are registered of that "CType", such as "bodytext",
which then knows it is a Rich Text Field (the default column does not have this information),
or "image" (a file relation field), but the sub-schema does not contain fields
that are irrelevant for this type, such as "assets" (also a file relation field).
An abstracted approach to available TCA field types such as "input" or "select",
which also takes information into account, if a select field is a selection of a
static list (such as "pages.layout") or if it contains a relation to another
schema or field (based on "foreign_table"). Previously, this was evaluated in
many places in TYPO3 Core, and can now be reduced.
Thus, Schema API can now be utilized to determine the
RelationshipType
of a relational field type in a unified way without having to deal with deeply
nested arrays.
Information about relations to other database tables or fields. This is
especially useful when dealing with Inline elements or category selection fields.
Schema API can find out, which fields of other schemata are pointing to one-self.
Schema API differentiates between an "Active Relation" and a "Passive Relation".
An Active Relation is the information that a field such as "pages.media"
(a field of type "file") contains a reference to the "sys_file_reference.uid_foreign"
field. Active Relations in consequence are connected to a specific field
(of type
RelationalFieldTypeInterface).
In turn, a "Passive Relation" is the information what other schemata/fields are
pointing to a specific table or field.
A common example of a "Passive Relation" is "sys_workspace_stage":
The information stored in
$GLOBALS[TCA][sys_workspace_stage] does not contain
the information that this table is actually used as a reference from the database
field sys_workspace.custom_stages, the sys_workspace_stage Schema now
contains this information directly via
TcaSchema->getPassiveRelations().
This is possible as TcaSchemaFactory is evaluating all TCA information and
holistically as a graph. Passive Relations
are currently only connected to a Schema, and Active Relations to a Field or
a Schema.
As the Schema API fetches information solely based on the TCA, an Active Relation
only points to possible references, however, the actual reference
(does a record really have a connection to another database table) would
require an actual Record instance (a database row) to evaluate this information.
Relations do not know about the "Type" or "Quantity" (many-to-many etc) as
this information is already stored in the Field information. For this reason,
the "Relations" currently only contain a flat information structure of the table
(and possibly a field) pointing TO another schema name (Active Relation) or
FROM another schema name / field (Passive Relation).
Schema API also parses all available FlexForm data structures in order to
resolve relations. As a result, a field of type FlexFormField contains
a list of possible "FlexFormSchema" instances, which resolve all fields, sheets
and section containers within each data structure.
Once built, the Schema can never be changed. Whereas the TCA could be
overridden at runtime, all TCA is now evaluated once and
then cached. This is a consequence of working with an object-oriented approach.
If the TCA is changed after the Bootstrap process is completed,
the Schema needs to be rebuilt manually, which TYPO3 Core currently does, for
example, in some Functional Testing Scenarios.
All key objects (Schema, FieldType, Capabilities) are treated as immutable DTOs
and never contain cross-references to their parent objects (Sub schemata do not
know information about their parent schema, a field does not know which schema
it belongs to), so the only entry point is always the
TcaSchemaFactory
object.
This design allows the API to be fully cacheable at PHP level as a nested tree.
Low-level, not full-fledged but serves as a basis.
Several API decisions were made in order to let Schema API keep only its
original purpose, but can be encapsulated further in other APIs:
Schema API is not available during Bootstrap as it needs TCA to be available
and fully finished.
Schema API does not contain all available TCA properties for each field type.
An example is "renderType" for select fields. This information is not relevant
when querying records in the frontend, and mainly relevant for FormEngine -
it is not generic enough to justify a getter method.
Extensibility: custom field types are currently not available
until TYPO3 Core as fully migrated to Schema API.
User Permissions: Evaluating if a user has access to "tt_content.bodytext"
requires information about the currently logged in user, thus it is not part of the
Schema API. A "Permission API" should evaluate this information in the
future.
Available options for a field. As an example, a common scenario is to find out
which possible options are available for "pages.backend_layout". In TYPO3 Core
an
itemsProcFunc is connected to that field in TCA. Whether there is an
itemsProcFunc is stored, but Schema API is not designed to actually
execute the itemsProcFunc as it is dependent on various factors evaluated during
runtime, such as the page it resides on, user permissions or PageTsConfig
overrides.
Schema API is currently marked as internal, as it might be changed during
TYPO3 v13 development, because more parts of TYPO3 will be migrated towards
Schema API.
DataHandler and the Record Factory already utilize Schema API in order to reduce
direct access to
$GLOBALS[TCA].
In the future Schema API might be used to evaluate information
for Site Configurations, like TCA and FlexForms.
Impact
Reading and writing
$GLOBALS[TCA] within Configuration/TCA/*
and via TCA Overrides is untouched, as the API is meant for reading the
information there in a unified way.
Usage
The API can now be used to find out information about TCA fields.
publicfunction__construct(
protected readonly PageRepository $pageRepository,
protected readonly TcaSchemaFactory $tcaSchemaFactory
){}
publicfunctionmyMethod(string $tableName): void{
if (!$this->tcaSchemaFactory->has($tableName)) {
// this table is not managed via TYPO3's TCA APIreturn;
}
$schema = $this->tcaSchemaFactory->get($tableName);
// Find out if a table is localizableif ($schema->isLocalizable()) {
// do something
}
// Find all registered types
$types = $schema->getSubSchemata();
}
Copied!
Using the API improves handling for parts such as evaluating
columnsOverrides,
foreign field structures, FlexForm Schema parsing, and evaluating type fields
for database fields.
Feature: #104020 - ViewHelper to check feature flags
The <f:feature> ViewHelper allows integrators to check for feature flags from within Fluid
templates. The ViewHelper follows the same rules as the underlying TYPO3 api, which means
that undefined flags will be treated as false.
Examples
Basic usage
<f:feature name="myFeatureFlag">
This is being shown if the flag is enabled
</f:feature>
Copied!
feature / then / else
<f:feature name="myFeatureFlag">
<f:then>
Flag is enabled
</f:then>
<f:else>
Flag is undefined or not enabled
</f:else>
</f:feature>
Copied!
Impact
Feature flags can now be checked from within Fluid templates.
Feature: #104067 - Sorting of forms in the form module
The notifications shown on the lower right now have a "Clear all" button to allow the
user to clear all notifications with a single click. This button is only displayed when
two or more notifications are on screen.
In case the height of the notification container exceeds the viewport, a scroll bar will
allow the user to navigate through the notifications.
Impact
Handling of multiple notifications has been improved by allowing to
scroll and clear all notifications at once.
Feature: #104085 - Edit specific columns of multiple records in List module
Using the "Show columns" button on a record table in the Web > List
backend module allows to select the columns to be displayed for the
corresponding table listing.
When selecting multiple records, it has already been possible to edit all
those records at once, using the "Edit" button in the table header.
Now, a new button "Edit columns" has been introduced, which additionally
allows to access the editing form for the selected records with just the
columns of the current selection (based on "Show columns"). This improves
the usability when doing mass editing of specific columns.
Impact
It's now possible to edit the columns of multiple records in the
Web > List backend module, using the new "Edit columns" button.
Feature: #104095 - Edit specific columns of multiple files in Filelist module
Using the "Show columns" action in the File > Filelist
backend module allows to select the columns to be displayed file and
folder listing.
When selecting multiple files, it has already been possible to edit the
metadata of all those records at once, using the "Edit Metadata" button
above the listing.
Now, a new button "Edit selected columns" has been introduced, which
additionally allows to access the editing form for the selected files
with just the columns of the current selection (based on "Show columns").
This improves the usability when doing mass editing of specific columns.
Impact
It's now possible to edit selected columns of multiple file metadata in the
File > Filelist backend module, using the new
"Edit selected columns" button.
Feature: #104114 - Command to generate Fluid schema files
With Fluid Standalone 2.12, a new implementation of the XSD schema generator has
been introduced, which was previously a separate composer package. These XSD files
allow IDEs to provide autocompletion for ViewHelper arguments in Fluid templates,
provided that they are included in the template by using the xmlns syntax:
A new CLI command has been defined to apply Fluid's new schema generator to TYPO3's
Fluid integration. New Fluid APIs are used to find all ViewHelpers that exist in
the current project (based on the composer autoloader). Then, TYPO3's configuration
is checked for any merged Fluid namespaces (like f:, which consists of both
Fluid Standalone and EXT:fluid ViewHelpers which in some cases override each other).
After that consolidation, *.xsd files are created in var/transient/ using another
API from Fluid Standalone. These files which will automatically get picked up by
supporting IDEs (like PhpStorm) to provide autocompletion in template files.
Impact
To get autocompletion for all available ViewHelpers in supporting IDEs, the following
CLI command can be executed in local development environments:
vendor/bin/typo3 fluid:schema:generate
Copied!
Feature: #104220 - Make parseFunc allowTags and denyTags optional