TYPO3 v15 dev-main snapshot ()
This commit is contained in:
+76
@@ -0,0 +1,76 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-100887-1774712028:
|
||||
|
||||
======================================================================================================
|
||||
Deprecation: #100887 - Deprecation of useNonce argument in f:asset:css and f:asset:script view helpers
|
||||
======================================================================================================
|
||||
|
||||
See :issue:`100887`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :html:`useNonce` argument in the :html:`f:asset.script` and
|
||||
:html:`f:asset.css` ViewHelpers has been renamed to :html:`csp` to better
|
||||
reflect its purpose (controlling Content-Security-Policy hash/nonce
|
||||
collection rather than nonce usage specifically).
|
||||
|
||||
Similarly, the :php:`'useNonce'` asset option key accepted by
|
||||
:php:`addJavaScript()` and :php:`addStyleSheet()` in class
|
||||
:php:`\TYPO3\CMS\Core\Page\AssetCollector` has been
|
||||
replaced by :php:`'csp'`.
|
||||
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Passing :html:`useNonce` as a ViewHelper argument or as an
|
||||
:php-short:`\TYPO3\CMS\Core\Page\AssetCollector` option key will trigger
|
||||
a deprecation-level log entry in TYPO3 v14. This usage is scheduled for
|
||||
removal in TYPO3 v15.
|
||||
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
Installations with Fluid templates using :html:`<f:asset.script useNonce="1">`
|
||||
or :html:`<f:asset.css useNonce="1">`, and extensions calling
|
||||
:php:`AssetCollector::addJavaScript()` or :php:`AssetCollector::addStyleSheet()`
|
||||
with :php:`['useNonce' => true]`.
|
||||
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Replace the :html:`useNonce` argument with :html:`csp` in Fluid templates:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<!-- Before -->
|
||||
<f:asset.script identifier="my-script"
|
||||
src="EXT:my_ext/Resources/Public/JavaScript/foo.js"
|
||||
useNonce="1" />
|
||||
|
||||
<!-- After -->
|
||||
<f:asset.script identifier="my-script"
|
||||
src="EXT:my_ext/Resources/Public/JavaScript/foo.js"
|
||||
csp="1" />
|
||||
|
||||
Replace the :php:`'useNonce'` option key with :php:`'csp'` in PHP:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
// Before
|
||||
$assetCollector->addJavaScript('my-script', $src, [], ['useNonce' => true]);
|
||||
|
||||
// After
|
||||
$assetCollector->addJavaScript('my-script', $src, [], ['csp' => true]);
|
||||
|
||||
The :php-short:`\TYPO3\CMS\Core\Page\PageRenderer` methods :php:`addJsInlineCode()`,
|
||||
:php:`addJsFooterInlineCode()`, and :php:`addCssInlineBlock()` retain their
|
||||
:php:`$useNonce` parameter names for backward compatibility. No migration is
|
||||
required for callers of these methods.
|
||||
|
||||
|
||||
.. index:: Fluid, PHP-API, PartiallyScanned, ext:core, ext:fluid
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-107068-1759214357:
|
||||
|
||||
==================================================================
|
||||
Deprecation: #107068 - Rename fieldExplanationText to description
|
||||
==================================================================
|
||||
|
||||
See :issue:`107068`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The configuration option :yaml:`fieldExplanationText` has been deprecated
|
||||
in favor of :yaml:`description`. The new name better reflects its purpose
|
||||
and is easier to understand.
|
||||
|
||||
This affects form element type definitions in
|
||||
:yaml:`prototypes.*.formElementsDefinition.*.formEditor` configuration,
|
||||
including editors, validators, and finishers in any extension.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Using :yaml:`fieldExplanationText` will trigger a PHP deprecation warning.
|
||||
The migration service will automatically convert :yaml:`fieldExplanationText`
|
||||
to :yaml:`description` when form configuration is loaded, ensuring
|
||||
backward compatibility.
|
||||
|
||||
Support for :yaml:`fieldExplanationText` will be removed in TYPO3 v15.0.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
Any installations with extensions that provide custom form element type
|
||||
definitions with the configuration option :yaml:`fieldExplanationText`
|
||||
in their form prototype YAML files (e.g., :file:`Configuration/Form/*.yaml`
|
||||
or :file:`Configuration/Yaml/FormSetup.yaml`).
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Rename any occurrence of :yaml:`fieldExplanationText` to :yaml:`description`
|
||||
in your form element type definition YAML files (typically located in
|
||||
:file:`Configuration/Yaml/FormElements/*.yaml`).
|
||||
|
||||
Example migration:
|
||||
|
||||
.. code-block:: diff
|
||||
|
||||
# After
|
||||
formEditor:
|
||||
editors:
|
||||
200:
|
||||
identifier: placeholder
|
||||
label: Placeholder
|
||||
- fieldExplanationText: Enter the placeholder text
|
||||
+ description: Enter the placeholder text
|
||||
|
||||
.. index:: Backend, ext:form, NotScanned
|
||||
@@ -0,0 +1,35 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-107208-1754387701:
|
||||
|
||||
==================================================
|
||||
Deprecation: #107208 - <f:debug.render> ViewHelper
|
||||
==================================================
|
||||
|
||||
See :issue:`107208`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The `<f:debug.render>` ViewHelper has been deprecated. It was used internally to
|
||||
render Fluid debug output for the admin panel.
|
||||
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Calling the ViewHelper from a template triggers a deprecation warning. The
|
||||
ViewHelper will be removed in TYPO3 v15.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
Projects and extensions that use `<f:debug.render>` in a template.
|
||||
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
A custom ViewHelper can be created that mimics the behavior of the Core ViewHelper.
|
||||
|
||||
.. index:: Fluid, NotScanned, ext:fluid
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-107802-1770827443:
|
||||
|
||||
=======================================================================================================
|
||||
Deprecation: #107802 - Deprecate usage of array in password for authentication in Redis session backend
|
||||
=======================================================================================================
|
||||
|
||||
See :issue:`107802`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
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 via password. With
|
||||
this patch, you can configure the TYPO3 Redis session backend as follows:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: config/system/additional.php
|
||||
|
||||
use TYPO3\CMS\Core\Session\Backend\RedisSessionBackend;
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['session']['BE'] = [
|
||||
'backend' => RedisSessionBackend::class,
|
||||
'options' => [
|
||||
'database' => 0,
|
||||
'hostname' => 'redis',
|
||||
'port' => 6379,
|
||||
'username' => 'redis',
|
||||
'password' => 'redis',
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The "password" configuration option of the Redis session backend is now typed as
|
||||
:php:`array|string`. Setting this configuration option with an array is deprecated
|
||||
and will be removed in 15.0.
|
||||
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
All installations using a Redis session backend and using the `password` configuration
|
||||
option to pass an array with a username and password to it.
|
||||
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Use the configuration options `username` and `password`.
|
||||
|
||||
**Before:**
|
||||
|
||||
.. code-block:: php
|
||||
:caption: config/system/additional.php
|
||||
|
||||
use TYPO3\CMS\Core\Session\Backend\RedisSessionBackend;
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['session']['BE'] = [
|
||||
'backend' => RedisSessionBackend::class,
|
||||
'options' => [
|
||||
'database' => 0,
|
||||
'hostname' => 'redis',
|
||||
'port' => 6379,
|
||||
'username' => 'redis',
|
||||
'password' =>[
|
||||
'user' => 'redis',
|
||||
'pass' => 'redis'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
**After:**
|
||||
|
||||
.. code-block:: php
|
||||
:caption: config/system/additional.php
|
||||
|
||||
use TYPO3\CMS\Core\Session\Backend\RedisSessionBackend;
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['session']['BE'] = [
|
||||
'backend' => RedisSessionBackend::class,
|
||||
'options' => [
|
||||
'database' => 0,
|
||||
'hostname' => 'redis',
|
||||
'port' => 6379,
|
||||
'username' => 'redis',
|
||||
'password' => 'redis',
|
||||
]
|
||||
];
|
||||
|
||||
.. index:: LocalConfiguration, NotScanned, ext:core
|
||||
@@ -0,0 +1,282 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-108345-1774126701:
|
||||
|
||||
====================================================
|
||||
Deprecation: #108345 - Deprecation of ext_emconf.php
|
||||
====================================================
|
||||
|
||||
See :issue:`108345`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
TYPO3 extensions that still ship an `ext_emconf.php` file
|
||||
**and** do not declare future compatibility to omit this file
|
||||
will now trigger a deprecation message during cache warm-up.
|
||||
|
||||
With TYPO3 v15 the `ext_emconf.php` file is no longer evaluated.
|
||||
For TYPO3 classic (non-Composer) mode to keep working, TYPO3 then needs to
|
||||
know the extension version and which `require` and `suggest` entries are not
|
||||
TYPO3 extensions. The extension version and the `providesPackages` definition
|
||||
therefore become mandatory fields in `composer.json`, regardless of whether an
|
||||
`ext_emconf.php` file is still shipped.
|
||||
|
||||
In TYPO3 v14 these fields are not yet mandatory, because the deprecated
|
||||
`ext_emconf.php` can still provide this information. A deprecation message is
|
||||
triggered only when the `version` or `providesPackages` field is missing, so
|
||||
that an extension can stay compatible with both TYPO3 v14 and v15 at the same
|
||||
time.
|
||||
|
||||
To avoid this deprecation message, the extension must provide
|
||||
the required package metadata in `composer.json`.
|
||||
|
||||
At minimum, this includes the extension version and the
|
||||
`providesPackages` definition:
|
||||
|
||||
.. code-block:: json
|
||||
:caption: composer.json for an extension providing Composer packages
|
||||
|
||||
{
|
||||
"name": "vendor/example",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"require": {
|
||||
"typo3/cms-core": "^14.2",
|
||||
"vendor/other-example": "*",
|
||||
"symfony/dotenv": "^8.0"
|
||||
},
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example_extension",
|
||||
"version": "1.0.0",
|
||||
"Package": {
|
||||
"providesPackages": {
|
||||
"symfony/dotenv": "Resources/Private/Php/ComposerVendor"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: json
|
||||
:caption: composer.json for an extension not providing Composer packages
|
||||
|
||||
{
|
||||
"name": "vendor/example2",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"require": {
|
||||
"typo3/cms-core": "^14.2"
|
||||
},
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example2_extension",
|
||||
"version": "1.0.0",
|
||||
"Package": {
|
||||
"providesPackages": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
For compatibility with TYPO3 classic mode, third-party extensions
|
||||
must set the exact extension version in `extra.typo3/cms.version`
|
||||
or in the top level `version` field of :file:`composer.json`.
|
||||
This version must match the version previously
|
||||
defined in :file:`ext_emconf.php` and the released Git tag.
|
||||
|
||||
Fixture extensions used in tests can set any version number, for example `1.0.0`,
|
||||
but a version number must still be provided to avoid deprecation messages.
|
||||
|
||||
During testing, the version number is not evaluated.
|
||||
|
||||
TYPO3 Core extensions may omit the version number
|
||||
in :file:`composer.json` because their version number is derived from
|
||||
:php-short:`\TYPO3\CMS\Core\Information\Typo3Version`.
|
||||
|
||||
State migration
|
||||
---------------
|
||||
|
||||
The former `state` field from `ext_emconf.php` is deprecated as a source of
|
||||
extension metadata and should be set in `composer.json` using
|
||||
dedicated metadata instead.
|
||||
|
||||
Supported stability values should be expressed via the version string:
|
||||
|
||||
.. code-block:: json
|
||||
:caption: composer.json using version stability suffixes
|
||||
|
||||
{
|
||||
"name": "vendor/example",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example_extension",
|
||||
"version": "1.2.3-beta2",
|
||||
"Package": {
|
||||
"providesPackages": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Supported Composer stability values are:
|
||||
|
||||
* `dev`
|
||||
* `alpha`
|
||||
* `beta`
|
||||
* `RC`
|
||||
* `stable`
|
||||
|
||||
State values that are not in the list of supported Composer stability values
|
||||
can be expressed as build metadata:
|
||||
|
||||
.. code-block:: json
|
||||
:caption: composer.json using build metadata for custom state labels
|
||||
|
||||
{
|
||||
"name": "vendor/example",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example_extension",
|
||||
"version": "1.0.0+obsolete",
|
||||
"Package": {
|
||||
"providesPackages": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
In this example, `obsolete` is preserved as build metadata and can still be displayed
|
||||
in the TYPO3 Extension Manager.
|
||||
|
||||
The former `state = excludeFromUpdates` value should now be expressed via
|
||||
a dedicated boolean flag:
|
||||
|
||||
.. code-block:: json
|
||||
:caption: composer.json marking an extension as excluded from updates
|
||||
|
||||
{
|
||||
"name": "vendor/example",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example_extension",
|
||||
"version": "1.0.0",
|
||||
"exclude-from-updates": true,
|
||||
"Package": {
|
||||
"providesPackages": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PHP constraints
|
||||
---------------
|
||||
|
||||
If an extension declares a PHP version dependency, it should be in
|
||||
the `require` section of :file:`composer.json`:
|
||||
|
||||
.. code-block:: json
|
||||
:caption: composer.json defining a PHP version constraint
|
||||
|
||||
{
|
||||
"name": "vendor/example",
|
||||
"version": "1.0.0",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"require": {
|
||||
"typo3/cms-core": "^14.2",
|
||||
"php": "^8.2"
|
||||
},
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example_extension",
|
||||
"Package": {
|
||||
"providesPackages": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The PHP dependency remains relevant for metadata and compatibility checks
|
||||
in TYPO3 classic mode, but it is not used for extension dependency ordering.
|
||||
|
||||
If an extension provides regular Composer packages itself in TYPO3 classic mode,
|
||||
these packages must be declared in
|
||||
`extra.typo3/cms.Package.providesPackages`.
|
||||
|
||||
Packages that are already shipped by TYPO3 or already provided by another loaded
|
||||
extension do not need to be repeated there.
|
||||
|
||||
Entries in `providesPackages` may also associate a provided package with a
|
||||
relative path to a Composer vendor directory inside the extension. If that
|
||||
directory contains a Composer-generated `autoload.php`, TYPO3 includes it
|
||||
early during bootstrap.
|
||||
|
||||
If an extension does not provide any regular Composer packages itself,
|
||||
`providesPackages` must still be present and set to an empty object
|
||||
to avoid deprecation messages and to declare future compatibility
|
||||
with TYPO3 classic mode.
|
||||
|
||||
If strict :file:`composer.json` validation is required and the extension is published
|
||||
to Packagist where setting the top level `version` field is not recommended,
|
||||
it is recommended to set the version via `extra.typo3/cms.version`.
|
||||
|
||||
If the `version` field is set anyway, it is recommended to omit `extra.typo3/cms.version`
|
||||
to avoid redundant data points.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
There is no impact on Composer-based TYPO3 installations.
|
||||
|
||||
TYPO3 classic installations will trigger a deprecation message
|
||||
for extensions that ship a :file:`ext_emconf.php` and have not defined
|
||||
the required metadata in :file:`composer.json`.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
TYPO3 classic installations are affected if they use extensions that:
|
||||
|
||||
* still ship :file:`ext_emconf.php`
|
||||
* do not define a `"version"` field or `extra.typo3/cms.version`
|
||||
* or do not define `extra.typo3/cms.Package.providesPackages`
|
||||
at all, even as an empty object
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Extension authors should move extension metadata from :file:`ext_emconf.php`
|
||||
to :file:`composer.json`.
|
||||
|
||||
This includes:
|
||||
|
||||
* the extension version via `"version"` or `extra.typo3/cms.version`
|
||||
* `providesPackages` via `extra.typo3/cms.Package.providesPackages`,
|
||||
using it for packages provided by the extension itself; packages already
|
||||
shipped by TYPO3 or already provided by another extension do not need
|
||||
to be repeated
|
||||
* optional autoload paths for self-provided Composer packages via
|
||||
`extra.typo3/cms.Package.providesPackages`, pointing to a Composer
|
||||
vendor directory whose `autoload.php` can be included early
|
||||
* supported stability via version suffixes such as `-dev`, `-alpha1`,
|
||||
`-beta2`, or `-RC3`
|
||||
* custom former state labels via build metadata such as `+obsolete`
|
||||
* update exclusion via `extra.typo3/cms.exclude-from-updates`
|
||||
* PHP constraints via the :file:`require.php` entry
|
||||
|
||||
For the time being, :file:`ext_emconf.php` may still need to be kept for
|
||||
third-party tooling such as TYPO3 TER or Tailor. However, once the
|
||||
required metadata is correctly defined in :file:`composer.json`,
|
||||
TYPO3 will no longer evaluate :file:`ext_emconf.php`.
|
||||
|
||||
.. index:: ext:core, NotScanned
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-108557-1768610680:
|
||||
|
||||
===================================================================
|
||||
Deprecation: #108557 - TCA option allowedRecordTypes for Page Types
|
||||
===================================================================
|
||||
|
||||
See :issue:`108557`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The following methods of :php:`TYPO3\CMS\Core\DataHandling\PageDoktypeRegistry`
|
||||
have been marked as deprecated:
|
||||
|
||||
* :php:`PageDoktypeRegistry->add()`
|
||||
* :php:`PageDoktypeRegistry->addAllowedRecordTypes()`
|
||||
* :php:`PageDoktypeRegistry->doesDoktypeOnlyAllowSpecifiedRecordTypes()`
|
||||
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Calling any of the above methods will trigger a deprecation-level log
|
||||
entry and result in a fatal PHP error in TYPO3 v15.0.
|
||||
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
All installations using the :php-short:`TYPO3\CMS\Core\DataHandling\PageDoktypeRegistry`
|
||||
to configure page types using the :php:`add()` method. Also, in some rare cases, using the
|
||||
methods :php:`addAllowedRecordTypes()` or
|
||||
:php:`doesDoktypeOnlyAllowSpecifiedRecordTypes()`.
|
||||
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
A new TCA option is introduced to configure allowed record types for pages:
|
||||
|
||||
Before:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/ext_tables.php
|
||||
|
||||
use TYPO3\CMS\Core\DataHandling\PageDoktypeRegistry;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
$dokTypeRegistry = GeneralUtility::makeInstance(PageDoktypeRegistry::class);
|
||||
$dokTypeRegistry->add(
|
||||
116,
|
||||
[
|
||||
'allowedTables' => '*',
|
||||
],
|
||||
);
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Configuration/TCA/Overrides/pages.php
|
||||
|
||||
$GLOBALS['TCA']['pages']['types']['116']['allowedRecordTypes'] = ['*'];
|
||||
|
||||
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 :php:`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 by TCA types. The former usage of
|
||||
:php-short:`TYPO3\CMS\Core\DataHandling\PageDoktypeRegistry` was only useful
|
||||
to define allowed record types different to the default.
|
||||
|
||||
The option `allowedRecordType` is only evaluated in the "pages" table.
|
||||
|
||||
.. index:: TCA, PartiallyScanned, ext:core
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-108568-1734962478:
|
||||
|
||||
===========================================================================================
|
||||
Deprecation: #108568 - BackendUserAuthentication::recordEditAccessInternals() and $errorMsg
|
||||
===========================================================================================
|
||||
|
||||
See :issue:`108568`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The method :php:`BackendUserAuthentication::recordEditAccessInternals()`
|
||||
and the property :php:`BackendUserAuthentication::$errorMsg` of class
|
||||
:php:`\TYPO3\CMS\Core\Authentication\BackendUserAuthentication`
|
||||
have been deprecated.
|
||||
|
||||
They represented an anti-pattern in which 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 :php:`checkRecordEditAccess()` has been introduced. It returns
|
||||
an :php:`\TYPO3\CMS\Core\Authentication\AccessCheckResult` value object
|
||||
containing both the access decision and any error messages.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Calling the deprecated method :php:`recordEditAccessInternals()` or accessing
|
||||
the deprecated property :php:`$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
|
||||
:php:`recordEditAccessInternals()` or access the
|
||||
:php:`$errorMsg` property.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Replace calls to :php:`recordEditAccessInternals()` with
|
||||
:php:`checkRecordEditAccess()`. The new method returns a
|
||||
:php:`TYPO3\CMS\Core\Authentication\AccessCheckResult` object with two public
|
||||
properties:
|
||||
|
||||
* :php:`isAllowed` - Boolean indicating whether access is granted
|
||||
* :php:`errorMessage` - String containing the error message. It is empty if
|
||||
access is granted
|
||||
|
||||
Before
|
||||
------
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\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;
|
||||
}
|
||||
|
||||
After
|
||||
-----
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
|
||||
$backendUser = $this->getBackendUser();
|
||||
$accessResult = $backendUser->checkRecordEditAccess($table, $record);
|
||||
if ($accessResult->isAllowed) {
|
||||
// Access granted
|
||||
} else {
|
||||
// Access denied
|
||||
$errorMessage = $accessResult->errorMessage;
|
||||
}
|
||||
|
||||
.. index:: PHP-API, FullyScanned, ext:core
|
||||
@@ -0,0 +1,141 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-108653-1741600000:
|
||||
|
||||
==========================================================
|
||||
Deprecation: #108653 - Form file-based storage deprecated
|
||||
==========================================================
|
||||
|
||||
See :issue:`108653`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
File-based form storage (YAML files stored via file mounts) in
|
||||
`EXT:form` has been deprecated in favor of database storage.
|
||||
|
||||
Since TYPO3 v14.2, the `EXT:form` module stores form definitions as
|
||||
records in the :sql:`form_definition` database table. This approach provides
|
||||
simpler setup, integrates better with the TYPO3 permission system, and
|
||||
eliminates the need for file mounts and file system configuration.
|
||||
|
||||
The following components are deprecated and will be removed in TYPO3 v15.0:
|
||||
|
||||
* :php:`\TYPO3\CMS\Form\Storage\FileMountStorageAdapter` – the storage
|
||||
adapter for FAL file mount-based form persistence
|
||||
* The YAML configuration option :yaml:`persistenceManager.allowedFileMounts`
|
||||
– configuring allowed file mounts for form storage
|
||||
|
||||
See :ref:`feature-108653-1767199420` for the new database storage approach.
|
||||
|
||||
An upgrade wizard as well as a CLI command are available to migrate existing file-based form definitions
|
||||
to the database: :guilabel:`System > Upgrade > Upgrade Wizard > Migrate file-based forms to database storage`.
|
||||
|
||||
.. note::
|
||||
|
||||
YAML form files provided within extension directories
|
||||
(:yaml:`persistenceManager.allowedExtensionPaths`) still work as before
|
||||
and are not affected by this deprecation until further concepts are evaluated.
|
||||
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
File-based form storage will continue to work without any functional changes
|
||||
during the deprecation period. However, it will be removed in TYPO3 v15.0.
|
||||
|
||||
An upgrade wizard is available to check whether file-based forms exist
|
||||
and to migrate them to database storage. Run the wizard regularly to verify
|
||||
your migration status.
|
||||
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
All installations that:
|
||||
|
||||
* Store form definitions as YAML files in file mounts
|
||||
(for example, :file:`1:/form_definitions/`)
|
||||
* Use the :yaml:`persistenceManager.allowedFileMounts` configuration
|
||||
option in their form setup YAML with one or more mount points configured
|
||||
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
.. warning::
|
||||
|
||||
If your installation uses file mount–based permission separation (i.e.,
|
||||
different backend user groups have access to different form storage
|
||||
folders to isolate which forms they can see and edit), an equivalent
|
||||
access control mechanism for database-stored forms is **not yet available**.
|
||||
|
||||
In this case, it is recommended to **not migrate at this time**. The
|
||||
file-based storage will continue to work without functional changes during
|
||||
the entire deprecation period. A dedicated permission feature for
|
||||
database storage is planned for a future release.
|
||||
|
||||
|
||||
1. Run the upgrade wizard :guilabel:`Migrate file-based forms to database storage`
|
||||
in the :guilabel:`System > Upgrade` module. This wizard:
|
||||
|
||||
* Copies all file-based form definitions into the :sql:`form_definition`
|
||||
database table
|
||||
* Updates all :sql:`tt_content` FlexForm references
|
||||
(`persistenceIdentifier`) to point to the new database records
|
||||
* Deletes the original YAML files after successful migration
|
||||
|
||||
If the :sql:`form_definition` table does not exist yet, run
|
||||
:guilabel:`System > Maintenance > Analyze Database` first.
|
||||
|
||||
.. important::
|
||||
|
||||
The upgrade wizard only updates references in :sql:`tt_content`
|
||||
(CType `form_formframework`). If your installation stores form
|
||||
persistence identifiers in **custom database tables** or FlexForm
|
||||
fields outside :sql:`tt_content` (e.g., through third-party
|
||||
extensions), these references are **not updated automatically** and
|
||||
must be migrated manually.
|
||||
|
||||
2. After verifying that all forms work correctly from the database, remove
|
||||
the :yaml:`allowedFileMounts` configuration from your YAML setup:
|
||||
|
||||
**Before (deprecated):**
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: EXT:my_extension/Configuration/Yaml/FormSetup.yaml
|
||||
|
||||
persistenceManager:
|
||||
allowedFileMounts:
|
||||
10: '1:/form_definitions/'
|
||||
|
||||
**After:**
|
||||
|
||||
To explicitly disable file mount storage, set :yaml:`allowedFileMounts`
|
||||
to null (:yaml:`~`):
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: EXT:my_extension/Configuration/Yaml/FormSetup.yaml
|
||||
|
||||
persistenceManager:
|
||||
allowedFileMounts: ~
|
||||
|
||||
3. Optionally, if the upgrade wizard did not delete the YAML files (e.g.,
|
||||
due to file permission issues), delete them manually from the file system
|
||||
after confirming that the migration was successful.
|
||||
|
||||
Alternatively, the CLI command :bash:`form:definition:transfer` can be used
|
||||
to transfer forms between storage types:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Transfer all file mount forms to database
|
||||
bin/typo3 form:definition:transfer --source=filemount --target=database
|
||||
|
||||
# Move (transfer + delete source) in one step
|
||||
bin/typo3 form:definition:transfer --source=filemount --target=database --move
|
||||
|
||||
# Preview without changes
|
||||
bin/typo3 form:definition:transfer --source=filemount --target=database --dry-run
|
||||
|
||||
.. index:: YAML, NotScanned, ext:form
|
||||
@@ -0,0 +1,76 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-108761-1769281290:
|
||||
|
||||
==============================================================
|
||||
Deprecation: #108761 - BackendUtility TSconfig-related methods
|
||||
==============================================================
|
||||
|
||||
See :issue:`108761`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The following methods in :php:`\TYPO3\CMS\Backend\Utility\BackendUtility` have
|
||||
been deprecated:
|
||||
|
||||
* :php:`getTCEFORM_TSconfig()`
|
||||
* :php:`getTSCpidCached()`
|
||||
* :php:`getTSCpid()`
|
||||
|
||||
A new method :php:`BackendUtility::getRealPageId()` has been introduced. It
|
||||
returns the real page ID of a given record. Unlike the previous methods which
|
||||
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
|
||||
:php:`null` if the page cannot be determined.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Calling any of the deprecated methods triggers 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 call any of the deprecated methods.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
getTCEFORM_TSconfig()
|
||||
---------------------
|
||||
|
||||
This method has been moved to :php:`FormEngineUtility`. If you need TSconfig
|
||||
for TCEFORM, it is recommended that you 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 :php:`getRealPageId()` method returns only the real page ID.
|
||||
|
||||
Before:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
// getTSCpidCached returned [$tscPid, $realPid]
|
||||
[$tscPid, $realPid] = BackendUtility::getTSCpidCached($table, $uid, $pid);
|
||||
|
||||
// getTSCpid returned the same structure
|
||||
[$tscPid, $realPid] = BackendUtility::getTSCpid($table, $uid, $pid);
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
// 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);
|
||||
|
||||
.. index:: PHP-API, FullyScanned, ext:backend
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-108810-1738253894:
|
||||
|
||||
==================================================================
|
||||
Deprecation: #108810 - BackendUtility localization-related methods
|
||||
==================================================================
|
||||
|
||||
See :issue:`108810`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The following methods in :php:`\TYPO3\CMS\Backend\Utility\BackendUtility` have
|
||||
been deprecated in favor of new methods in
|
||||
:php:`\TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository`:
|
||||
|
||||
* :php:`BackendUtility::getRecordLocalization()` - use
|
||||
:php:`LocalizationRepository::getRecordTranslation()` instead
|
||||
* :php:`BackendUtility::getExistingPageTranslations()` - use
|
||||
:php:`LocalizationRepository::getPageTranslations()` instead
|
||||
* :php:`BackendUtility::translationCount()` - use
|
||||
:php:`LocalizationRepository::getRecordTranslations()` instead
|
||||
|
||||
See :ref:`feature-108799-1738094060` for details of the new methods.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Calling any of the deprecated methods triggers 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 :php-short:`\TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository`
|
||||
and use the new methods. The new methods return
|
||||
:php:`\TYPO3\CMS\Core\Domain\RawRecord` objects instead of plain arrays.
|
||||
|
||||
getRecordLocalization()
|
||||
-----------------------
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
getExistingPageTranslations()
|
||||
-----------------------------
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
|
||||
// Before
|
||||
$pageTranslations = BackendUtility::getExistingPageTranslations($pageUid);
|
||||
|
||||
// After
|
||||
// Returns an array of RawRecord objects indexed by language ID
|
||||
$pageTranslations = $this->localizationRepository->getPageTranslations($pageUid);
|
||||
|
||||
translationCount()
|
||||
------------------
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
|
||||
// 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);
|
||||
|
||||
.. index:: PHP-API, FullyScanned, ext:backend
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-108843-1738600000:
|
||||
|
||||
==========================================================================
|
||||
Deprecation: #108843 - ExtensionManagementUtility::addFieldsToUserSettings
|
||||
==========================================================================
|
||||
|
||||
See :issue:`108843`
|
||||
See :issue:`108832`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The method
|
||||
:php:`\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addFieldsToUserSettings()`
|
||||
has been deprecated in favor of the new :php:`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 and second, calling
|
||||
:php:`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
|
||||
:php:`ExtensionManagementUtility::addFieldsToUserSettings()` or directly
|
||||
modify :php:`$GLOBALS['TYPO3_USER_SETTINGS']` to add custom fields to the
|
||||
backend user profile settings.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Replace the two-step approach with the new :php:`addUserSetting()` method.
|
||||
Note that the new method uses TCA-style configuration and should be called
|
||||
from :file:`Configuration/TCA/Overrides/be_users.php` instead of
|
||||
:file:`ext_tables.php`.
|
||||
|
||||
Before
|
||||
~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
// In ext_tables.php
|
||||
$GLOBALS['TYPO3_USER_SETTINGS']['columns']['myCustomSetting'] = [
|
||||
'type' => 'check',
|
||||
'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang.xlf:myCustomSetting',
|
||||
];
|
||||
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addFieldsToUserSettings(
|
||||
'myCustomSetting',
|
||||
'after:emailMeAtLogin'
|
||||
);
|
||||
|
||||
After
|
||||
~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
// In Configuration/TCA/Overrides/be_users.php
|
||||
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addUserSetting(
|
||||
'myCustomSetting',
|
||||
[
|
||||
'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang.xlf:myCustomSetting',
|
||||
'config' => [
|
||||
'type' => 'check',
|
||||
'renderType' => 'checkboxToggle',
|
||||
],
|
||||
],
|
||||
'after:emailMeAtLogin'
|
||||
);
|
||||
|
||||
Field type mapping
|
||||
------------------
|
||||
|
||||
When migrating, use the following type mappings:
|
||||
|
||||
============== ==========================================================
|
||||
Legacy type TCA config
|
||||
============== ==========================================================
|
||||
text :php:`['type' => 'input']`
|
||||
email :php:`['type' => 'email']`
|
||||
number :php:`['type' => 'number']`
|
||||
password :php:`['type' => 'password']`
|
||||
check :php:`['type' => 'check', 'renderType' => 'checkboxToggle']`
|
||||
select :php:`['type' => 'select', 'renderType' => 'selectSingle']`
|
||||
language :php:`['type' => 'language']`
|
||||
============== ==========================================================
|
||||
|
||||
.. index:: PHP-API, FullyScanned, ext:core
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-108963-1770907005:
|
||||
|
||||
==========================================================================
|
||||
Deprecation: #108963 - Deprecate `PageRenderer->addInlineLanguageDomain()`
|
||||
==========================================================================
|
||||
|
||||
See :issue:`108963`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
:php:`\TYPO3\CMS\Core\Page\PageRenderer->addInlineLanguageDomain()` has been
|
||||
deprecated in favor of importing JavaScript modules, as introduced in
|
||||
:ref:`feature-108941-1770902109`.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Extension developers can now use labels in JavaScript components without
|
||||
requiring labels to be preloaded globally or per module. This reduces the risk
|
||||
of missing labels and simplifies developer workflows.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
The deprecated method was introduced in TYPO3 v14.1. This means that only
|
||||
installations that use :php:`addInlineLanguageDomain()` in TYPO3 v14.1 or later
|
||||
are affected.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
The call to :php:`PageRenderer::addInlineLanguageDomain()` can be removed. In
|
||||
the JavaScript code, add a module import that imports from the
|
||||
:js:`'~labels/'` prefix.
|
||||
|
||||
Before:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$pageRenderer->addInlineLanguageDomain('core.bookmarks');
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
import { html } from 'lit';
|
||||
import { lll } from '@typo3/core/lit-helper.js';
|
||||
|
||||
html`<p>{lll('core.bookmarks:groupType.global')}</p>`
|
||||
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
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>`
|
||||
|
||||
.. index:: Backend, JavaScript, FullyScanned, ext:backend
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109027-1771514240:
|
||||
|
||||
===============================================================================
|
||||
Deprecation: #109027 - Move `language:update` command and events to `EXT:core`
|
||||
===============================================================================
|
||||
|
||||
See :issue:`109027`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The `language:update` CLI command and related
|
||||
:php:`\TYPO3\CMS\Install\Service\LanguagePackService` have been moved from
|
||||
`EXT:install` to `EXT:core`, allowing installations to update language packs
|
||||
without `EXT:install` having to be installed.
|
||||
|
||||
Since TYPO3 v13 it has been possible to run TYPO3 without `EXT:install` in
|
||||
Composer-based installations. However, the `language:update` command still
|
||||
required `EXT:install`, which was impractical for deployments that needed to
|
||||
update language packs.
|
||||
|
||||
The following classes have been moved, and their old class names deprecated:
|
||||
|
||||
* :php:`\TYPO3\CMS\Install\Command\LanguagePackCommand` is now
|
||||
:php:`\TYPO3\CMS\Core\Command\UpdateLanguagePackCommand`
|
||||
* :php:`\TYPO3\CMS\Install\Service\Event\ModifyLanguagePackRemoteBaseUrlEvent`
|
||||
is now :php:`\TYPO3\CMS\Core\Localization\Event\ModifyLanguagePackRemoteBaseUrlEvent`
|
||||
* :php:`\TYPO3\CMS\Install\Service\Event\ModifyLanguagePacksEvent` is now
|
||||
:php:`\TYPO3\CMS\Core\Localization\Event\ModifyLanguagePacksEvent`
|
||||
|
||||
The old class names are registered as aliases via
|
||||
:php-short:`TYPO3\ClassAliasLoader\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 usage 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 :php:`use` statements:
|
||||
|
||||
.. code-block:: diff
|
||||
:caption: EXT:my_extension/Classes/EventListener/MyEventListener.php
|
||||
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyVendor\MyExtension\EventListener;
|
||||
|
||||
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||
-use TYPO3\CMS\Install\Service\Event\ModifyLanguagePacksEvent;
|
||||
+use TYPO3\CMS\Core\Localization\Event\ModifyLanguagePacksEvent;
|
||||
|
||||
final class MyEventListener
|
||||
{
|
||||
#[AsEventListener(
|
||||
identifier: 'my-extension/modify-language-packs',
|
||||
)]
|
||||
public function __invoke(
|
||||
ModifyLanguagePacksEvent $event,
|
||||
): void {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: diff
|
||||
:caption: EXT:my_extension/Classes/EventListener/MyOtherEventListener.php
|
||||
|
||||
<?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 {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
.. 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 :php:`use` statements when dropping TYPO3 v13 support.
|
||||
|
||||
.. index:: CLI, PHP-API, FullyScanned, ext:install
|
||||
@@ -0,0 +1,71 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109029-1771804800:
|
||||
|
||||
=========================================================
|
||||
Deprecation: #109029 - FormEngine ``doSave`` hidden field
|
||||
=========================================================
|
||||
|
||||
See :issue:`109029`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :html:`<input type="hidden" name="doSave">` field in FormEngine was a
|
||||
legacy mechanism where JavaScript set the field value to `1` to
|
||||
signal to 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 operation should be performed. The field is **no longer
|
||||
evaluated internally**.
|
||||
|
||||
For backward compatibility, the :html:`doSave` field is still appended to the
|
||||
form on programmatic saves in TYPO3 v14, but this behavior is deprecated and
|
||||
will be removed in TYPO3 v15.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Third-party code reading :php:`$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
|
||||
:html:`doSave` POST field to determine whether incoming form data should be
|
||||
persisted.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Replace any checking of the :html:`doSave` POST field with a check of
|
||||
all native submit action fields that FormEngine sends as part of normal
|
||||
form submission.
|
||||
|
||||
Before:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$doSave = (bool)($parsedBody['doSave'] ?? false);
|
||||
if ($doSave) {
|
||||
// process data
|
||||
}
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$isSaveAction = !empty($parsedBody['_savedok'])
|
||||
|| !empty($parsedBody['_saveandclosedok'])
|
||||
|| !empty($parsedBody['_savedokview'])
|
||||
|| !empty($parsedBody['_savedoknew']);
|
||||
if ($isSaveAction) {
|
||||
// process data
|
||||
}
|
||||
|
||||
.. index:: Backend, JavaScript, NotScanned, ext:backend
|
||||
@@ -0,0 +1,60 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109102-1740480000:
|
||||
|
||||
==============================================================
|
||||
Deprecation: #109102 - FormEngine "additionalHiddenFields" key
|
||||
==============================================================
|
||||
|
||||
See :issue:`109102`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The `additionalHiddenFields` result array key in FormEngine was a legacy
|
||||
mechanism that stored hidden :html:`<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.
|
||||
|
||||
The following have been deprecated:
|
||||
|
||||
* The `additionalHiddenFields` key in FormEngine result arrays
|
||||
* :php:`FormResult::$hiddenFieldsHtml`
|
||||
* :php:`FormResultCollection::getHiddenFieldsHtml()`
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Third-party FormEngine elements that add entries to
|
||||
:php:`$resultArray['additionalHiddenFields']` will trigger a PHP
|
||||
:php:`E_USER_DEPRECATED` level error when their result is merged via
|
||||
:php:`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.
|
||||
|
||||
Before:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$resultArray = $this->initializeResultArray();
|
||||
$resultArray['additionalHiddenFields'][] =
|
||||
'<input type="hidden" name="myField" value="myValue" />';
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$resultArray = $this->initializeResultArray();
|
||||
$resultArray['html'] .=
|
||||
'<input type="hidden" name="myField" value="myValue" />';
|
||||
|
||||
.. index:: Backend, PHP-API, NotScanned, ext:backend
|
||||
@@ -0,0 +1,76 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109152-1741600000:
|
||||
|
||||
==============================================
|
||||
Deprecation: #109152 - Form DatePicker element
|
||||
==============================================
|
||||
|
||||
See :issue:`109152`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :yaml:`DatePicker` form element type and its associated
|
||||
:php:`DatePickerViewHelper` and :php:`TimePickerViewHelper` have been
|
||||
deprecated as part of removing jQuery dependency from
|
||||
:composer:`typo3/cms-form`. The :yaml:`Date` form element type serves as a
|
||||
replacement and uses native HTML5 :html:`<input type="date">` without needing
|
||||
a JavaScript library.
|
||||
|
||||
The following components are deprecated:
|
||||
|
||||
* :php:`TYPO3\CMS\Form\Domain\Model\FormElements\DatePicker`
|
||||
* :php:`TYPO3\CMS\Form\ViewHelpers\Form\DatePickerViewHelper`
|
||||
* :php:`TYPO3\CMS\Form\ViewHelpers\Form\TimePickerViewHelper`
|
||||
* The :file:`EXT:form/Resources/Public/JavaScript/frontend/date-picker.js`
|
||||
jQuery initialization script
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Using the :yaml:`DatePicker` form element type in a form definition will
|
||||
trigger a PHP :php:`E_USER_DEPRECATED` level error at runtime. The element,
|
||||
its ViewHelpers, and the JavaScript file will be removed in TYPO3 v15.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
All installations that use the :yaml:`DatePicker` form element type in form
|
||||
definitions created with the TYPO3 Form Framework.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Replace :yaml:`DatePicker` with the :yaml:`Date` form element type in your
|
||||
form definitions.
|
||||
|
||||
Before:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
type: DatePicker
|
||||
identifier: date-1
|
||||
label: 'Pick a date'
|
||||
properties:
|
||||
dateFormat: Y-m-d
|
||||
enableDatePicker: true
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
type: Date
|
||||
identifier: date-1
|
||||
label: 'Pick a date'
|
||||
|
||||
The :yaml:`Date` element uses a native HTML5 date input, which does not
|
||||
require jQuery or additional JavaScript. The
|
||||
:yaml:`dateFormat` and :yaml:`enableDatePicker` properties are no longer
|
||||
needed because the browser handles date formatting and the picker natively.
|
||||
|
||||
Alternatively, if the native HTML5 date input does not meet your
|
||||
requirements, you can create a custom form element with a date picker
|
||||
JavaScript library of your choice.
|
||||
|
||||
.. index:: Frontend, YAML, NotScanned, ext:form
|
||||
@@ -0,0 +1,78 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109171-1741254000:
|
||||
|
||||
===========================================
|
||||
Deprecation: #109171 - Bootstrap tab events
|
||||
===========================================
|
||||
|
||||
See :issue:`109171`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Bootstrap's tab JavaScript has been replaced with a custom implementation
|
||||
tailored to TYPO3. The Bootstrap tab events :js:`show.bs.tab` and
|
||||
:js:`shown.bs.tab` are now deprecated and will be removed in TYPO3 v15.
|
||||
|
||||
The following new custom events are available as replacements:
|
||||
|
||||
* :js:`typo3:tab:show` — dispatched before a tab switch, cancelable via
|
||||
:js:`event.preventDefault()`
|
||||
* :js:`typo3:tab:shown` — dispatched after a tab switch
|
||||
|
||||
Both events bubble from the tab button and carry a
|
||||
:js:`detail.relatedTarget` property that points to the previously active tab
|
||||
button or :js:`null`.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Listening for :js:`show.bs.tab` or :js:`shown.bs.tab` events will continue to
|
||||
work in TYPO3 v14 but will stop working in TYPO3 v15, when the backward-
|
||||
compatibility events will be removed.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
All extensions that listen to :js:`show.bs.tab` or :js:`shown.bs.tab` events
|
||||
on tab buttons are affected.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Replace Bootstrap tab event listeners with the new TYPO3 tab events.
|
||||
|
||||
Before:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
document.addEventListener('show.bs.tab', (e) => {
|
||||
console.log(
|
||||
'Tab is about to show',
|
||||
e.target,
|
||||
e.detail.relatedTarget
|
||||
);
|
||||
});
|
||||
|
||||
document.addEventListener('shown.bs.tab', (e) => {
|
||||
console.log(
|
||||
'Tab was shown',
|
||||
e.target,
|
||||
e.detail.relatedTarget
|
||||
);
|
||||
});
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
document.addEventListener('typo3:tab:show', (e) => {
|
||||
console.log('Tab is about to show', e.target, e.detail.relatedTarget);
|
||||
});
|
||||
|
||||
document.addEventListener('typo3:tab:shown', (e) => {
|
||||
console.log('Tab was shown', e.target, e.detail.relatedTarget);
|
||||
});
|
||||
|
||||
.. index:: Backend, JavaScript, NotScanned, ext:backend
|
||||
@@ -0,0 +1,67 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109192-1741560000:
|
||||
|
||||
=====================================================
|
||||
Deprecation: #109192 - FormEngine OuterWrapContainer
|
||||
=====================================================
|
||||
|
||||
See :issue:`109192`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :php:`\TYPO3\CMS\Backend\Form\Container\OuterWrapContainer` FormEngine
|
||||
container has been deprecated in favor
|
||||
of the new :php:`\TYPO3\CMS\Backend\Form\Container\FormWrapContainer`.
|
||||
The old container rendered record
|
||||
headers, type icons, and record identity information inside FormEngine, which
|
||||
forced controllers to hide redundant elements via CSS hacks.
|
||||
|
||||
The new :php-short:`\TYPO3\CMS\Backend\Form\Container\FormWrapContainer`
|
||||
only handles form wrapping (description,
|
||||
read-only notice, field information, field wizards, and child HTML).
|
||||
Rendering record headers and identity information is now the responsibility of
|
||||
the controllers themselves.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Using the `outerWrapContainer` render type will trigger a PHP
|
||||
:php:`E_USER_DEPRECATED` level error. The container will still work as before
|
||||
during the deprecation period.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
Installations with custom controllers or FormEngine integrations that set
|
||||
:php:`$formData['renderType'] = 'outerWrapContainer'`.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Replace the render type `outerWrapContainer` with
|
||||
`formWrapContainer`.
|
||||
|
||||
Before:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$formData['renderType'] = 'outerWrapContainer';
|
||||
$formResult = $this->nodeFactory->create($formData)->render();
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$formData['renderType'] = 'formWrapContainer';
|
||||
$formResult = $this->nodeFactory->create($formData)->render();
|
||||
|
||||
Note that :php-short:`\TYPO3\CMS\Backend\Form\Container\FormWrapContainer`
|
||||
no longer renders the record heading
|
||||
(:html:`<h1>`) or the record identity footer (icon, table title, uid). If
|
||||
your controller relied on these being rendered by
|
||||
:php-short:`\TYPO3\CMS\Backend\Form\Container\OuterWrapContainer`, you need to
|
||||
render them in your controller code.
|
||||
|
||||
.. index:: Backend, PHP-API, NotScanned, ext:backend
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109196-1742122800:
|
||||
|
||||
==============================================================================
|
||||
Deprecation: #109196 - Deprecate doktypesToShowInNewPageDragArea user TSconfig
|
||||
==============================================================================
|
||||
|
||||
See :issue:`109196`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The user TSconfig option
|
||||
:tsconfig:`options.pageTree.doktypesToShowInNewPageDragArea`
|
||||
has been deprecated and will be removed in TYPO3 v15.0.
|
||||
|
||||
The page tree toolbar submenu now automatically determines available doktypes
|
||||
based on the user's group permissions. Manual TSconfig configuration is no
|
||||
longer needed.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Using the deprecated user TSconfig option triggers a deprecation-level log
|
||||
entry and will stop working in TYPO3 v15.0.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
TYPO3 installations that set
|
||||
:tsconfig:`options.pageTree.doktypesToShowInNewPageDragArea` in their user
|
||||
TSconfig.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Remove the :tsconfig:`options.pageTree.doktypesToShowInNewPageDragArea` option
|
||||
from your user TSconfig. The page tree toolbar will then display all
|
||||
doktypes that the current backend user is allowed to create based on their
|
||||
group permissions.
|
||||
|
||||
.. index:: Backend, TSConfig, NotScanned, ext:backend
|
||||
@@ -0,0 +1,89 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109230-1773404000:
|
||||
|
||||
=========================================
|
||||
Deprecation: #109230 - FormResultCompiler
|
||||
=========================================
|
||||
|
||||
See :issue:`109230`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The class :php:`TYPO3\CMS\Backend\Form\FormResultCompiler` has been
|
||||
deprecated. The internal implementation of FormEngine has been adjusted to
|
||||
better separate concerns, especially regarding rendering and asset handling.
|
||||
This change also removed all internal usages of
|
||||
:php:`TYPO3\CMS\Backend\Form\FormResultCompiler`,
|
||||
as it handled more tasks than its name suggested.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Extensions and installations that render FormEngine forms manually rather
|
||||
than through standard controllers, such as
|
||||
:php-short:`\TYPO3\CMS\Backend\Controller\EditDocumentController`, and that use
|
||||
:php-short:`TYPO3\CMS\Backend\Form\FormResultCompiler`, will be affected when the
|
||||
class is removed in TYPO3 v15.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
Installations and extensions using
|
||||
:php-short:`TYPO3\CMS\Backend\Form\FormResultCompiler` to build FormEngine forms.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Replace :php-short:`TYPO3\CMS\Backend\Form\FormResultCompiler` with
|
||||
:php-short:`\TYPO3\CMS\Backend\Form\FormResultFactory` and
|
||||
:php-short:`\TYPO3\CMS\Backend\Form\FormResultHandler`.
|
||||
|
||||
Before:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Backend\Form\NodeFactory;
|
||||
use TYPO3\CMS\Backend\Form\FormResultCompiler;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
$nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
|
||||
$formResultCompiler = GeneralUtility::makeInstance(
|
||||
FormResultCompiler::class
|
||||
);
|
||||
|
||||
$formResult = $nodeFactory->create($formData)->render();
|
||||
$formResultCompiler->mergeResult($formResult);
|
||||
|
||||
// Form HTML markup is accessible in the data array
|
||||
$body = $formResult['html'];
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Backend\Form\NodeFactory;
|
||||
use TYPO3\CMS\Backend\Form\FormResultFactory;
|
||||
use TYPO3\CMS\Backend\Form\FormResultHandler;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
$nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
|
||||
$formResultFactory = GeneralUtility::makeInstance(
|
||||
FormResultFactory::class
|
||||
);
|
||||
$formResultHandler = GeneralUtility::makeInstance(
|
||||
FormResultHandler::class
|
||||
);
|
||||
|
||||
$formResult = $nodeFactory->create($formData)->render();
|
||||
// Convert the raw result array into a FormResult object
|
||||
$formResult = $formResultFactory->create($formResult);
|
||||
|
||||
// Use FormResultHandler to pass collected assets (JS, CSS, labels) to PageRenderer
|
||||
$formResultHandler->addAssets($formResult);
|
||||
|
||||
// Form HTML markup is accessible in the FormResult DTO
|
||||
$body = $formResult->html;
|
||||
|
||||
.. index:: Backend, FullyScanned, ext:backend
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109280-1742109280:
|
||||
|
||||
=================================================================
|
||||
Deprecation: #109280 - FormEngine TcaDescription fieldInformation
|
||||
=================================================================
|
||||
|
||||
See :issue:`109280`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :php:`\TYPO3\CMS\Backend\Form\FieldInformation\TcaDescription` field
|
||||
information render type has been deprecated. Field descriptions configured
|
||||
via TCA :php:`['columns']['fieldName']['description']` are now rendered
|
||||
automatically next to the field label by
|
||||
:php:`\TYPO3\CMS\Backend\Form\Element\AbstractFormElement::renderDescription()`
|
||||
and
|
||||
:php:`\TYPO3\CMS\Backend\Form\Container\AbstractContainer::renderDescription()`.
|
||||
|
||||
Previously, every FormEngine element and container registered
|
||||
`tcaDescription` as a default field information node, which rendered the
|
||||
description inside the element body. The description is now rendered
|
||||
after the label or legend element, providing more consistent positioning
|
||||
across all field types.
|
||||
|
||||
Additionally, the :php:`$defaultFieldInformation` property has been removed
|
||||
from all Core FormEngine elements and containers. Custom elements that extend
|
||||
Core elements and rely on `tcaDescription` being present in
|
||||
:php:`$defaultFieldInformation` are also affected.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Using the `tcaDescription` render type in a custom `fieldInformation`
|
||||
configuration will trigger a PHP :php:`E_USER_DEPRECATED` level error. The
|
||||
render type still exists but will return empty output during the deprecation
|
||||
period, since descriptions are now rendered at the label level.
|
||||
|
||||
Custom FormEngine nodes that extend core elements and override
|
||||
:php:`$defaultFieldInformation` to include `tcaDescription` will still work,
|
||||
but the `tcaDescription` entry will trigger a deprecation warning.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
* Installations with extensions that explicitly configure `tcaDescription`
|
||||
as a field information node in TCA:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
'fieldInformation' => [
|
||||
'tcaDescription' => [
|
||||
'renderType' => 'tcaDescription',
|
||||
],
|
||||
],
|
||||
|
||||
* Custom FormEngine elements and containers that set `tcaDescription` in
|
||||
their :php:`$defaultFieldInformation` property:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
protected $defaultFieldInformation = [
|
||||
'tcaDescription' => [
|
||||
'renderType' => 'tcaDescription',
|
||||
],
|
||||
];
|
||||
|
||||
Extensions that only use the standard TCA `description` property are not
|
||||
affected — descriptions will continue to be rendered.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Remove any explicit `tcaDescription` field information configuration from
|
||||
TCA and from custom FormEngine node classes. Field descriptions are now
|
||||
rendered automatically next to the label and no longer require a field
|
||||
information node.
|
||||
|
||||
**TCA configuration**
|
||||
|
||||
.. code-block:: diff
|
||||
|
||||
'columns' => [
|
||||
'my_field' => [
|
||||
'label' => 'My field',
|
||||
'description' => 'Help text for this field',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
- 'fieldInformation' => [
|
||||
- 'tcaDescription' => [
|
||||
- 'renderType' => 'tcaDescription',
|
||||
- ],
|
||||
- ],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
**Custom FormEngine nodes with defaultFieldInformation**
|
||||
|
||||
If your custom element had a `tcaDescription` in
|
||||
:php:`$defaultFieldInformation`, remove the property entirely:
|
||||
|
||||
.. code-block:: diff
|
||||
|
||||
class MyCustomElement extends AbstractFormElement
|
||||
{
|
||||
- protected $defaultFieldInformation = [
|
||||
- 'tcaDescription' => [
|
||||
- 'renderType' => 'tcaDescription',
|
||||
- ],
|
||||
- ];
|
||||
+ // tcaDescription is no longer needed; descriptions are
|
||||
+ // rendered automatically next to the label.
|
||||
}
|
||||
|
||||
If your custom element has other field information entries alongside
|
||||
`tcaDescription`, remove only the `tcaDescription` entry:
|
||||
|
||||
.. code-block:: diff
|
||||
|
||||
class MyCustomElement extends AbstractFormElement
|
||||
{
|
||||
protected $defaultFieldInformation = [
|
||||
- 'tcaDescription' => [
|
||||
- 'renderType' => 'tcaDescription',
|
||||
- ],
|
||||
'myCustomInfo' => [
|
||||
'renderType' => 'myCustomInfo',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
.. index:: Backend, PHP-API, TCA, NotScanned, ext:backend
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109286-1773844395:
|
||||
|
||||
================================================================
|
||||
Deprecation: #109286 - Explicit request handling in PageRenderer
|
||||
================================================================
|
||||
|
||||
See :issue:`109286`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Since TYPO3 v14.2 some methods of class :php:`TYPO3\CMS\Core\Page\PageRenderer`
|
||||
require an instance of :php-short:`\Psr\Http\Message\ServerRequestInterface` to
|
||||
be passed explicitly :
|
||||
|
||||
setLanguage()
|
||||
-------------
|
||||
|
||||
* Old: :php:`PageRenderer->setLanguage(Locale $locale, ?ServerRequestInterface $request = null)`
|
||||
* TYPO3 v14.2: :php:`PageRenderer->setLanguage(Locale $locale, ?ServerRequestInterface $request = null)`
|
||||
* TYPO3 v15: :php:`PageRenderer->setLanguage(Locale $locale, ServerRequestInterface $request)`
|
||||
|
||||
setDocType()
|
||||
------------
|
||||
|
||||
* Old: :php:`PageRenderer->setDocType(DocType $docType)`
|
||||
* TYPO3 v14.2: :php:`PageRenderer->setDocType(DocType $docType, ?ServerRequestInterface $request = null)`
|
||||
* TYPO3 v15: :php:`PageRenderer->setDocType(DocType $docType, ServerRequestInterface $request)`
|
||||
|
||||
render()
|
||||
--------
|
||||
|
||||
* Old: :php:`PageRenderer->render()`
|
||||
* TYPO3 v14.2: :php:`PageRenderer->render(?ServerRequestInterface $request = null)`
|
||||
* TYPO3 v15: :php:`PageRenderer->render(ServerRequestInterface $request)`
|
||||
|
||||
renderResponse()
|
||||
----------------
|
||||
|
||||
* Old: :php:`PageRenderer->renderResponse(int $code = 200, string $reasonPhrase = '')`
|
||||
* TYPO3 v14.2: :php:`PageRenderer->render(ServerRequestInterface|int $requestOrCode = 200, int|string $codeOrReasonPhrase = '', string $reasonPhrase = '')`
|
||||
* TYPO3 v15: :php:`PageRenderer->render(ServerRequestInterface $request, int $code = 200, string $reasonPhrase = '')`
|
||||
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Request dependencies within
|
||||
:php-short:`TYPO3\CMS\Core\Page\PageRenderer` are no longer implicit via
|
||||
:php:`$GLOBALS['TYPO3_REQUEST']` and must now be passed explicitly. Not
|
||||
passing a request to the methods listed above will trigger a deprecation-level
|
||||
log entry in TYPO3 v14 and will result in a fatal PHP error in TYPO3 v15.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
:php-short:`TYPO3\CMS\Core\Page\PageRenderer` is a low-level Core class. Many
|
||||
extensions use higher-level APIs and are therefore not directly affected by
|
||||
this change.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Adapt method calls to pass the
|
||||
:php-short:`\Psr\Http\Message\ServerRequestInterface` object explicitly.
|
||||
|
||||
.. index:: Backend, Frontend, PHP-API, NotScanned, ext:core
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109295-1742407200:
|
||||
|
||||
==================================================================
|
||||
Deprecation: #109295 - DatabaseWriter::setLogTable()/getLogTable()
|
||||
==================================================================
|
||||
|
||||
See :issue:`109295`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The methods :php:`setLogTable()` and :php:`getLogTable()` in
|
||||
:php:`\TYPO3\CMS\Core\Log\Writer\DatabaseWriter` have been deprecated.
|
||||
|
||||
:php-short:`\TYPO3\CMS\Core\Log\Writer\DatabaseWriter` is a dedicated writer
|
||||
for the :sql:`sys_log` table. Its :php:`writeLog()` method maps
|
||||
:php-short:`\TYPO3\CMS\Core\Log\LogRecord` fields to the :sql:`sys_log`
|
||||
schema (:sql:`request_id`, :sql:`time_micro`, :sql:`component`,
|
||||
:sql:`level`, :sql:`message`, :sql:`data`, :sql:`tstamp`). Allowing an
|
||||
arbitrary table to be set via :php:`setLogTable()` created a false sense of
|
||||
flexibility - any custom table needs to replicate the full
|
||||
:sql:`sys_log` schema to work correctly.
|
||||
|
||||
The long-term goal is to make
|
||||
:php-short:`\TYPO3\CMS\Core\Log\Writer\DatabaseWriter` :php:`final` and to
|
||||
remove the :php:`$logTable` property entirely.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Calling :php:`setLogTable()` or :php:`getLogTable()` triggers a PHP
|
||||
:php:`E_USER_DEPRECATED` error. This also includes passing
|
||||
:php:`logTable` as a configuration option when
|
||||
:php-short:`\TYPO3\CMS\Core\Log\Writer\DatabaseWriter` is registered via
|
||||
:php:`$GLOBALS['TYPO3_CONF_VARS']['LOG']`, since the
|
||||
:php:`AbstractWriter` constructor resolves options to :php:`set*()` calls.
|
||||
|
||||
Support will be removed in TYPO3 v15.0.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
Installations that configure
|
||||
:php-short:`\TYPO3\CMS\Core\Log\Writer\DatabaseWriter` with a custom
|
||||
:php:`logTable` option, or that call :php:`setLogTable()` or
|
||||
:php:`getLogTable()` on a
|
||||
:php-short:`\TYPO3\CMS\Core\Log\Writer\DatabaseWriter` instance.
|
||||
|
||||
The extension scanner detects direct calls to :php:`->setLogTable()` and
|
||||
:php:`->getLogTable()`. The more common case, passing :php:`logTable` as a
|
||||
configuration option via :php:`$GLOBALS['TYPO3_CONF_VARS']['LOG']`, cannot
|
||||
be detected automatically and requires a manual search for
|
||||
:php-short:`\TYPO3\CMS\Core\Log\Writer\DatabaseWriter` usage with a
|
||||
:php:`logTable` key.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Replace :php-short:`\TYPO3\CMS\Core\Log\Writer\DatabaseWriter` with a
|
||||
dedicated writer that extends
|
||||
:php:`\TYPO3\CMS\Core\Log\Writer\AbstractWriter` and implements
|
||||
:php:`writeLog()` with explicit field mapping for the custom table.
|
||||
|
||||
Before:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use Psr\Log\LogLevel;
|
||||
use TYPO3\CMS\Core\Log\Writer\DatabaseWriter;
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['LOG']['writerConfiguration'][LogLevel::WARNING] =
|
||||
[
|
||||
DatabaseWriter::class => ['logTable' => 'my_custom_log'],
|
||||
];
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Log/Writer/MyCustomTableWriter.php
|
||||
|
||||
namespace MyVendor\MyExtension\Log\Writer;
|
||||
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Log\LogRecord;
|
||||
use TYPO3\CMS\Core\Log\Writer\AbstractWriter;
|
||||
use TYPO3\CMS\Core\Log\Writer\WriterInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
class MyCustomTableWriter extends AbstractWriter
|
||||
{
|
||||
public function writeLog(LogRecord $record): WriterInterface
|
||||
{
|
||||
GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getConnectionForTable('my_custom_log')
|
||||
->insert('my_custom_log', [
|
||||
'created' => (int)$record->getCreated(),
|
||||
'level' => $record->getLevel(),
|
||||
'message' => $record->getMessage(),
|
||||
]);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use Psr\Log\LogLevel;
|
||||
use MyVendor\MyExtension\Log\Writer\MyCustomTableWriter;
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['LOG']['writerConfiguration'][LogLevel::WARNING] = [
|
||||
MyCustomTableWriter::class => [],
|
||||
];
|
||||
|
||||
.. index:: PHP-API, PartiallyScanned, ext:core
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109306-1774010043:
|
||||
|
||||
===============================================================================
|
||||
Deprecation: #109306 - Deprecate form editor stage template rendering functions
|
||||
===============================================================================
|
||||
|
||||
See :issue:`109306`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The Form Editor stage component provided a set of JavaScript helper functions
|
||||
for template-based rendering of form elements in the stage area. These
|
||||
functions were designed to be called from subscribers to the
|
||||
:js:`view/stage/abstract/render/template/perform` PubSub event, which is the
|
||||
extension point for custom form element rendering in the stage.
|
||||
|
||||
With the introduction of the
|
||||
:html:`<typo3-form-form-element-stage-item>` and
|
||||
:html:`<typo3-form-page-stage-item>` web components (see
|
||||
:ref:`feature-107058-1769168658`), the built-in template-based helper
|
||||
functions have been superseded. The
|
||||
:js:`view/stage/abstract/render/template/perform` event
|
||||
**remains available**, and extension authors may continue to subscribe to it
|
||||
to implement fully custom stage rendering logic.
|
||||
|
||||
The following exported functions from
|
||||
:js:`@typo3/form/backend/form-editor/stage-component` are deprecated:
|
||||
|
||||
* :js:`eachTemplateProperty()`
|
||||
* :js:`renderSimpleTemplate()`
|
||||
* :js:`renderSimpleTemplateWithValidators()`
|
||||
* :js:`renderCheckboxTemplate()`
|
||||
* :js:`renderSelectTemplates()`
|
||||
* :js:`renderFileUploadTemplates()`
|
||||
* :js:`createAbstractViewFormElementToolbar()` — only used by the legacy
|
||||
template-based rendering path. Web component-based elements handle
|
||||
their toolbar via the :js:`toolbarConfig` property of
|
||||
:html:`<typo3-form-form-element-stage-item>`
|
||||
|
||||
In addition, all Fluid partial templates in
|
||||
:file:`EXT:form/Resources/Private/Backend/Partials/FormEditor/Stage/` are
|
||||
deprecated, as they were designed for use with the template-based rendering
|
||||
approach described above:
|
||||
|
||||
* :file:`SimpleTemplate.fluid.html`
|
||||
* :file:`SelectTemplate.fluid.html`
|
||||
* :file:`FileUploadTemplate.fluid.html`
|
||||
* :file:`ContentElement.fluid.html`
|
||||
* :file:`Fieldset.fluid.html`
|
||||
* :file:`StaticText.fluid.html`
|
||||
* :file:`Page.fluid.html`
|
||||
* :file:`SummaryPage.fluid.html`
|
||||
* :file:`_ElementToolbar.fluid.html`
|
||||
* :file:`_UnknownElement.fluid.html`
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Extensions that call any of the deprecated helper functions will receive IDE
|
||||
deprecation hints and TypeScript compiler warnings. The deprecated Fluid
|
||||
templates will emit an HTML comment in the rendered stage area indicating
|
||||
their deprecation. All deprecated functions and templates will be removed in
|
||||
TYPO3 v15.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
All extensions that:
|
||||
|
||||
* call any of the deprecated JavaScript helper functions (including
|
||||
:js:`createAbstractViewFormElementToolbar()`), typically from a subscriber
|
||||
of the :js:`view/stage/abstract/render/template/perform` event, or
|
||||
* reference any of the deprecated Fluid partial templates via
|
||||
:yaml:`formEditorPartials` in their prototype configuration.
|
||||
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Two migration paths are available:
|
||||
|
||||
**Option 1: Use the built-in web component (recommended)**
|
||||
|
||||
Remove the custom JavaScript subscriber and omit the
|
||||
:yaml:`formEditorPartials` stage partial configuration from your form
|
||||
element's YAML definition. The Form Editor will then render the element
|
||||
automatically using the built-in
|
||||
:html:`<typo3-form-form-element-stage-item>` web component.
|
||||
|
||||
See :ref:`feature-107058-1769168658` for full details.
|
||||
|
||||
**Option 2: Implement custom rendering logic in the event subscriber**
|
||||
|
||||
If you need to keep using the
|
||||
:js:`view/stage/abstract/render/template/perform` event, replace calls to
|
||||
the deprecated helper functions with your own DOM manipulation logic.
|
||||
|
||||
.. index:: Backend, JavaScript, NotScanned, ext:form
|
||||
@@ -0,0 +1,88 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109329-1774349266:
|
||||
|
||||
=================================================
|
||||
Deprecation: #109329 - PageRenderer get() methods
|
||||
=================================================
|
||||
|
||||
See :issue:`109329`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The following methods have been deprecated:
|
||||
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getTitle()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getLanguage()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getDocType()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getHtmlTag()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getHeadTag()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getFavIcon()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getIconMimeType()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getTemplateFile()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getMoveJsFromHeaderToFooter()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getBodyContent()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getInlineLanguageLabels()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getInlineLanguageLabelFiles()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->getMetaTag()`
|
||||
* :php:`TYPO3\CMS\Core\Page\PageRenderer->removeMetaTag()`
|
||||
* :php:`TYPO3\CMS\Frontend\ContentObject\AbstractContentObject->getPageRenderer()`
|
||||
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Invoking any of the methods listed above will generate a
|
||||
deprecation-level log entry in TYPO3 v14. These methods are scheduled
|
||||
for removal in TYPO3 v15.
|
||||
|
||||
From an architectural perspective, the
|
||||
:php-short:`TYPO3\CMS\Core\Page\PageRenderer` singleton represents
|
||||
a central yet problematic construct, particularly in TYPO3 frontend
|
||||
rendering. With the deprecation of
|
||||
these methods, the :php-short:`TYPO3\CMS\Core\Page\PageRenderer` class loses its
|
||||
ability to serve as a data source - data can still be added but no longer retrieved.
|
||||
|
||||
This change paves the way for refactoring the construct in TYPO3 v15,
|
||||
including the introduction of a compatibility layer to maintain
|
||||
backward compatibility.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
Instances with extensions invoking one of the methods listed above are
|
||||
affected. The extension scanner is configured to find consumers, apart from the
|
||||
generic method names :php:`getTitle()`, :php:`getLanguage()`, and :php:`getPageRenderer()`.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
In practice, there is often little reason to rely on the methods
|
||||
mentioned above. Most data passed to PageRenderer is handled through
|
||||
mechanisms that can be intercepted and configured, for example title
|
||||
and meta tag handling. As a result, the deprecated get() methods do
|
||||
not have a direct replacement.
|
||||
|
||||
A commonly used case is :php:`PageRenderer->getDocType()`, which
|
||||
determines whether self-closing tags should include a trailing slash
|
||||
(`/`). This is relevant only in the frontend, as the backend
|
||||
always uses HTML5. The DocType itself is derived from TypoScript
|
||||
configuration, which is available as a request attribute.
|
||||
|
||||
Before:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$needsEndingSlash = GeneralUtility::makeInstance(PageRenderer::class)
|
||||
->getDocType()
|
||||
->isXmlCompliant();
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$needsEndingSlash = DocType::createFromRequest($request)
|
||||
->isXmlCompliant();
|
||||
|
||||
.. index:: PHP-API, PartiallyScanned, ext:core
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109409-1774787352:
|
||||
|
||||
==================================================================
|
||||
Deprecation: #109409 - Access to arbitrary resources in extensions
|
||||
==================================================================
|
||||
|
||||
See :issue:`109409`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Accessing extension resources outside the configured resource
|
||||
definitions is deprecated.
|
||||
|
||||
By default, extension resources are limited to the following paths:
|
||||
|
||||
* :folder:`Configuration`
|
||||
* :folder:`Resources/Private`
|
||||
* :folder:`Resources/Public`
|
||||
|
||||
If a resource identifier references another extension path, that path
|
||||
must be configured explicitly in :file:`Configuration/Resources.php`.
|
||||
|
||||
See :ref:`feature-109409-1774770383` for information on how to
|
||||
configure resources for extensions.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
TYPO3 installations using resource identifiers that reference extension
|
||||
folders outside :folder:`Configuration`,
|
||||
:folder:`Resources/Private`, or :folder:`Resources/Public`
|
||||
will receive a deprecation message when such a resource is resolved.
|
||||
|
||||
Every accessed resource must be configured beforehand as described in
|
||||
:ref:`feature-109409-1774770383`.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
TYPO3 installations using resource identifiers that reference extension
|
||||
folders outside :folder:`Configuration`,
|
||||
:folder:`Resources/Private`, or :folder:`Resources/Public`.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Either configure the referenced paths explicitly in
|
||||
:file:`Configuration/Resources.php`, as described in
|
||||
:ref:`feature-109409-1774770383`, or move the resources to a path that
|
||||
is already configured.
|
||||
|
||||
.. index:: PHP-API, NotScanned, ext:core
|
||||
@@ -0,0 +1,44 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109409-1774774806:
|
||||
|
||||
================================================================
|
||||
Deprecation: #109409 - Allowed paths configuration is deprecated
|
||||
================================================================
|
||||
|
||||
See :issue:`109409`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Using :php:`$GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths']`
|
||||
to configure additional public paths for the `typo3/app` package
|
||||
has been deprecated.
|
||||
|
||||
Configure resources in :file:`config/system/resources.php` instead.
|
||||
See :ref:`feature-109409-1774770383` for details.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
TYPO3 installations that use
|
||||
:php:`$GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths']`
|
||||
will receive a deprecation message whenever resources for the
|
||||
`typo3/app` package are resolved.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
TYPO3 installations that use
|
||||
:php:`$GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths']`.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Configure resources in :file:`config/system/resources.php` instead of
|
||||
using :php:`$GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths']`.
|
||||
|
||||
See :ref:`feature-109409-1774770383` for information on how to
|
||||
configure resources for the `typo3/app` package.
|
||||
|
||||
.. index:: PHP-API, NotScanned, ext:core
|
||||
@@ -0,0 +1,96 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-109412-1742000001:
|
||||
|
||||
==============================================================
|
||||
Deprecation: #109412 - TypoScript-based form YAML registration
|
||||
==============================================================
|
||||
|
||||
See :issue:`109412`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The TypoScript-based registration of form YAML configuration files via
|
||||
:typoscript:`plugin.tx_form.settings.yamlConfigurations` and
|
||||
:typoscript:`module.tx_form.settings.yamlConfigurations` has been
|
||||
deprecated in favor of the new auto-discovery mechanism introduced in
|
||||
TYPO3 v14.2 (see :ref:`Feature-109412 <feature-109412-1742000001>`).
|
||||
|
||||
Before TYPO3 v14.2 this was the only way to register `EXT:form` YAML files. It
|
||||
required separate registration of the frontend and the backend
|
||||
in TypoScript:
|
||||
|
||||
.. code-block:: typoscript
|
||||
:caption: EXT:my_extension/Configuration/TypoScript/setup.typoscript — deprecated
|
||||
|
||||
plugin.tx_form.settings.yamlConfigurations {
|
||||
1732785702 = EXT:my_extension/Configuration/Form/MySetup.yaml
|
||||
}
|
||||
|
||||
# Backend had to be registered separately:
|
||||
module.tx_form.settings.yamlConfigurations {
|
||||
1732785703 = EXT:my_extension/Configuration/Form/MySetup.yaml
|
||||
}
|
||||
|
||||
The TypoScript-based paths will still be loaded during the deprecation
|
||||
period but will be removed in TYPO3 v15.0.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Extensions that register form YAML files via TypoScript will trigger a
|
||||
PHP :php:`E_USER_DEPRECATED` error. The registered YAML files are still
|
||||
loaded and will remain functional during the deprecation period.
|
||||
|
||||
Affected installations
|
||||
======================
|
||||
|
||||
All installations where an extension registers form YAML files via:
|
||||
|
||||
* :typoscript:`plugin.tx_form.settings.yamlConfigurations`
|
||||
* :typoscript:`module.tx_form.settings.yamlConfigurations`
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Replace TypoScript registration with the auto-discovery directory
|
||||
convention introduced in TYPO3 v14.2
|
||||
(see :ref:`Feature-109412 <feature-109412-1742000001>`).
|
||||
|
||||
1. Create directory :file:`EXT:my_extension/Configuration/Form/MySet/`.
|
||||
|
||||
2. Add a :file:`config.yaml` file with a unique `name` and, optionally,
|
||||
a `priority` value (default: 100; the core base set is
|
||||
priority 10):
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: EXT:my_extension/Configuration/Form/MySet/config.yaml
|
||||
|
||||
name: my-vendor/my-form-set
|
||||
label: 'My Custom Form Set'
|
||||
priority: 200
|
||||
|
||||
3. Add your existing form configuration to :file:`config.yaml` below
|
||||
the metadata keys:
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: EXT:my_extension/Configuration/Form/MySet/config.yaml
|
||||
|
||||
name: my-vendor/my-form-set
|
||||
label: 'My Custom Form Set'
|
||||
priority: 200
|
||||
|
||||
# Content of your former MySetup.yaml
|
||||
persistenceManager:
|
||||
allowedExtensionPaths:
|
||||
10: 'EXT:my_extension/Resources/Private/Forms/'
|
||||
|
||||
4. Remove TypoScript registrations from
|
||||
:file:`setup.typoscript`. PHP or TypoScript registration is
|
||||
no longer necessary.
|
||||
|
||||
The YAML files are picked up automatically for **both** frontend and
|
||||
backend without any additional registration.
|
||||
|
||||
.. index:: YAML, Frontend, Backend, FullyScanned, ext:form
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _deprecation-69190-1770668741:
|
||||
|
||||
========================================================================================
|
||||
Deprecation: #69190 - Deprecate random password generator for frontend and backend users
|
||||
========================================================================================
|
||||
|
||||
See :issue:`69190`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The `passwordRules` option of the `passwordGenerator` field control has been
|
||||
deprecated. Password generation is now configured through password policies
|
||||
registered in :php:`$GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies']`.
|
||||
|
||||
Each password policy can define a `generator` section with a class implementing
|
||||
:php:`\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 in password
|
||||
or secret token fields.
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Replace the `passwordRules` option with a `passwordPolicy` reference.
|
||||
|
||||
.. code-block:: diff
|
||||
:caption: EXT:my_extension/Configuration/TCA/Overrides/be_users.php
|
||||
|
||||
'fieldControl' => [
|
||||
'passwordGenerator' => [
|
||||
'renderType' => 'passwordGenerator',
|
||||
'options' => [
|
||||
- 'passwordRules' => [
|
||||
- 'length' => 20,
|
||||
- 'upperCaseCharacters' => true,
|
||||
- 'lowerCaseCharacters' => true,
|
||||
- 'digitCharacters' => true,
|
||||
- 'specialCharacters' => false,
|
||||
- ],
|
||||
+ 'passwordPolicy' => 'myCustomPolicy',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
The referenced password policy must be registered in
|
||||
:php:`$GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies']`:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: config/system/additional.php OR typo3conf/system/additional.php
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies']['myCustomPolicy'] = [
|
||||
'generator' => [
|
||||
'className' => \TYPO3\CMS\Core\PasswordPolicy\Generator\PasswordGenerator::class,
|
||||
'options' => [
|
||||
'length' => 20,
|
||||
'upperCaseCharacters' => true,
|
||||
'lowerCaseCharacters' => true,
|
||||
'digitCharacters' => true,
|
||||
'specialCharacters' => false,
|
||||
],
|
||||
],
|
||||
'validators' => [],
|
||||
];
|
||||
|
||||
.. note::
|
||||
|
||||
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
|
||||
:php:`$GLOBALS['TYPO3_CONF_VARS']['BE']['passwordPolicy']` and
|
||||
:php:`$GLOBALS['TYPO3_CONF_VARS']['FE']['passwordPolicy']` respectively.
|
||||
|
||||
See :ref:`feature-69190-1770137533` for details on password policies and
|
||||
custom password generators.
|
||||
|
||||
.. index:: Backend, Frontend, PHP-API, TCA, NotScanned
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-100254-1742119200:
|
||||
|
||||
==================================================================
|
||||
Feature: #100254 - Support download attribute in file link browser
|
||||
==================================================================
|
||||
|
||||
See :issue:`100254`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The HTML5 :html:`download` attribute can now be configured for a link in
|
||||
the file link browser. When set, the browser forces a file download
|
||||
instead of navigating to the file URL.
|
||||
|
||||
The link browser renders a `Force download` checkbox for file links. When
|
||||
enabled, an optional `Custom filename` text field appears, allowing
|
||||
editors to specify an alternative filename for the downloaded file.
|
||||
|
||||
The TypoLink codec supports an optional seventh TypoLink segment for
|
||||
:html:`download`. The value :php:`true` produces a boolean download
|
||||
attribute (:html:`<a download>`). Any other string value produces a
|
||||
named download attribute (:html:`<a download="custom-name.pdf">`).
|
||||
|
||||
Example TypoLink strings:
|
||||
|
||||
* `t3://file?uid=42 - - - - - true`
|
||||
* `t3://file?uid=42 - - - - - report.pdf`
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Editors can now set whether a file should be downloaded or
|
||||
displayed in the browser directly in the link. This works in both the RTE and non-RTE link
|
||||
browser dialogs.
|
||||
|
||||
Existing TypoLink values without :html:`download` remain unchanged and
|
||||
continue to work as before.
|
||||
|
||||
.. index:: Backend, Frontend, RTE, ext:backend, ext:frontend
|
||||
@@ -0,0 +1,149 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-100887-1773012077:
|
||||
|
||||
===========================================================
|
||||
Feature: #100887 - Prefer CSP hash values over nonce values
|
||||
===========================================================
|
||||
|
||||
See :issue:`100887`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Content-Security-Policy nonce values are random tokens in each request that prevent
|
||||
HTTP response caching. By collecting hash values of assets at render time
|
||||
instead, responses can be cached, for example by using
|
||||
:composer:`lochmueller/staticfilecache` or reverse proxies, while still
|
||||
enforcing a strict CSP.
|
||||
|
||||
Hash-based CSP is an explicit opt-in configured for a site via :file:`csp.yaml`.
|
||||
Nonce values remain the default when no behavior is configured.
|
||||
|
||||
New `DirectiveHashCollection` service
|
||||
-------------------------------------
|
||||
|
||||
The new
|
||||
:php:`\TYPO3\CMS\Core\Security\ContentSecurityPolicy\DirectiveHashCollection`
|
||||
service is a per-request registry that collects CSP hash values for inline and
|
||||
static assets during page rendering.
|
||||
|
||||
Both inline content and static file resources are supported:
|
||||
|
||||
* Inline assets: the SHA-256 hash is computed over the content that appears
|
||||
inside the :html:`<script>` or :html:`<style>` element.
|
||||
* Static assets: if an :html:`integrity` attribute is already present, its
|
||||
value is reused; otherwise, the file content is hashed on demand.
|
||||
* Style attributes: the new :html:`f:asset.styleAttr` ViewHelper hashes
|
||||
inline style values and covers the :csp:`style-src-attr` directive.
|
||||
|
||||
The collected hashes survive the frontend page cache round-trip via
|
||||
:php:`\TYPO3\CMS\Frontend\Cache\MetaDataState`.
|
||||
|
||||
Updated `Behavior` class
|
||||
------------------------
|
||||
|
||||
:php:`\TYPO3\CMS\Core\Security\ContentSecurityPolicy\Configuration\Behavior`
|
||||
now carries a second nullable boolean property, :php:`$useHash`:
|
||||
|
||||
* :php:`true` explicitly enables hash collection and CSP hash sources.
|
||||
* :php:`null` means off, which is the default. Hashes are not collected or
|
||||
applied.
|
||||
* :php:`false` explicitly disables hash collection and CSP hash sources.
|
||||
|
||||
Configuring behavior via `csp.yaml`
|
||||
-----------------------------------
|
||||
|
||||
Both :php:`$useNonce` and :php:`$useHash` can be set in a site's
|
||||
:file:`config/sites/<site>/csp.yaml` under the top-level :yaml:`behavior:` key:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
behavior:
|
||||
useNonce: false
|
||||
useHash: true
|
||||
|
||||
enforce:
|
||||
inheritDefault: true
|
||||
includeResolutions: true
|
||||
|
||||
Setting :yaml:`useHash: true` enables hash-based CSP for that site.
|
||||
Setting :yaml:`useNonce: false` removes nonce sources from the compiled
|
||||
policy, which is required for responses to be cacheable by reverse proxies.
|
||||
|
||||
Updated `Policy::prepare()` and `Policy::compile()`
|
||||
---------------------------------------------------
|
||||
|
||||
Both methods now accept a
|
||||
:php:`\TYPO3\CMS\Core\Security\ContentSecurityPolicy\Middleware\PolicyBag`
|
||||
instead of separate :php:`ConsumableNonce`, :php:`Behavior`, and
|
||||
:php:`HashCollection` arguments. The :php:`PolicyBag` is forwarded directly
|
||||
from the CSP middleware, making hash collection visible to PSR-14 event
|
||||
listeners via
|
||||
:php:`PolicyPreparedEvent::$policyBag->directiveHashCollection`.
|
||||
|
||||
The behavior resolution, applying collected hashes and suppressing nonce
|
||||
sources, now happens inside :php:`Policy::prepare()`.
|
||||
|
||||
New `f:asset.styleAttr` ViewHelper
|
||||
----------------------------------
|
||||
|
||||
A new ViewHelper registers inline style values using the
|
||||
:csp:`style-src-attr` CSP directive:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<div style="{f:asset.styleAttr(value: 'color: green', csp: true)}"></div>
|
||||
|
||||
The :html:`csp` argument defaults to :html:`true` and controls whether the
|
||||
hash is collected.
|
||||
|
||||
Updated `f:asset.script` and `f:asset.css` ViewHelpers
|
||||
------------------------------------------------------
|
||||
|
||||
The :html:`useNonce` argument has been renamed to :html:`csp`
|
||||
(deprecated, see :ref:`deprecation-100887-1774712028`). The new default is
|
||||
:html:`true` for external files, that is, static resources, and
|
||||
:html:`false` for inline content.
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<!-- static file: csp=1 by default, hash collected from file content -->
|
||||
<f:asset.script
|
||||
identifier="my-script"
|
||||
src="EXT:my_ext/Resources/Public/JavaScript/foo.js"
|
||||
/>
|
||||
|
||||
<!-- with integrity attribute: hash reused directly, no file read -->
|
||||
<f:asset.script
|
||||
identifier="my-script"
|
||||
src="EXT:my_ext/Resources/Public/JavaScript/foo.js"
|
||||
integrity="sha256-abc123=="
|
||||
/>
|
||||
|
||||
<!-- inline script: opt in explicitly -->
|
||||
<f:asset.script identifier="my-inline" csp="1">
|
||||
document.querySelector('.foo').classList.add('active');
|
||||
</f:asset.script>
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
The :html:`useNonce` ViewHelper argument and :php:`'useNonce'` asset option key
|
||||
are deprecated and replaced by :html:`csp` and :php:`'csp'`. See
|
||||
:ref:`deprecation-100887-1774712028`.
|
||||
|
||||
The signature of :php:`Policy::prepare()` and :php:`Policy::compile()` has
|
||||
changed to accept a :php:`PolicyBag`. Code calling these methods directly, as
|
||||
they are marked :php:`@internal`, must be updated.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Sites that configure :yaml:`behavior.useHash: true`, and optionally
|
||||
:yaml:`behavior.useNonce: false`, in their :file:`csp.yaml` can use hash-based
|
||||
CSP sources. This allows HTTP responses to be cached by reverse proxies and
|
||||
static file cache extensions without sacrificing Content-Security-Policy
|
||||
enforcement. Sites without this configuration continue to use nonce-based CSP.
|
||||
|
||||
.. index:: Backend, Frontend, PHP-API, FluidViewHelpers, ext:core, ext:fluid
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-102079-1756482906:
|
||||
|
||||
===========================================================================
|
||||
Feature: #102079 - Introduce BeforePersistingReportEvent for CSP violations
|
||||
===========================================================================
|
||||
|
||||
See :issue:`102079`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
When a Content-Security-Policy violation report needs to be persisted, the
|
||||
:php:`\TYPO3\CMS\Core\Security\ContentSecurityPolicy\Event\BeforePersistingReportEvent`
|
||||
can be used to provide an alternative report or to prevent a particular report
|
||||
from being persisted.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Example\Demo\EventListener;
|
||||
|
||||
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Event\BeforePersistingReportEvent;
|
||||
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\Report;
|
||||
|
||||
final class BeforePersistingReportEventListener
|
||||
{
|
||||
private const BROWSER_PREFIXES = [
|
||||
'chrome-extension://',
|
||||
'moz-extension://',
|
||||
'safari-extension://',
|
||||
];
|
||||
|
||||
#[AsEventListener('example/security/before-persisting-csp-report')]
|
||||
public function __invoke(BeforePersistingReportEvent $event): void
|
||||
{
|
||||
// Avoid persisting CSP violations caused by browser extensions
|
||||
$blockedUri = $event->originalReport->details['blocked-uri'] ?? null;
|
||||
if (is_string($blockedUri) && $this->isBrowserExtensions($blockedUri)) {
|
||||
$event->report = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, adjust the report and provide custom metadata
|
||||
$event->report = new Report(
|
||||
$event->originalReport->scope,
|
||||
$event->originalReport->status,
|
||||
$event->originalReport->requestTime,
|
||||
array_merge(
|
||||
$event->originalReport->meta,
|
||||
['x-example' => '... additional metadata ...']
|
||||
),
|
||||
$event->originalReport->details,
|
||||
$event->originalReport->summary,
|
||||
$event->originalReport->uuid,
|
||||
$event->originalReport->created,
|
||||
$event->originalReport->changed
|
||||
);
|
||||
}
|
||||
|
||||
private function isBrowserExtensions(string $blockedUri): bool
|
||||
{
|
||||
foreach (self::BROWSER_PREFIXES as $prefix) {
|
||||
if (str_starts_with($blockedUri, $prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The new
|
||||
:php-short:`\TYPO3\CMS\Core\Security\ContentSecurityPolicy\Event\BeforePersistingReportEvent`
|
||||
allows custom control over whether and how Content-Security-Policy violation
|
||||
reports are persisted in TYPO3.
|
||||
|
||||
.. index:: Backend, Frontend, PHP-API, ext:core
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-102159-1675976883:
|
||||
|
||||
=============================================================================
|
||||
Feature: #102159 - Support additional parameters for TCA slug prefix userFunc
|
||||
=============================================================================
|
||||
|
||||
See :issue:`102159`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
TCA slug prefix user functions now receive the full TCA field configuration
|
||||
and the field name as additional parameters.
|
||||
|
||||
User function implementation
|
||||
============================
|
||||
|
||||
The prefix user function receives two additional keys alongside the existing
|
||||
parameters:
|
||||
|
||||
* `fieldName` - The name of the slug field
|
||||
* `config` - The full TCA configuration array of the slug field
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Utility/SlugUtility.php
|
||||
|
||||
namespace MyExtension\Utility;
|
||||
|
||||
class SlugUtility
|
||||
{
|
||||
public function generatePrefix(array $parameters): string
|
||||
{
|
||||
// Standard parameters (always available)
|
||||
$site = $parameters['site'];
|
||||
$languageId = $parameters['languageId'];
|
||||
$table = $parameters['table'];
|
||||
$row = $parameters['row'];
|
||||
|
||||
$fieldName = $parameters['fieldName'];
|
||||
$config = $parameters['config'];
|
||||
|
||||
return '/default/';
|
||||
}
|
||||
}
|
||||
|
||||
Available parameters
|
||||
====================
|
||||
|
||||
The user function receives an array with the following keys:
|
||||
|
||||
* `site` - The current site object
|
||||
* `languageId` - The current language ID (int)
|
||||
* `table` - The table name (string)
|
||||
* `row` - The current record data (array)
|
||||
* `fieldName` - The name of the slug field (string)
|
||||
* `config` - The full TCA configuration array of the slug field
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Extension developers can access the complete TCA field configuration and the
|
||||
field name inside prefix user functions.
|
||||
|
||||
.. index:: Backend, TCA, ext:backend
|
||||
@@ -0,0 +1,73 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-102194-1772779432:
|
||||
|
||||
==================================================
|
||||
Feature: #102194 - Introduce QueryBuilderPaginator
|
||||
==================================================
|
||||
|
||||
See :issue:`102194`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new :php:`\TYPO3\CMS\Core\Pagination\QueryBuilderPaginator` is introduced to
|
||||
enable pagination of
|
||||
:php-short:`\TYPO3\CMS\Core\Database\Query\QueryBuilder` instances.
|
||||
|
||||
The paginator implements the existing
|
||||
:php-short:`\TYPO3\CMS\Core\Pagination\PaginatorInterface` and integrates
|
||||
seamlessly with the existing
|
||||
:php-short:`\TYPO3\CMS\Core\Pagination\SimplePagination` and
|
||||
:php-short:`\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 :php:`QueryBuilder` instance. This approach correctly
|
||||
handles advanced queries involving `UNION`, nested CTEs, window functions, and
|
||||
grouping.
|
||||
|
||||
.. note::
|
||||
|
||||
The :php-short:`\TYPO3\CMS\Core\Pagination\QueryBuilderPaginator` does
|
||||
**not** handle language overlays. Applying overlays to the result set can
|
||||
lead to unexpected item count differences between pages when some records
|
||||
are hidden after overlay processing. Use
|
||||
:php-short:`\TYPO3\CMS\Extbase\Pagination\QueryResultPaginator` or
|
||||
:php-short:`\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 or offset constraints on the
|
||||
passed :php:`QueryBuilder` instance.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
A new :php-short:`\TYPO3\CMS\Core\Pagination\QueryBuilderPaginator` is
|
||||
available to paginate
|
||||
:php-short:`\TYPO3\CMS\Core\Database\Query\QueryBuilder` result sets using
|
||||
the TYPO3 pagination API.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Controller/MyController.php
|
||||
|
||||
use TYPO3\CMS\Core\Pagination\QueryBuilderPaginator;
|
||||
use TYPO3\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();
|
||||
|
||||
.. index:: Database, PHP-API, ext:core
|
||||
@@ -0,0 +1,86 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-102215-1709554850:
|
||||
|
||||
===========================================================================
|
||||
Feature: #102215 - ViewHelper and data structure to render srcset attribute
|
||||
===========================================================================
|
||||
|
||||
See :issue:`102215`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The `srcset` HTML attribute can be used to provide different image sizes to
|
||||
the browser. The browser is free to choose which image size to use, which is
|
||||
why the images must all be scaled versions of the same original image. Each
|
||||
image in the `srcset` list also has a descriptor which either specifies
|
||||
the absolute width of the image, for example `400w`, or is a scale factor
|
||||
relative to the original image size for use on high-density screens, for
|
||||
example `2x`.
|
||||
|
||||
`srcset` attributes are used by various HTML tags:
|
||||
|
||||
* :html:`<img srcset="image@500.jpg 500w, image@1000.jpg 1000w" />`
|
||||
* :html:`<source srcset="image@1x.jpg 1x, image@2x.jpg 2x" />` inside
|
||||
:html:`<picture>`
|
||||
* :html:`<link rel="preload" as="image" imagesrcset="image@500.jpg 500w, image@1000.jpg 1000w" />`
|
||||
|
||||
.. note::
|
||||
|
||||
File name notation like `image@500.jpg` is just a regular file name
|
||||
used to indicate its pixel dimensions. The `@` notation has no inherent
|
||||
conversion magic, unlike the descriptors `500w` and `1x`.
|
||||
|
||||
To generate `srcset` attributes easily based on input, a new
|
||||
data structure has been added to calculate the appropriate image sizes from a
|
||||
list of descriptors. Based on these calculations, image files can be
|
||||
generated using the image manipulation API.
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_ext/Classes/Service/SomeImageService.php
|
||||
|
||||
use TYPO3\CMS\Core\Html\Srcset\SrcsetAttribute;
|
||||
|
||||
// From width descriptors
|
||||
$srcset = SrcsetAttribute::createFromDescriptors(['400w', '600w', '800w']);
|
||||
|
||||
// Or from pixel density descriptors (a reference width must be supplied)
|
||||
$srcset = SrcsetAttribute::createFromDescriptors(
|
||||
['1.5x', '2x', '3x'],
|
||||
800
|
||||
);
|
||||
|
||||
// Add image URIs
|
||||
foreach ($srcset->getCandidates() as $candidate) {
|
||||
// Generate scaled image here using $candidate->getCalculatedWidth()
|
||||
|
||||
// Set URI of the generated image
|
||||
$candidate->setUri($generatedImageUri);
|
||||
}
|
||||
|
||||
// Render srcset attribute
|
||||
$srcsetString = $srcset->generateSrcset();
|
||||
|
||||
To generate `srcset` attributes in Fluid templates, a new ViewHelper has also
|
||||
been introduced.
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<picture>
|
||||
<source
|
||||
srcset="{f:image.srcset(image: imageObject, srcset: '400w, 600w, 800w', cropVariant: 'wide')}"
|
||||
sizes="100vw"
|
||||
media="(min-width: 1200px)"
|
||||
/>
|
||||
<!-- ... -->
|
||||
</picture>
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The new ViewHelper `f:image.srcset` simplifies previous manual implementations
|
||||
that used :fluid:`f:uri.image` for each image size. This now makes it easier
|
||||
to provide images in different dimensions based on a single image.
|
||||
|
||||
.. index:: Fluid, PHP-API, ext:core, ext:fluid
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-102430-1700581800:
|
||||
|
||||
==================================================================
|
||||
Feature: #102430 - Flush cache tags for file and folder operations
|
||||
==================================================================
|
||||
|
||||
See :issue:`102430`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
This feature is guarded by the `frontend.cache.autoTagging` feature toggle and
|
||||
is currently experimental. The core flushes cache tags automatically for all
|
||||
kinds of records when they are created, changed, or deleted. This is not the
|
||||
case for files and folders. This feature adds cache tag handling for file and
|
||||
folder operations when they are created, changed, or deleted. File metadata
|
||||
changes are now handled correctly as well. This will lead to a better editor
|
||||
experience if 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 by
|
||||
the TYPO3 core when an editor interacts with them in the :guilabel:`Media` module.
|
||||
|
||||
.. index:: FAL, PHP-API, ext:core
|
||||
@@ -0,0 +1,37 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-102790-1738838400:
|
||||
|
||||
=======================================================
|
||||
Feature: #102790 - Line wrapping option for code editor
|
||||
=======================================================
|
||||
|
||||
See :issue:`102790`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new TCA appearance option `lineWrapping` has been added for the
|
||||
`codeEditor` render type. When enabled, long lines are wrapped inside
|
||||
the editor instead of requiring horizontal scrolling.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
'config' => [
|
||||
'type' => 'text',
|
||||
'renderType' => 'codeEditor',
|
||||
'format' => 'html',
|
||||
'appearance' => [
|
||||
'lineWrapping' => true,
|
||||
],
|
||||
],
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Code editor fields can now be configured to wrap long lines by setting
|
||||
`lineWrapping` in the `appearance` array.
|
||||
|
||||
.. index:: Backend, TCA, ext:backend
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-104546-1737580000:
|
||||
|
||||
==============================================================
|
||||
Feature: #104546 - Support ICU MessageFormat for plural forms
|
||||
==============================================================
|
||||
|
||||
See :issue:`104546`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
TYPO3 now supports ICU MessageFormat for translations. This enables the proper handling
|
||||
of plural forms, gender-based selections, and other locale-aware formatting
|
||||
in language labels.
|
||||
|
||||
ICU MessageFormat is an internationalization standard that allows messages to
|
||||
contain placeholders that can vary based on parameters such as quantity,
|
||||
gender, or other conditions. This is particularly useful for proper
|
||||
pluralization in languages with complex plural rules.
|
||||
|
||||
The format is detected automatically when named arguments, that is,
|
||||
associative arrays, are used in translation calls. If the message contains ICU
|
||||
patterns like `{count, plural, ...}` or `{name}`, and named arguments are
|
||||
provided, the ICU MessageFormatter is used automatically.
|
||||
|
||||
Language file format
|
||||
--------------------
|
||||
|
||||
ICU MessageFormat strings are stored as regular translation strings in XLIFF
|
||||
files:
|
||||
|
||||
.. code-block:: xml
|
||||
:caption: EXT:my_extension/Resources/Private/Language/locallang.xlf
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="locallang.xlf">
|
||||
<body>
|
||||
<!-- Simple plural form -->
|
||||
<trans-unit id="file_count">
|
||||
<source>{count, plural, one {# file} other {# files}}</source>
|
||||
</trans-unit>
|
||||
|
||||
<!-- Plural with zero case -->
|
||||
<trans-unit id="item_count">
|
||||
<source>{count, plural, =0 {no items} one {# item} other {# items}}</source>
|
||||
</trans-unit>
|
||||
|
||||
<!-- Combined placeholder and plural -->
|
||||
<trans-unit id="greeting">
|
||||
<source>Hello {name}, you have {count, plural, one {# message} other {# messages}}.</source>
|
||||
</trans-unit>
|
||||
|
||||
<!-- Gender selection -->
|
||||
<trans-unit id="profile_update">
|
||||
<source>{gender, select, male {He} female {She} other {They}} updated the profile.</source>
|
||||
</trans-unit>
|
||||
|
||||
<!-- Simple named placeholder -->
|
||||
<trans-unit id="welcome">
|
||||
<source>Welcome, {name}!</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
PHP usage
|
||||
---------
|
||||
|
||||
Use named arguments in an associative array to trigger ICU
|
||||
MessageFormat processing:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: Using ICU MessageFormat with LanguageService
|
||||
|
||||
use TYPO3\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"
|
||||
|
||||
.. code-block:: php
|
||||
:caption: Using ICU MessageFormat with LocalizationUtility
|
||||
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
|
||||
// Use named arguments for ICU format
|
||||
$label = LocalizationUtility::translate(
|
||||
'file_count',
|
||||
'MyExtension',
|
||||
['count' => 1]
|
||||
);
|
||||
// Result: "1 file"
|
||||
|
||||
Fluid usage
|
||||
-----------
|
||||
|
||||
In Fluid templates use named arguments in the `arguments` attribute:
|
||||
|
||||
.. code-block:: html
|
||||
:caption: EXT:my_extension/Resources/Private/Templates/Example.html
|
||||
|
||||
<!-- ICU plural forms with named arguments -->
|
||||
<f:translate key="file_count" arguments="{count: numberOfFiles}" />
|
||||
|
||||
<!-- Combined placeholder and plural -->
|
||||
<f:translate key="greeting" arguments="{name: userName, count: messageCount}" />
|
||||
|
||||
<!-- Gender selection -->
|
||||
<f:translate key="profile_update" arguments="{gender: userGender}" />
|
||||
|
||||
<!-- sprintf-style with positional arguments still works -->
|
||||
<f:translate key="downloaded_times" arguments="{0: downloadCount}" />
|
||||
|
||||
ICU MessageFormat syntax reference
|
||||
----------------------------------
|
||||
|
||||
**Plural forms:**
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
{variable, plural,
|
||||
=0 {zero case}
|
||||
one {singular case}
|
||||
other {plural case}
|
||||
}
|
||||
|
||||
**Select (gender/choice):**
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
{variable, select,
|
||||
male {He}
|
||||
female {She}
|
||||
other {They}
|
||||
}
|
||||
|
||||
**Number formatting:**
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
{count, number} - Basic number
|
||||
{price, number, currency} - Currency format
|
||||
|
||||
The `#` symbol in plural patterns is replaced by 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
|
||||
* handles locale-specific plural rules
|
||||
* supports complex pluralization for languages such as Russian and Arabic
|
||||
* is backward compatible; existing sprintf-style translations will continue to
|
||||
work
|
||||
|
||||
The system detects which format to use based on arguments:
|
||||
|
||||
* **Named arguments** (associative array): Uses ICU MessageFormat
|
||||
* **Positional arguments** (indexed array): Uses sprintf
|
||||
|
||||
.. index:: PHP-API, Fluid, ext:core, ext:extbase, ext:fluid
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-104974-1726401724:
|
||||
|
||||
===================================================================
|
||||
Feature: #104974 - Content area related information in the frontend
|
||||
===================================================================
|
||||
|
||||
See :issue:`104974`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
:ref:`feature-103504-1712041725` introduced the :typoscript:`PAGEVIEW` cObject
|
||||
for frontend rendering. It is a powerful alternative to
|
||||
the :typoscript:`FLUIDTEMPLATE` cObject, allowing a full page to be rendered with
|
||||
less configuration.
|
||||
|
||||
:typoscript:`PAGEVIEW` has now been extended and provides all
|
||||
content elements related to a page, grouped by their columns
|
||||
as defined in the page layout. The elements are provided as fully resolved
|
||||
:php:`Record` objects (see :ref:`feature-103783-1715113274` and
|
||||
:ref:`feature-103581-1723209131`).
|
||||
|
||||
The content elements are attached to the new
|
||||
:php:`\TYPO3\CMS\Core\Page\ContentArea` object, which also contains all
|
||||
column-related information and configuration.
|
||||
This is useful for frontend rendering because an
|
||||
element may need to know its rendering context. Knowing
|
||||
this information, an element can, for example, decide not to render the
|
||||
:html:`Header` partial if it is in a sidebar content area.
|
||||
|
||||
:php-short:`\TYPO3\CMS\Core\Page\ContentArea` objects are added to the
|
||||
view either by variable name defined in :typoscript:`contentAs` or,
|
||||
if not defined, `content`. Content elements can then be accessed via the
|
||||
:html:`records` property.
|
||||
|
||||
:php-short:`\TYPO3\CMS\Core\Page\ContentArea` objects contain
|
||||
backend layout-related configuration, such as
|
||||
:ref:`content restrictions <feature-108623-1768315053>`. These allow
|
||||
further validation such as whether a content type is
|
||||
valid.
|
||||
|
||||
Therefore :html:`{content.main.records}` can be used to get 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::
|
||||
|
||||
:php-short:`\TYPO3\CMS\Core\Page\ContentArea` objects are attached in
|
||||
the :php-short:`\TYPO3\CMS\Core\Page\ContentAreaCollection`, which
|
||||
implements the PSR-11 :php:`\Psr\Container\ContainerInterface` to allow
|
||||
access to the content areas using :php:`get()`. To optimize performance
|
||||
:php-short:`\TYPO3\CMS\Core\Page\ContentArea` objects are
|
||||
instantiated only when accessed (lazy loading).
|
||||
|
||||
Accessing a :php-short:`\TYPO3\CMS\Core\Page\ContentArea` using
|
||||
:html:`{content.main}` makes the following information available, as defined in
|
||||
the page layout:
|
||||
|
||||
* :html:`identifier` - The column identifier
|
||||
* :html:`colPos` - The defined `colPos`
|
||||
* :html:`name` - The descriptive `name`, which might be a locallang key
|
||||
* :html:`allowedContentTypes` - The defined `allowedContentTypes`
|
||||
* :html:`disallowedContentTypes` - The defined `disallowedContentTypes`
|
||||
* :html:`slideMode` - The defined :php:`ContentSlideMode`, which defaults to
|
||||
:php:`ContentSlideMode::None`
|
||||
* :html:`configuration` - The complete content area-related configuration
|
||||
* :html:`records` - The content elements as :php:`Record` objects
|
||||
|
||||
The following example renders the content elements of a page which has only
|
||||
a single column:
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
page = PAGE
|
||||
page.10 = PAGEVIEW
|
||||
page.10.paths.10 = EXT:my_site_package/Resources/Private/Templates/
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<f:for each="{content.main.records}" as="record">
|
||||
<f:render partial="ContentElement"
|
||||
arguments="{record: record, area: content.main}" />
|
||||
</f:for>
|
||||
|
||||
The introduction of the new
|
||||
:ref:`f:render.contentArea <feature-108726-1769071158>` and
|
||||
:ref:`f:render.record <feature-108726-1769503907>` ViewHelpers means that manually
|
||||
iterating over content elements is no longer necessary. All the content elements
|
||||
in a content area can be rendered with a single ViewHelper call:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<!-- Tag syntax -->
|
||||
<f:render.contentArea contentArea="{content.main}" />
|
||||
|
||||
<!-- Inline syntax -->
|
||||
{content.main -> f:render.contentArea()}
|
||||
|
||||
To render a single record, use the :html:`f:render.record` ViewHelper:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<!-- Tag syntax -->
|
||||
<f:render.record record="{content.main.records.0}" />
|
||||
|
||||
<!-- Inline syntax -->
|
||||
{content.main.records.0 -> f:render.record()}
|
||||
|
||||
.. note::
|
||||
|
||||
:php-short:`\TYPO3\CMS\Core\Page\ContentArea` helps the
|
||||
:php-short:`\TYPO3\CMS\Frontend\Event\AfterContentHasBeenFetchedEvent`
|
||||
to manipulate content elements in an area by
|
||||
providing context.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
It is now possible to access all the content elements on a page, grouped by their
|
||||
column, as well as having all the column-related information and
|
||||
configuration available. In addition to reduced configuration effort,
|
||||
different rendering is possible for an element depending on context.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
A content element template using a `Default` layout that renders the
|
||||
`Header` partial only if the content element is not in the `sidebar` column.
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<f:layout name="Default" />
|
||||
|
||||
<f:section name="Main">
|
||||
<f:if condition="{area.identifier} != 'sidebar'">
|
||||
<f:render partial="Header" arguments="{_all}" />
|
||||
</f:if>
|
||||
|
||||
<p>{record.text}</p>
|
||||
<f:image image="{record.image}" width="{area.configuration.imageWidth}" />
|
||||
</f:section>
|
||||
|
||||
.. index:: Frontend, ext:frontend
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-105084-1771501624:
|
||||
|
||||
=====================================================================
|
||||
Feature: #105084 - Add setting to configure indexed_search pagination
|
||||
=====================================================================
|
||||
|
||||
See :issue:`105084`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new TypoScript setting
|
||||
:typoscript:`plugin.tx_indexedsearch.settings.pagination_type` has been
|
||||
introduced to set the pagination implementation used by
|
||||
`EXT:indexed_search`.
|
||||
|
||||
Available values:
|
||||
|
||||
* :typoscript:`simple`: uses
|
||||
:php-short:`\TYPO3\CMS\Core\Pagination\SimplePagination` and renders
|
||||
all result pages.
|
||||
* :typoscript:`slidingWindow`: uses
|
||||
:php-short:`\TYPO3\CMS\Core\Pagination\SlidingWindowPagination` and
|
||||
limits the displayed page links as set in
|
||||
:typoscript:`plugin.tx_indexedsearch.settings.page_links`.
|
||||
|
||||
The default is :typoscript:`simple` to preserve existing behavior.
|
||||
Integrators can switch to :typoscript:`slidingWindow` to make
|
||||
:typoscript:`page_links` effective for indexed_search result browsing.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Integrators can now switch between core pagination implementations using
|
||||
TypoScript, without having to use custom PHP code.
|
||||
|
||||
Advanced, fully-customized pagination logic can still be implemented using
|
||||
:php-short:`\TYPO3\CMS\IndexedSearch\Event\ModifySearchResultSetsEvent`.
|
||||
|
||||
.. index:: Frontend, TypoScript, ext:indexed_search
|
||||
@@ -0,0 +1,80 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-105649-1743710535:
|
||||
|
||||
======================================================
|
||||
Feature: #105649 - New PSR-14 CustomFileSelectorsEvent
|
||||
======================================================
|
||||
|
||||
See :issue:`105649`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new PSR-14 event :php:`\TYPO3\CMS\Backend\Form\Event\CustomFileSelectorsEvent`
|
||||
has been added. It is dispatched in
|
||||
:php-short:`\TYPO3\CMS\Backend\Form\Container\FilesControlContainer`
|
||||
during the rendering of selectors for relations to `sys_file_references`.
|
||||
|
||||
To modify the selectors used to add files, the following methods are
|
||||
available:
|
||||
|
||||
* :php:`getSelectors()`: Get all selectors
|
||||
* :php:`setSelectors()`: Set all selectors
|
||||
* :php:`getJavascriptModules()`: Get all JavaScript modules
|
||||
* :php:`setJavascriptModules()`: Set all JavaScript modules
|
||||
* :php:`getTableName()`: Get the table name of the current record
|
||||
* :php:`getFieldName()`: Get the field name of the element
|
||||
* :php:`getDatabaseRow()`: Get the raw database row
|
||||
* :php:`getFieldConfig()`: Get the TCA configuration of the current field
|
||||
* :php:`getFileExtensionFilter()`: Get the allowed and disallowed file
|
||||
extensions
|
||||
* :php:`getFormFieldIdentifier()`: Get the DOM object ID used in the form
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
The corresponding event listener class:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyVendor\MyExtension\EventListener;
|
||||
|
||||
use TYPO3\CMS\Backend\Form\Event\CustomFileSelectorsEvent;
|
||||
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||
|
||||
#[AsEventListener(identifier: 'my-extension/custom-file-selector')]
|
||||
final class CustomFileSelectorEventListener
|
||||
{
|
||||
public function __construct(
|
||||
private CustomDamFileSelector $damFileSelector,
|
||||
) {}
|
||||
|
||||
public function __invoke(CustomFileSelectorsEvent $event): void
|
||||
{
|
||||
$result = $this->damFileSelector->renderFileSelector(
|
||||
$event->getFormFieldIdentifier(),
|
||||
);
|
||||
$event->setSelectors(array_merge(
|
||||
$event->getSelectors(),
|
||||
$result['control'],
|
||||
));
|
||||
$event->setJavascriptModules(array_merge(
|
||||
$event->getJavascriptModules(),
|
||||
$result['javaScriptModule'],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
It is now possible to modify file selectors using the new PSR-14 event
|
||||
:php-short:`\TYPO3\CMS\Backend\Form\Event\CustomFileSelectorsEvent`. This is
|
||||
especially useful for integrating a DAM system.
|
||||
|
||||
.. index:: Backend, PHP-API, ext:backend
|
||||
@@ -0,0 +1,236 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-105708-1739721600:
|
||||
|
||||
=============================================================
|
||||
Feature: #105708 - Multiple file upload for EXT:form elements
|
||||
=============================================================
|
||||
|
||||
See :issue:`105708`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The TYPO3 form framework now supports multiple file uploads in the
|
||||
:yaml:`FileUpload` and :yaml:`ImageUpload` form elements. This allows users
|
||||
to select and upload multiple files using a single form field.
|
||||
|
||||
The implementation follows the same security patterns as Extbase file upload
|
||||
handling. It uses HMAC-signed deletion requests to ensure secure file removal.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
To enable multiple file uploads for a form element, set the :yaml:`multiple`
|
||||
property to :yaml:`true` in your form definition:
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: fileadmin/form_definitions/someForm.yaml
|
||||
:emphasize-lines: 14-15,26-27
|
||||
|
||||
type: Form
|
||||
identifier: contact-form
|
||||
label: 'Contact Form'
|
||||
prototypeName: standard
|
||||
renderables:
|
||||
- type: Page
|
||||
identifier: page-1
|
||||
label: 'Page 1'
|
||||
renderables:
|
||||
- type: FileUpload
|
||||
identifier: attachments
|
||||
label: 'Attachments'
|
||||
properties:
|
||||
multiple: true
|
||||
allowRemoval: true
|
||||
saveToFileMount: '1:/user_upload/'
|
||||
allowedMimeTypes:
|
||||
- application/pdf
|
||||
- image/jpeg
|
||||
|
||||
- type: ImageUpload
|
||||
identifier: images
|
||||
label: 'Images'
|
||||
properties:
|
||||
multiple: true
|
||||
allowRemoval: true
|
||||
saveToFileMount: '1:/user_upload/'
|
||||
allowedMimeTypes:
|
||||
- image/jpeg
|
||||
- image/png
|
||||
|
||||
The :yaml:`multiple` option is also available in the Form Editor backend
|
||||
module as a checkbox in the element's inspector panel.
|
||||
|
||||
The :yaml:`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 :yaml:`Count` validator can now be used with
|
||||
:yaml:`FileUpload` and :yaml:`ImageUpload` elements to limit the number of
|
||||
uploaded files:
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: fileadmin/form_definitions/someForm.yaml
|
||||
|
||||
- type: FileUpload
|
||||
identifier: attachments
|
||||
label: 'Attachments'
|
||||
properties:
|
||||
multiple: true
|
||||
validators:
|
||||
- identifier: Count
|
||||
options:
|
||||
minimum: 1
|
||||
maximum: 5
|
||||
|
||||
Frontend rendering
|
||||
------------------
|
||||
|
||||
When :yaml:`multiple` is enabled:
|
||||
|
||||
* The file input field renders with the HTML5 :html:`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 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, :html:`<formvh:form.uploadDeleteCheckbox>`, is available
|
||||
for custom templates:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<formvh:form.uploadDeleteCheckbox
|
||||
property="{element.identifier}"
|
||||
fileReference="{file}"
|
||||
fileIndex="{iterator.index}"
|
||||
/>
|
||||
|
||||
Adapting custom finishers for multiple file uploads
|
||||
---------------------------------------------------
|
||||
|
||||
When :yaml:`multiple` is enabled on a :yaml:`FileUpload` element, the value
|
||||
returned by :php:`$formRuntime[$element->getIdentifier()]` is an
|
||||
:php:`ObjectStorage<FileReference>` instead of a single
|
||||
:php:`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
|
||||
:php:`EmailFinisher` and :php:`DeleteUploadsFinisher`:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Domain/Finishers/MyFinisher.php
|
||||
|
||||
use TYPO3\CMS\Core\Resource\FileInterface;
|
||||
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
use TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher;
|
||||
use TYPO3\CMS\Form\Domain\Model\FormElements\FileUpload;
|
||||
|
||||
class MyFinisher extends AbstractFinisher
|
||||
{
|
||||
protected function executeInternal(): 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 FileReference
|
||||
if ($file instanceof FileReference) {
|
||||
$this->processFile($file->getOriginalResource());
|
||||
}
|
||||
|
||||
// Multiple file upload: value is an ObjectStorage of FileReferences
|
||||
if ($file instanceof ObjectStorage) {
|
||||
foreach ($file as $singleFile) {
|
||||
if ($singleFile instanceof FileReference) {
|
||||
$this->processFile(
|
||||
$singleFile->getOriginalResource()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function processFile(FileInterface $file): void
|
||||
{
|
||||
// Your custom logic, e.g. move, copy, attach, etc.
|
||||
}
|
||||
}
|
||||
|
||||
Per-element validation with ObjectStorageElementValidatorInterface
|
||||
------------------------------------------------------------------
|
||||
|
||||
When a form field value is an
|
||||
:php-short:`\TYPO3\CMS\Extbase\Persistence\ObjectStorage`, for example, a
|
||||
multiple-file upload, the :php:`ProcessingRule` must decide how to call each
|
||||
registered validator:
|
||||
|
||||
* **Collection-level validators** (default) receive the entire
|
||||
:php-short:`\TYPO3\CMS\Extbase\Persistence\ObjectStorage`. Use this for
|
||||
validators that check the collection as a whole, such as
|
||||
:php-short:`\TYPO3\CMS\Form\Mvc\Validation\CountValidator` for the minimum
|
||||
or maximum number of items.
|
||||
* **Element-level validators** receive each item individually. Use this for
|
||||
validators that inspect a single item, such as
|
||||
:php-short:`\TYPO3\CMS\Form\Mvc\Validation\MimeTypeValidator` or
|
||||
:php-short:`\TYPO3\CMS\Form\Mvc\Validation\FileSizeValidator`.
|
||||
|
||||
To mark a validator as element-level, implement the marker interface
|
||||
:php-short:`\TYPO3\CMS\Form\Mvc\Validation\ObjectStorageElementValidatorInterface`:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Validation/MyPerFileValidator.php
|
||||
|
||||
use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator;
|
||||
use TYPO3\CMS\Form\Mvc\Validation\ObjectStorageElementValidatorInterface;
|
||||
|
||||
final class MyFileValidator extends AbstractValidator implements
|
||||
ObjectStorageElementValidatorInterface
|
||||
{
|
||||
public function isValid(mixed $value): void
|
||||
{
|
||||
// $value is a single element from the ObjectStorage,
|
||||
// e.g. a FileReference - not the whole collection.
|
||||
}
|
||||
}
|
||||
|
||||
For single-value fields, that is, non-
|
||||
:php-short:`\TYPO3\CMS\Extbase\Persistence\ObjectStorage` values, 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 :yaml:`FileUpload` and :yaml:`ImageUpload` elements support the new
|
||||
:yaml:`multiple` property
|
||||
* All existing finishers, `EmailFinisher`, `SaveToDatabaseFinisher`, and
|
||||
`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 list of filenames
|
||||
|
||||
.. index:: Frontend, ext:form
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-105742-1755084132:
|
||||
|
||||
=================================================================
|
||||
Feature: #105742 - Synchronized manipulation of all crop variants
|
||||
=================================================================
|
||||
|
||||
See :issue:`105742`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The image manipulation wizard allows images to be cropped to multiple
|
||||
crop variants. When many variants were present, each had to be edited
|
||||
individually, requiring the same changes to be applied multiple times.
|
||||
This was particularly tedious when an editor needed identical crop values
|
||||
across all image variants.
|
||||
|
||||
This feature introduces a checkbox that allows editors to crop all image variants
|
||||
simultaneously. This checkbox is available if all crop variants share
|
||||
identical aspect ratios and configuration (except for the title).
|
||||
|
||||
`excludeFromSync` is a new sub-option of the `cropVariants` TCA/TCEFORM
|
||||
configuration array which allows developers to exclude specific crop variants
|
||||
from synchronized cropping.
|
||||
|
||||
This is useful, for example, when adding a special crop variant for a list
|
||||
view that has a different configuration, while still allowing other crop
|
||||
variants to be synchronized.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
The following example defines standard crop variants for a Bootstrap-based
|
||||
template.
|
||||
|
||||
All crop variants are configured identically (except for the title), which
|
||||
enables synchronized cropping.
|
||||
|
||||
An additional crop variant for the `tx_news` list view is defined.
|
||||
The option `excludeFromSync = 1` ensures that this variant is excluded
|
||||
from synchronization.
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
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 custom aspect ratio definitions
|
||||
# (or identical aspect ratios, but not considered for
|
||||
# synchronized cropping)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Editors can now apply changes to image aspect ratios and cropping to
|
||||
multiple matching crop variants in the image manipulation wizard.
|
||||
|
||||
Specific `cropVariants` can be excluded from synchronization.
|
||||
|
||||
.. index:: Backend, ext:core
|
||||
@@ -0,0 +1,73 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-105827-1734338503:
|
||||
|
||||
===================================================================================================
|
||||
Feature: #105827 - Search in backend page tree and live search can find pages by their frontend URI
|
||||
===================================================================================================
|
||||
|
||||
See :issue:`105827` and :issue:`105833`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The backend page tree search functionality has been enhanced to allow users to
|
||||
enter a full URI such as `https://mysite.example.com/de/any/subtree/page/`,
|
||||
which shows the matching page in the result tree.
|
||||
|
||||
Multiple URIs can be separated with commas (`,`), just like multiple page IDs.
|
||||
|
||||
It is also possible to combine different search input:
|
||||
|
||||
.. code-block::
|
||||
:caption: Combining multiple search parts
|
||||
|
||||
4,8,https://example.com/first,http://sub.example.com/en/second,anyPageTitle
|
||||
|
||||
Matches in frontend URIs of translated pages are marked accordingly.
|
||||
|
||||
This functionality uses the PSR-14 event
|
||||
:php-short:`TYPO3\CMS\Backend\Tree\Repository\BeforePageTreeIsFilteredEvent`
|
||||
(see :ref:`feature-105833-1734420558`) and can serve as
|
||||
inspiration for custom search variations.
|
||||
|
||||
In addition, live search has been enhanced to perform the same lookup based on
|
||||
a single URI. This is achieved with the new PSR-14 event
|
||||
:php-short:`TYPO3\CMS\Backend\Search\Event\ModifyConstraintsForLiveSearchEvent`
|
||||
(see :ref:`feature-105827-1751912675`).
|
||||
|
||||
Live search returns both the default language page derived from the URI and the
|
||||
matching translated page.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Search by frontend URI is enabled by default and can be controlled in two ways,
|
||||
similar to :ref:`search by translation <feature-107961-1762076523>`:
|
||||
|
||||
User TSconfig
|
||||
-------------
|
||||
|
||||
Administrators can control the availability of frontend URI search with
|
||||
user TSconfig:
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
# Disable searching by frontend URI for specific users/groups
|
||||
options.pageTree.searchByFrontendUri = 0
|
||||
|
||||
User preference
|
||||
---------------
|
||||
|
||||
Individual backend users can toggle this setting in the page tree toolbar menu.
|
||||
The preference is stored in the backend user's configuration, allowing each
|
||||
user to customize search behavior.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Editors can now easily locate a backend page when only the frontend URI is
|
||||
available. Permissions to view or edit the page are respected. Invalid or
|
||||
non-matching URIs are ignored.
|
||||
|
||||
.. index:: Backend, ext:backend
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-105827-1751912675:
|
||||
|
||||
=================================================================
|
||||
Feature: #105827 - New PSR-14 ModifyConstraintsForLiveSearchEvent
|
||||
=================================================================
|
||||
|
||||
See :issue:`105827`, :issue:`105833`, :issue:`93494`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new PSR-14 event
|
||||
:php:`\TYPO3\CMS\Backend\Search\Event\ModifyConstraintsForLiveSearchEvent`
|
||||
has been added to TYPO3 Core. This event is dispatched in the
|
||||
:php-short:`TYPO3\CMS\Backend\Search\LiveSearch\LiveSearch` class and allows
|
||||
extensions to modify the
|
||||
:php-short:`TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression`
|
||||
constraints collected in an array before execution.
|
||||
|
||||
This makes it possible to add additional constraints to the main query
|
||||
constraints, combined with a logical `OR`. These constraints could
|
||||
not previously be accessed by the existing event
|
||||
:php-short:`TYPO3\CMS\Backend\Search\Event\ModifyQueryForLiveSearchEvent`.
|
||||
|
||||
The event provides the following methods:
|
||||
|
||||
* :php:`getConstraints()`: Returns the current array of query constraints
|
||||
(composite expressions).
|
||||
* :php:`addConstraint()`: Adds a single constraint.
|
||||
* :php:`addConstraints()`: Adds multiple new constraints.
|
||||
* :php:`getTableName()`: Returns the table for which the query is executed
|
||||
(for example, `pages` or `tt_content`).
|
||||
* :php:`getSearchDemand()`: Returns the search demand.
|
||||
|
||||
.. hint::
|
||||
|
||||
Constraints are intended to be added only. This ensures that
|
||||
security-related mandatory constraints added by Core or extensions cannot
|
||||
be negatively affected. For this reason, there is no way to remove a
|
||||
constraint after it has been added.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
The corresponding event listener class:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
namespace Vendor\MyPackage\Backend\EventListener;
|
||||
|
||||
use TYPO3\CMS\Backend\Search\Event\ModifyConstraintsForLiveSearchEvent;
|
||||
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
|
||||
final readonly class PageRecordProviderEnhancedSearch
|
||||
{
|
||||
public function __construct(
|
||||
private ConnectionPool $connectionPool,
|
||||
) {}
|
||||
|
||||
#[AsEventListener('my-package/livesearch-enhanced')]
|
||||
public function __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);
|
||||
}
|
||||
}
|
||||
|
||||
Core itself uses this event to allow searching for frontend URIs in the
|
||||
backend page tree.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
A new PSR-14 event is now available for adding constraints to the live search
|
||||
query. These constraints are combined with a logical `OR`.
|
||||
|
||||
.. index:: Backend, PHP-API, ext:backend
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-106153-1770150965:
|
||||
|
||||
========================================================================
|
||||
Feature: #106153 - Improve DebugExceptionHandler with copy functionality
|
||||
========================================================================
|
||||
|
||||
See :issue:`106153`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The debugging exception handler, which can be configured for backend and
|
||||
frontend error reporting, provides a large stack trace with details about
|
||||
an error.
|
||||
|
||||
This is often essential when reporting bugs in TYPO3 or debugging custom
|
||||
code.
|
||||
|
||||
The output has now been improved:
|
||||
|
||||
* Each stack trace segment's file name and the line number where the error
|
||||
occurred now has a "Copy path" button. Clicking it copies the full
|
||||
path, file name, and line number to the browser clipboard.
|
||||
|
||||
* The bottom of the page shows two buttons: one toggles the output above
|
||||
to hide or reveal the file contents, and the other copies the entire
|
||||
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 link is
|
||||
available to go from the top of the page to the export section.
|
||||
|
||||
.. hint::
|
||||
|
||||
The "copy to clipboard" functionality is based on JavaScript. Some
|
||||
browsers, such as Firefox, allow access to the clipboard only when
|
||||
the site is accessed via `https`. If copying fails, the condensed
|
||||
output that would have been written to the clipboard is shown instead
|
||||
in a box below so it can be copied manually.
|
||||
|
||||
Thanks to Olivier Dobberkau, whose extension
|
||||
`https://github.com/dkd-dobberkau/enhanced-error-handler`__ inspired the
|
||||
rework of this feature.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Errors and their stack traces can now be copied and forwarded much more
|
||||
easily for support requests, without the need to save an HTML file or take
|
||||
screenshots.
|
||||
|
||||
File names and line numbers of errors can also be copied easily and
|
||||
inserted into an IDE to jump directly to the relevant code.
|
||||
|
||||
.. index:: Backend, ext:core
|
||||
@@ -0,0 +1,104 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-106261-1762614000:
|
||||
|
||||
=========================================================================================
|
||||
Feature: #106261 - Align command line arguments of message consumer with Symfony original
|
||||
=========================================================================================
|
||||
|
||||
See :issue:`106261`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
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:
|
||||
|
||||
* :bash:`--limit` / :bash:`-l`: Limits the number of received messages.
|
||||
* :bash:`--failure-limit` / :bash:`-f`: Limits the number of failed
|
||||
messages the worker can consume.
|
||||
* :bash:`--memory-limit` / :bash:`-m`: Sets the memory limit available
|
||||
to the worker.
|
||||
* :bash:`--time-limit` / :bash:`-t`: Sets the time limit in seconds
|
||||
during which the worker can handle new messages.
|
||||
* :bash:`--bus` / :bash:`-b`: Specifies the name of the bus to which
|
||||
received messages are dispatched.
|
||||
* :bash:`--all`: Consumes messages from all receivers.
|
||||
* :bash:`--keepalive`: Uses the transport 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 normal
|
||||
request handling and consumed in the background by the scheduler task,
|
||||
improving application performance and user experience.
|
||||
|
||||
.. important::
|
||||
|
||||
The :bash:`messenger:consume` command blocks other scheduler tasks
|
||||
from executing while it is running. It is therefore strongly
|
||||
recommended to set the :bash:`--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.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Consume messages from a specific receiver:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vendor/bin/typo3 messenger:consume my_receiver
|
||||
|
||||
Consume messages with a message limit:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vendor/bin/typo3 messenger:consume my_receiver --limit=10
|
||||
|
||||
Stop the worker after 2 failed messages:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vendor/bin/typo3 messenger:consume my_receiver --failure-limit=2
|
||||
|
||||
Stop the worker when the memory limit is exceeded:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vendor/bin/typo3 messenger:consume my_receiver --memory-limit=128M
|
||||
|
||||
Stop the worker after a time limit:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vendor/bin/typo3 messenger:consume my_receiver --time-limit=3600
|
||||
|
||||
Consume from specific queues only:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vendor/bin/typo3 messenger:consume my_receiver --queues=fasttrack
|
||||
|
||||
Consume from all configured receivers:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vendor/bin/typo3 messenger:consume --all
|
||||
|
||||
.. index:: PHP-API, ext:core
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-106640-1766572100:
|
||||
|
||||
====================================================================
|
||||
Feature: #106640 - Localize enum labels in site settings definitions
|
||||
====================================================================
|
||||
|
||||
See :issue:`106640`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Enum option labels in site settings definitions can now be localized
|
||||
consistently.
|
||||
|
||||
This applies to all common enum declaration styles:
|
||||
|
||||
* List-style enum declarations derive localization keys using
|
||||
:code:`settings.<settingKey>.enum.<enumValue>` in the set labels file.
|
||||
* Map-style enum declarations are independent of that key schema. Only
|
||||
the configured label value is evaluated.
|
||||
* Map-style enum declarations with localization references
|
||||
(:code:`LLL:...`) resolve these references.
|
||||
* Map-style enum declarations with literal labels keep these labels
|
||||
as-is.
|
||||
* Map-style key-only enum entries fall back to the enum value.
|
||||
* Map-style empty string labels remain empty strings.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: List-style enum declaration in settings.definitions.yaml
|
||||
|
||||
settings:
|
||||
my.enumSetting:
|
||||
type: string
|
||||
default: optionA
|
||||
enum:
|
||||
- optionA
|
||||
- optionB
|
||||
|
||||
.. code-block:: xml
|
||||
:caption: Matching labels in labels.xlf
|
||||
|
||||
<trans-unit id="settings.my.enumSetting.enum.optionA">
|
||||
<source>Option A (localized)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="settings.my.enumSetting.enum.optionB">
|
||||
<source>Option B (localized)</source>
|
||||
</trans-unit>
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: Map-style enum declaration in settings.definitions.yaml
|
||||
|
||||
settings:
|
||||
my.enumSetting:
|
||||
type: string
|
||||
default: optionA
|
||||
enum:
|
||||
optionA: 'LLL:my_extension.labels:settings.custom.optionA' # Explicit LLL reference
|
||||
optionB: 'Literal Option B' # Literal label
|
||||
optionC: # Key-only map-style entry, falls back to enum value "optionC"
|
||||
optionD: '' # Empty label stays empty
|
||||
|
||||
.. code-block:: xml
|
||||
:caption: Referenced label in labels.xlf
|
||||
|
||||
<trans-unit id="settings.custom.optionA">
|
||||
<source>Option A (localized)</source>
|
||||
</trans-unit>
|
||||
|
||||
If you want to work with automatically derived keys in the set
|
||||
:file:`labels.xlf`, for example
|
||||
:code:`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.
|
||||
|
||||
.. index:: YAML, Backend, ext:core
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-106681-1740000000:
|
||||
|
||||
=======================================================================
|
||||
Feature: #106681 - Support relative date formats in DateRange validator
|
||||
=======================================================================
|
||||
|
||||
See :issue:`106681`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :yaml:`DateRange` validator of the form extension now supports relative
|
||||
as well as 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 date is in the future.
|
||||
|
||||
The following relative expressions are supported and follow the syntax of
|
||||
:php:`strtotime()`:
|
||||
|
||||
* Named dates: :yaml:`today`, :yaml:`now`, :yaml:`yesterday`,
|
||||
:yaml:`tomorrow`
|
||||
* Relative offsets: :yaml:`-18 years`, :yaml:`+1 month`,
|
||||
:yaml:`-2 weeks`, :yaml:`+30 days`
|
||||
|
||||
These expressions can be used in the :yaml:`options.minimum` and
|
||||
:yaml:`options.maximum` properties of the :yaml:`DateRange` validator.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
Ensure that a date of birth is at least 18 years in the past:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
type: Date
|
||||
identifier: date-of-birth
|
||||
label: 'Date of birth'
|
||||
validators:
|
||||
-
|
||||
identifier: DateRange
|
||||
options:
|
||||
maximum: '-18 years'
|
||||
|
||||
Ensure that a date is in the future:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
type: Date
|
||||
identifier: event-date
|
||||
label: 'Event date'
|
||||
validators:
|
||||
-
|
||||
identifier: DateRange
|
||||
options:
|
||||
minimum: '+1 day'
|
||||
|
||||
Mixed absolute and relative dates are also supported:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
validators:
|
||||
-
|
||||
identifier: DateRange
|
||||
options:
|
||||
minimum: '2020-01-01'
|
||||
maximum: 'today'
|
||||
|
||||
The form editor in the TYPO3 backend has been updated to accept these
|
||||
relative expressions in the date range fields. The HTML :html:`min` and
|
||||
:html:`max` attributes on the rendered
|
||||
:html:`<input type="date">` element are automatically resolved to
|
||||
absolute `Y-m-d` dates at render time.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Form integrators can now use relative date expressions in the
|
||||
:yaml:`DateRange` validator configuration. Existing form definitions
|
||||
using absolute dates will continue to work without changes.
|
||||
|
||||
.. index:: Frontend, Backend, ext:form
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-106828-1751343863:
|
||||
|
||||
=========================================================================
|
||||
Feature: #106828 - Add user TSconfig to define default live search action
|
||||
=========================================================================
|
||||
|
||||
See :issue:`106828`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new user TSconfig option :typoscript:`options.liveSearch.actions` has
|
||||
been introduced to allow integrators to define the default behavior of a
|
||||
search.
|
||||
|
||||
Available actions:
|
||||
|
||||
* `edit`: Opens the edit form of the record. This is the default for
|
||||
all tables except `pages`.
|
||||
* `layout`: Opens the page in the Page module. This is the default for
|
||||
the `pages` table.
|
||||
* `list`: Opens the storage page of the record in the Record List
|
||||
module.
|
||||
* `preview`: Opens the record in the frontend.
|
||||
|
||||
.. important::
|
||||
|
||||
The `layout` action can only be used for the :sql:`pages` and
|
||||
:sql:`tt_content` tables.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Set the default for all tables:
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
options.liveSearch.actions.default = edit
|
||||
|
||||
Set the default for the `tt_content` table:
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
options.liveSearch.actions.tt_content.default = layout
|
||||
|
||||
Set the default for a custom table:
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
options.liveSearch.actions.my_table.default = preview
|
||||
|
||||
.. note::
|
||||
|
||||
To use `preview` for a custom record, a valid preview configuration
|
||||
must exist for the table in `TCEMAIN.preview`.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The default actions of live search results can now be configured with
|
||||
user TSconfig. Integrators can define global and table-specific behavior
|
||||
for search results, improving backend workflows.
|
||||
|
||||
The default behavior for `pages` has been changed to `layout` to improve
|
||||
the user workflow.
|
||||
|
||||
.. index:: Backend, TSConfig, ext:backend
|
||||
@@ -0,0 +1,71 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-107003-1751223220:
|
||||
|
||||
===============================================================
|
||||
Feature: #107003 - Add event to change record data in list view
|
||||
===============================================================
|
||||
|
||||
See :issue:`107003`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new PSR-14 event
|
||||
:php:`\TYPO3\CMS\Backend\RecordList\Event\AfterRecordListRowPreparedEvent`
|
||||
has been added. This event is dispatched in
|
||||
:php-short:`TYPO3\CMS\Backend\RecordList\DatabaseRecordList` and allows
|
||||
extensions to modify the data used to render a single record in the list
|
||||
view.
|
||||
|
||||
The event allows the following properties to be modified:
|
||||
|
||||
* `data`: The row fields as an array. The following fields are available:
|
||||
|
||||
* `_SELECTOR_`: The checkbox element
|
||||
* `icon`: The icon
|
||||
* `__label`: Special field that contains the header
|
||||
* `_CONTROL_`: The row action buttons
|
||||
* `_LOCALIZATION_`: The current language
|
||||
* `_LOCALIZATION_b`: The translated language
|
||||
* `rowDescription`: The row description
|
||||
* `header`: The header. This field is used only if `__label` is not
|
||||
set. Use `__label` instead.
|
||||
* `uid`: The record UID (read-only)
|
||||
|
||||
* `tagAttributes`: The HTML tag attributes of the row. The following
|
||||
attributes are available:
|
||||
|
||||
* `class`
|
||||
* `data-table`
|
||||
* `title`
|
||||
|
||||
The corresponding event listener class:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Backend\RecordList\Event\AfterRecordListRowPreparedEvent;
|
||||
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||
|
||||
#[AsEventListener('my-package/backend/my-listener-name')]
|
||||
final class MyEventListener
|
||||
{
|
||||
public function __invoke(AfterRecordListRowPreparedEvent $event): void
|
||||
{
|
||||
$data = $event->getData();
|
||||
$tagAttributes = $event->getTagAttributes();
|
||||
|
||||
// Modify the row data and tag attributes here.
|
||||
|
||||
$event->setData($data);
|
||||
$event->setTagAttributes($tagAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The new PSR-14 event can be used, for example, to modify the title link in
|
||||
the record list.
|
||||
|
||||
.. index:: PHP-API, ext:backend
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-107058-1769168658:
|
||||
|
||||
=================================================================
|
||||
Feature: #107058 - Simplify registration of a custom form element
|
||||
=================================================================
|
||||
|
||||
See :issue:`107058`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The registration of custom form elements in the TYPO3 Form Framework has
|
||||
been simplified. Previously, registering a custom form
|
||||
element required subscribing to the JavaScript event
|
||||
:js:`view/stage/abstract/render/template/perform` to render the element
|
||||
in the Form Editor stage area.
|
||||
|
||||
Custom form elements can now be registered without
|
||||
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, omit the
|
||||
:yaml:`formEditorPartials` configuration in your form element's YAML
|
||||
definition. The Form Editor then automatically renders the element using
|
||||
the built-in :html:`<typo3-form-form-element-stage-item>` web component,
|
||||
which provides:
|
||||
|
||||
* Element type and identifier display
|
||||
* Element label with required indicator
|
||||
* Validator 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 the form element configuration without requiring a
|
||||
custom template or JavaScript code.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Extension developers can now register custom form elements with minimal
|
||||
configuration. By omitting the :yaml:`formEditorPartials`
|
||||
configuration, the Form Editor automatically renders the element using a
|
||||
generic Web Component, eliminating the need for:
|
||||
|
||||
* Custom Fluid templates in
|
||||
:file:`Resources/Private/Backend/Partials/FormEditor/Stage/`
|
||||
* Custom JavaScript code subscribing to
|
||||
:js:`view/stage/abstract/render/template/perform`
|
||||
* Manual element rendering logic
|
||||
|
||||
This significantly reduces the complexity and maintenance burden when
|
||||
creating custom form elements that do not require special visualization
|
||||
in the Form Editor.
|
||||
|
||||
For custom form elements that require specialized rendering or custom
|
||||
interactions in the stage area, the :yaml:`formEditorPartials`
|
||||
configuration can still be used to provide custom Fluid templates, which
|
||||
continue to 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>`.
|
||||
|
||||
.. index:: Backend, ext:form
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-107289-1734172800:
|
||||
|
||||
==================================================================
|
||||
Feature: #107289 - Automatic history tracking for Extbase entities
|
||||
==================================================================
|
||||
|
||||
See :issue:`107289`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
TYPO3 now tracks the history of all Extbase domain entities by
|
||||
listening to Extbase persistence events and storing them in the
|
||||
:sql:`sys_history` table. This provides a comprehensive audit trail for
|
||||
all frontend and backend operations on Extbase entities without requiring
|
||||
any code changes.
|
||||
|
||||
The feature leverages TYPO3's existing
|
||||
:php-short:`TYPO3\CMS\Backend\History\RecordHistoryStore`
|
||||
infrastructure and integrates seamlessly with the backend record history
|
||||
functionality.
|
||||
|
||||
The history tracking captures:
|
||||
|
||||
* Create operations: when entities are persisted for the first time
|
||||
* Update operations: when existing entities are modified
|
||||
* Delete operations: when entities are removed from persistence
|
||||
|
||||
All operations are tracked with their proper user context (frontend users,
|
||||
backend users, anonymous operations) and include full entity data
|
||||
snapshots.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
History tracking is **disabled** by default. It can be enabled with the
|
||||
feature toggle `extbase.enableHistoryTracking` (available via
|
||||
:guilabel:`System > Settings > Feature toggles`).
|
||||
|
||||
Once the feature toggle is enabled, history tracking is active for all
|
||||
Extbase domain model storage tables. It can then be **disabled** via TCA
|
||||
on a per-table basis:
|
||||
|
||||
.. code-block:: php
|
||||
:emphasize-lines: 11-13
|
||||
:caption: EXT:my_extension/Configuration/TCA/tx_myextension_domain_model_blog.php
|
||||
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'ctrl' => [
|
||||
'title' => 'my_extension.messages:my_title',
|
||||
'label' => 'uid',
|
||||
'tstamp' => 'tstamp',
|
||||
'crdate' => 'crdate',
|
||||
'delete' => 'deleted',
|
||||
// ...
|
||||
'extbase' => [
|
||||
'enableHistoryTracking' => false,
|
||||
],
|
||||
],
|
||||
'columns' => [
|
||||
// ...
|
||||
],
|
||||
];
|
||||
|
||||
Defining this at the TCA level (instead of TypoScript `persistence`
|
||||
configuration) means that it can be configured per table and
|
||||
evaluated consistently in all contexts (backend, frontend, CLI).
|
||||
|
||||
If a third-party extension enables history tracking via TCA, it can be
|
||||
disabled using TCA overrides. Disabling the feature toggle also disables
|
||||
all history tracking, even for tables configured with
|
||||
`enableHistoryTracking => true`.
|
||||
|
||||
In addition, the following PSR-14 event listeners can be deregistered or
|
||||
replaced at instance level:
|
||||
|
||||
* `extbase-history-tracker-persisted`
|
||||
* `extbase-history-tracker-updated`
|
||||
* `extbase-history-tracker-removed`
|
||||
|
||||
.. note::
|
||||
|
||||
Enabling history tracking can generate a large number of history
|
||||
entries for Extbase entities. These entries are mixed with regular
|
||||
editorial changes made in the TYPO3 backend (FormEngine).
|
||||
|
||||
.. important::
|
||||
|
||||
All changes to Extbase entity data are logged, including full initial
|
||||
data snapshots. This may have implications for GDPR / DSGVO and other
|
||||
security-related data handling requirements. Data may need to be
|
||||
pruned regularly. It is advisable to disable history tracking for
|
||||
tables containing sensitive data. For this reason, the feature toggle
|
||||
is disabled by default and requires explicit activation.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Changes to all Extbase domain entities can now be tracked
|
||||
in the :sql:`sys_history` table, making them visible in the
|
||||
backend record history. This requires enabling the feature toggle
|
||||
`extbase.enableHistoryTracking` (default: `false`).
|
||||
|
||||
This feature provides administrators and developers with full visibility
|
||||
into data changes without requiring interface implementations or code
|
||||
modifications.
|
||||
|
||||
Technical details
|
||||
=================
|
||||
|
||||
The implementation consists of a PSR-14 event listener
|
||||
:php-short:`TYPO3\CMS\Extbase\EventListener\ExtbaseHistoryTracker`
|
||||
which automatically registers for the following Extbase persistence
|
||||
events:
|
||||
|
||||
* :php-short:`TYPO3\CMS\Extbase\Event\Persistence\EntityAddedToPersistenceEvent`
|
||||
* :php-short:`TYPO3\CMS\Extbase\Event\Persistence\EntityUpdatedInPersistenceEvent`
|
||||
* :php-short:`TYPO3\CMS\Extbase\Event\Persistence\EntityRemovedFromPersistenceEvent`
|
||||
|
||||
All entities with valid TCA configuration are tracked automatically. This
|
||||
uses the Extbase DataMap API, TCA Schema API, and RecordHistoryStore API.
|
||||
|
||||
.. index:: PHP-API, ext:extbase
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-107771-1760360529:
|
||||
|
||||
====================================================================
|
||||
Feature: #107771 - Make rel attribute in external links configurable
|
||||
====================================================================
|
||||
|
||||
See :issue:`107771`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
For security reasons, external links that open in a new window should be
|
||||
generated with :code:`rel="noopener"` to prevent the opened page from
|
||||
accessing the originating document via JavaScript's
|
||||
:code:`Window.opener` object.
|
||||
|
||||
TYPO3's default behavior is to add :code:`rel="noreferrer"` to all such
|
||||
links. This automatically implies :code:`rel="noopener"` but is even more
|
||||
restrictive, as it also prevents the HTTP `Referer` header from being sent to
|
||||
the opened page. This may be too strict and therefore undesirable for some
|
||||
website owners.
|
||||
|
||||
This feature introduces a new TypoScript option
|
||||
:code:`config.linkSecurityRelValue` to define the :code:`rel`
|
||||
attribute for external links. The default behavior remains
|
||||
:code:`rel="noreferrer"`, but by setting the TypoScript property to
|
||||
:code:`noopener`, all external links are generated with
|
||||
:code:`rel="noopener"` instead.
|
||||
|
||||
The feature respects existing the individual settings of a link. Any existing
|
||||
:code:`rel="noopener"` and :code:`rel="noreferrer"` values from other
|
||||
sources are preserved.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
A new TypoScript configuration option
|
||||
:code:`config.linkSecurityRelValue` is available and can be set to
|
||||
`noreferrer` (default) or `noopener`.
|
||||
|
||||
This setting affects all external links with :code:`target="_blank"`.
|
||||
|
||||
.. index:: Frontend
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-107802-1770827507:
|
||||
|
||||
=========================================================================
|
||||
Feature: #107802 - Support username and password in Redis session backend
|
||||
=========================================================================
|
||||
|
||||
See :issue:`107802`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Since Redis 6.0, it is possible to authenticate against Redis using
|
||||
both a username and a password. Before that, authentication was possible
|
||||
by password only. This change means the TYPO3 Redis session backend
|
||||
can be configured as follows:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: config/system/additional.php
|
||||
|
||||
use TYPO3\CMS\Core\Session\Backend\RedisSessionBackend;
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['session']['BE'] = [
|
||||
'backend' => RedisSessionBackend::class,
|
||||
'options' => [
|
||||
'database' => 0,
|
||||
'hostname' => 'redis',
|
||||
'port' => 6379,
|
||||
'username' => 'redis',
|
||||
'password' => 'redis',
|
||||
],
|
||||
];
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The `password` configuration option of the Redis session backend is now
|
||||
typed as `array|string`. Setting this configuration option to an array is
|
||||
deprecated and will be removed in TYPO3 v15.0.
|
||||
|
||||
.. index:: LocalConfiguration, ext:core
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-107826-1766220191:
|
||||
|
||||
===================================================================
|
||||
Feature: #107826 - Introduce Extbase action authorization attribute
|
||||
===================================================================
|
||||
|
||||
See :issue:`107826`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new authorization mechanism has been introduced for Extbase controller
|
||||
actions using PHP attributes. Extension authors can now implement
|
||||
declarative access control logic on action methods using the
|
||||
:php:`#[Authorize]` attribute.
|
||||
|
||||
The :php:`#[Authorize]` attribute supports multiple authorization
|
||||
strategies:
|
||||
|
||||
**Built-in checks:**
|
||||
|
||||
* Require frontend user login via :php:`requireLogin`
|
||||
* Require specific frontend user groups via :php:`requireGroups`
|
||||
|
||||
**Custom authorization logic:**
|
||||
|
||||
* Dedicated authorization class (recommended for complex logic)
|
||||
* Public controller method (for simple checks)
|
||||
|
||||
Multiple :php:`#[Authorize]` attributes can be stacked on a single
|
||||
action. All authorization checks must pass for access to be granted. If
|
||||
a check fails, a
|
||||
:php-short:`\TYPO3\CMS\Core\Http\PropagateResponseException` is thrown with an
|
||||
HTTP 403 response, which stops the Extbase dispatch process.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Require frontend user login
|
||||
---------------------------
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Controller/MyController.php
|
||||
|
||||
namespace MyVendor\MyExtension\Controller;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TYPO3\CMS\Extbase\Attribute\Authorize;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
|
||||
class MyController extends ActionController
|
||||
{
|
||||
#[Authorize(requireLogin: true)]
|
||||
public function listAction(): ResponseInterface
|
||||
{
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
}
|
||||
|
||||
Require specific user groups
|
||||
----------------------------
|
||||
|
||||
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 group (OR logic).
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Controller/MyController.php
|
||||
|
||||
namespace MyVendor\MyExtension\Controller;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TYPO3\CMS\Extbase\Attribute\Authorize;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
|
||||
class MyController extends ActionController
|
||||
{
|
||||
// Recommended: Use group UIDs
|
||||
#[Authorize(requireGroups: [1, 2])]
|
||||
public function adminListAction(): ResponseInterface
|
||||
{
|
||||
// Only accessible to users in groups 1 or 2
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
|
||||
// Alternative: Use group titles (not recommended)
|
||||
#[Authorize(requireGroups: ['administrators', 'editors'])]
|
||||
public function editorListAction(): ResponseInterface
|
||||
{
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
|
||||
// Mixed: UIDs and titles can be combined (not recommended)
|
||||
#[Authorize(requireGroups: [1, 'editors'])]
|
||||
public function mixedListAction(): ResponseInterface
|
||||
{
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
}
|
||||
|
||||
.. 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. The class must be publicly available in the DI container,
|
||||
which can be achieved by annotating it with
|
||||
:php:`#[Autoconfigure(public: true)]`.
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Authorization/MyObjectAuthorization.php
|
||||
|
||||
namespace MyVendor\MyExtension\Authorization;
|
||||
|
||||
use MyVendor\MyExtension\Domain\Model\MyObject;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
|
||||
#[Autoconfigure(public: true)]
|
||||
class MyObjectAuthorization
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly Context $context,
|
||||
) {}
|
||||
|
||||
public function checkOwnership(MyObject $myObject): bool
|
||||
{
|
||||
$userAspect = $this->context->getAspect('frontend.user');
|
||||
return $myObject->getOwner()->getUid()
|
||||
=== $userAspect->get('id');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Controller/MyController.php
|
||||
|
||||
namespace MyVendor\MyExtension\Controller;
|
||||
|
||||
use MyVendor\MyExtension\Authorization\MyObjectAuthorization;
|
||||
use MyVendor\MyExtension\Domain\Model\MyObject;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TYPO3\CMS\Extbase\Attribute\Authorize;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
|
||||
class MyController extends ActionController
|
||||
{
|
||||
#[Authorize(callback: [MyObjectAuthorization::class, 'checkOwnership'])]
|
||||
public function editAction(MyObject $myObject): ResponseInterface
|
||||
{
|
||||
$this->view->assign('myObject', $myObject);
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
|
||||
#[Authorize(callback: [MyObjectAuthorization::class, 'checkOwnership'])]
|
||||
public function deleteAction(MyObject $myObject): ResponseInterface
|
||||
{
|
||||
// Delete the object
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
}
|
||||
|
||||
Public controller method
|
||||
------------------------
|
||||
|
||||
For simple checks, a public controller method can be used as a callback.
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Controller/MyController.php
|
||||
|
||||
namespace MyVendor\MyExtension\Controller;
|
||||
|
||||
use MyVendor\MyExtension\Domain\Model\MyObject;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Extbase\Attribute\Authorize;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
|
||||
class MyController extends ActionController
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly Context $context,
|
||||
) {}
|
||||
|
||||
#[Authorize(callback: 'checkOwnership')]
|
||||
public function editAction(MyObject $myObject): ResponseInterface
|
||||
{
|
||||
$this->view->assign('myObject', $myObject);
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
|
||||
public function checkOwnership(MyObject $myObject): bool
|
||||
{
|
||||
$userAspect = $this->context->getAspect('frontend.user');
|
||||
return $myObject->getOwner()->getUid() === $userAspect->get('id');
|
||||
}
|
||||
}
|
||||
|
||||
Combining multiple authorization checks
|
||||
----------------------------------------
|
||||
|
||||
Multiple :php:`#[Authorize]` attributes can be stacked. All checks must
|
||||
pass.
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Controller/MyController.php
|
||||
|
||||
namespace MyVendor\MyExtension\Controller;
|
||||
|
||||
use MyVendor\MyExtension\Authorization\MyObjectAuthorization;
|
||||
use MyVendor\MyExtension\Domain\Model\MyObject;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TYPO3\CMS\Extbase\Attribute\Authorize;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
|
||||
class MyController extends ActionController
|
||||
{
|
||||
#[Authorize(requireLogin: true)]
|
||||
#[Authorize(requireGroups: [1, 2])]
|
||||
#[Authorize(callback: [MyObjectAuthorization::class, 'checkOwnership'])]
|
||||
public function editAction(MyObject $myObject): ResponseInterface
|
||||
{
|
||||
// Only accessible to logged-in users in groups 1 or 2 who own the object
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
}
|
||||
|
||||
Authorization checks can be combined within a single attribute:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
#[Authorize(requireLogin: true, requireGroups: [1, 2])]
|
||||
public function adminAction(): ResponseInterface
|
||||
{
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
|
||||
Customizing the authorization denied response
|
||||
----------------------------------------------
|
||||
|
||||
By default, the authorization check throws a
|
||||
:php-short:`TYPO3\CMS\Core\Http\PropagateResponseException` with an HTTP
|
||||
403 response. This response can be handled by the TYPO3 page error
|
||||
handler configured in site settings.
|
||||
|
||||
The PSR-14 event
|
||||
:php-short:`TYPO3\CMS\Extbase\Event\Mvc\BeforeActionAuthorizationDeniedEvent`
|
||||
can be used to provide a custom PSR-7 response, which is then returned by
|
||||
Extbase.
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/EventListener/CustomAuthorizationResponseListener.php
|
||||
|
||||
namespace MyVendor\MyExtension\EventListener;
|
||||
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use TYPO3\CMS\Extbase\Authorization\AuthorizationFailureReason;
|
||||
use TYPO3\CMS\Extbase\Event\Mvc\BeforeActionAuthorizationDeniedEvent;
|
||||
|
||||
final class CustomAuthorizationResponseListener
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ResponseFactoryInterface $responseFactory,
|
||||
private readonly StreamFactoryInterface $streamFactory,
|
||||
) {}
|
||||
|
||||
public function __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);
|
||||
}
|
||||
}
|
||||
|
||||
Security considerations
|
||||
=======================
|
||||
|
||||
.. warning::
|
||||
|
||||
When using the
|
||||
:php-short:`TYPO3\CMS\Extbase\Event\Mvc\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 changes could lead to inconsistent data.
|
||||
|
||||
* Do not use Extbase persistence (for example, repository
|
||||
operations or 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. Ensure proper cache configuration when customizing
|
||||
authorization responses.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Extension authors can now implement secure, declarative authorization
|
||||
checks for Extbase controller actions using the
|
||||
:php:`#[Authorize]` attribute.
|
||||
|
||||
.. index:: PHP-API, ext:extbase
|
||||
@@ -0,0 +1,45 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-107887-1761641914:
|
||||
|
||||
=====================================================
|
||||
Feature: #107887 - New "Latest backend logins" widget
|
||||
=====================================================
|
||||
|
||||
See :issue:`107887`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new dashboard widget :guilabel:`Latest backend logins` has been
|
||||
introduced to display recent backend user logins in
|
||||
the TYPO3 Dashboard. This allows administrators to quickly monitor user
|
||||
activity and track recent backend access patterns without navigating
|
||||
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 login time
|
||||
|
||||
Key benefits:
|
||||
|
||||
* Displays recent backend user logins with user details and
|
||||
timestamps
|
||||
* Provides direct access to login monitoring without navigating the
|
||||
system log
|
||||
* 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 of recent backend user activity in the
|
||||
dashboard.
|
||||
|
||||
.. index:: Backend, ext:dashboard, Security
|
||||
@@ -0,0 +1,45 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-107906-1761739282:
|
||||
|
||||
===================================================
|
||||
Feature: #107906 - Recently opened documents widget
|
||||
===================================================
|
||||
|
||||
See :issue:`107906`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new `Recently Opened Documents` dashboard widget has been introduced to
|
||||
display documents that are currently open or 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 filtered out to maintain data accuracy.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
This feature improves editorial efficiency by providing immediate access to
|
||||
recently opened documents in the dashboard interface, reducing
|
||||
the time spent navigating through the backend to resume work.
|
||||
|
||||
.. index:: Backend, ext:dashboard, Usability
|
||||
@@ -0,0 +1,48 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-107940-1761853022:
|
||||
|
||||
============================================================
|
||||
Feature: #107940 - Introduce report about content type usage
|
||||
============================================================
|
||||
|
||||
See :issue:`107940`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new :guilabel:`Content statistics` module has been introduced in the TYPO3
|
||||
backend under :guilabel:`Reports`. This module provides information 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.
|
||||
All the associated fields of the element are listed, including key details such
|
||||
as:
|
||||
|
||||
* The field type
|
||||
* Whether it is marked as required
|
||||
* Whether it can be configured as excludable via user group permissions
|
||||
|
||||
Detail view
|
||||
===========
|
||||
|
||||
The detail view of each content element type lists all the relevant 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 belong to specific content element types
|
||||
* Gain insights into the overall configuration and diversity of content
|
||||
elements
|
||||
|
||||
.. index:: Backend, ext:reports
|
||||
@@ -0,0 +1,301 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108345-1774117214:
|
||||
|
||||
==========================================================================
|
||||
Feature: #108345 - Allow extensions without ext_emconf.php in classic mode
|
||||
==========================================================================
|
||||
|
||||
See :issue:`108345`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Initially :file:`ext_emconf.php` was the only file providing
|
||||
extension metadata. Since the introduction of :file:`composer.json`,
|
||||
now mandatory for extensions,
|
||||
there are now two files containing a lot of redundant data.
|
||||
|
||||
This is now resolved by allowing an extension's :file:`composer.json`
|
||||
to contain information that was previously defined in
|
||||
:file:`ext_emconf.php`:
|
||||
|
||||
1. Extension title and description
|
||||
2. Extension version
|
||||
3. Extension state / update exclusion
|
||||
4. Dependencies on other TYPO3 extensions
|
||||
5. PHP version constraints
|
||||
|
||||
Extension title and description
|
||||
-------------------------------
|
||||
|
||||
See :ref:`feature-108653-1767199420` for how the extension title and description can be set
|
||||
individually in :file:`composer.json`.
|
||||
|
||||
Extension version
|
||||
-----------------
|
||||
|
||||
The version number can be set in `extra.typo3/cms.version` or alternatively
|
||||
in the `"version"` field in :file:`composer.json`.
|
||||
|
||||
For third-party extensions to be compatible with TYPO3 classic mode,
|
||||
this version must now be set to the same version previously defined in :file:`ext_emconf.php`
|
||||
and should match the version in the Git tag, for example when publishing to Packagist.
|
||||
|
||||
Fixture extensions used in tests can set any version number, for example `1.0.0`,
|
||||
but a version number must still be provided to avoid deprecation messages.
|
||||
During testing the version number is not evaluated.
|
||||
|
||||
TYPO3 Core extensions may omit the version number
|
||||
in :file:`composer.json` because their version number is derived via
|
||||
:php:`TYPO3\CMS\Core\Information\Typo3Version`.
|
||||
|
||||
Extension state and update exclusion
|
||||
------------------------------------
|
||||
|
||||
The former `state` property in :file:`ext_emconf.php` was used for multiple purposes.
|
||||
In :file:`composer.json`, this is now represented by dedicated metadata instead
|
||||
of a single field.
|
||||
|
||||
Supported extension stability values are expressed as version suffixes, for example:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"name": "vendor/example",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example_extension",
|
||||
"version": "1.2.3-alpha4",
|
||||
"Package": {
|
||||
"providesPackages": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Supported Composer stability values are:
|
||||
|
||||
* `dev`
|
||||
* `alpha`
|
||||
* `beta`
|
||||
* `RC`
|
||||
* `stable`
|
||||
|
||||
For example:
|
||||
|
||||
* `1.2.3-dev`
|
||||
* `1.2.3-alpha1`
|
||||
* `1.2.3-beta2`
|
||||
* `1.2.3-RC3`
|
||||
* `1.2.3`
|
||||
|
||||
Values from the former `state` field that are not supported by Composer stability
|
||||
can be expressed as build metadata by appending `+...` to the version string.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"name": "vendor/example",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example_extension",
|
||||
"version": "1.4.2+obsolete",
|
||||
"Package": {
|
||||
"providesPackages": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
In this example, TYPO3 will treat the version as `1.0.0`, keep `obsolete`
|
||||
as build metadata, and expose it in the Extension Manager.
|
||||
|
||||
The former `state = excludeFromUpdates` value from :file:`ext_emconf.php`
|
||||
is now represented by a dedicated boolean flag in :file:`composer.json`:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"name": "vendor/example",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example_extension",
|
||||
"version": "1.2.3",
|
||||
"exclude-from-updates": true,
|
||||
"Package": {
|
||||
"providesPackages": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
This replaces overloading the former `state` field for update handling.
|
||||
|
||||
Dependencies on other TYPO3 extensions
|
||||
--------------------------------------
|
||||
|
||||
:file:`ext_emconf.php` had a property for specifying dependencies
|
||||
on other extensions by referencing the extension key and an optional
|
||||
range of versions.
|
||||
|
||||
:file:`composer.json` also contains a field for specifying dependencies
|
||||
using a Composer package name with a version range.
|
||||
However, there is no direct way to distinguish whether such a package name
|
||||
refers to another TYPO3 extension or to a regular Composer package
|
||||
that should be installed from Packagist.
|
||||
|
||||
TYPO3, however, needs to know which other extensions an extension depends on
|
||||
in order to resolve the extension loading order correctly.
|
||||
|
||||
Therefore, TYPO3 must know which package names refer to TYPO3 extensions
|
||||
and which refer to regular Composer packages. In Composer mode, this can
|
||||
be resolved automatically.
|
||||
|
||||
In classic mode, TYPO3 now recognizes several categories:
|
||||
|
||||
* TYPO3 framework packages shipped by the core
|
||||
* Composer packages already installed and shipped with TYPO3
|
||||
* Composer packages provided by other loaded extensions via
|
||||
`providesPackages`
|
||||
|
||||
Because of this, extension authors do not need to repeat such package names
|
||||
in `providesPackages`.
|
||||
|
||||
Extensions still need to declare Composer packages that they themselves provide
|
||||
when loaded in classic mode. For those entries, `providesPackages` can also
|
||||
define a relative path to a Composer vendor directory. If that directory contains
|
||||
a Composer-generated `autoload.php`, TYPO3 includes it early during bootstrap.
|
||||
|
||||
This makes it possible to both declare Composer packages and bootstrap
|
||||
their autoloader in a standardized way.
|
||||
|
||||
Here is an example of an extension that ships a local Composer vendor directory:
|
||||
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"name": "vendor/example",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"require": {
|
||||
"typo3/cms-core": "^14.2",
|
||||
"vendor/other-example": "*",
|
||||
"symfony/dotenv": "^8.0"
|
||||
},
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example_extension",
|
||||
"version": "1.2.3",
|
||||
"Package": {
|
||||
"providesPackages": {
|
||||
"symfony/dotenv": "Resources/Private/Php/ComposerVendor"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
In this example, the package `symfony/dotenv` is provided by the extension itself
|
||||
in TYPO3 classic mode, and TYPO3 will include
|
||||
`Resources/Private/Php/ComposerVendor/autoload.php` early if it is a
|
||||
Composer-generated autoload file.
|
||||
|
||||
The Composer package names `typo3/cms-core` and `vendor/other-example`
|
||||
are assumed to refer to TYPO3 extensions, and TYPO3 guarantees that `vendor/example`
|
||||
is loaded after `vendor/other-example`. Otherwise, an error is thrown if
|
||||
the extension `vendor/other-example` does not exist in the system.
|
||||
|
||||
Packages that are already shipped by TYPO3 or already provided by another loaded
|
||||
extension do not need to be listed in `providesPackages`.
|
||||
|
||||
Even if an extension does not depend on any Composer packages,
|
||||
it is still **required** to specify `providesPackages` in :file:`composer.json`
|
||||
as an empty object to ensure future compatibility with TYPO3 classic mode
|
||||
and to avoid deprecation messages in TYPO3 v14.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"name": "vendor/example",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"require": {
|
||||
"typo3/cms-core": "^14.2",
|
||||
"vendor/other-example": "*"
|
||||
},
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example_extension",
|
||||
"version": "1.2.3",
|
||||
"Package": {
|
||||
"providesPackages": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PHP version constraints
|
||||
-----------------------
|
||||
|
||||
PHP version constraints from :file:`ext_emconf.php` can also be represented in
|
||||
the `require` section of :file:`composer.json`.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"name": "vendor/example",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Example extension",
|
||||
"require": {
|
||||
"typo3/cms-core": "^14.2",
|
||||
"php": "^8.2"
|
||||
},
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "example_extension",
|
||||
"version": "1.5.6",
|
||||
"Package": {
|
||||
"providesPackages": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The PHP dependency is kept as package metadata so TYPO3 classic mode
|
||||
can still evaluate PHP version requirements. However, it is ignored for
|
||||
extension dependency ordering.
|
||||
|
||||
Be aware that keeping :file:`ext_emconf.php`, while no longer directly required
|
||||
by TYPO3, may still be necessary for some tools,
|
||||
such as Tailor or TYPO3 TER. Therefore, for the time being, it is recommended
|
||||
to keep the file and ensure that its information stays in sync
|
||||
with :file:`composer.json` as outlined above.
|
||||
|
||||
However, TYPO3 will **not** evaluate :file:`ext_emconf.php` anymore if the required
|
||||
metadata is correctly defined in :file:`composer.json` and package metadata can be
|
||||
derived from it.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Extensions can now omit :file:`ext_emconf.php` in TYPO3 classic mode.
|
||||
A deprecation message is shown during cache warm-up when :file:`ext_emconf.php`
|
||||
is present and :file:`composer.json` is not yet future-proof
|
||||
because it does not contain the required metadata definitions.
|
||||
|
||||
.. index:: ext:core
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108557-1768611915:
|
||||
|
||||
===============================================================
|
||||
Feature: #108557 - TCA option allowedRecordTypes for page types
|
||||
===============================================================
|
||||
|
||||
See :issue:`108557`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new TCA option :php:`allowedRecordTypes` is introduced for page types to
|
||||
configure which database tables are allowed for specific types (`doktype`).
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Configuration/TCA/Overrides/pages.php
|
||||
|
||||
// Allow any record on that page type.
|
||||
$GLOBALS['TCA']['pages']['types']['116']['allowedRecordTypes'] = ['*'];
|
||||
|
||||
// Allow only specific tables on that page type.
|
||||
$GLOBALS['TCA']['pages']['types']['116']['allowedRecordTypes'] = [
|
||||
'tt_content',
|
||||
'my_custom_record',
|
||||
];
|
||||
|
||||
The array can contain a list of table names or a single asterisk entry (`*`)
|
||||
to allow all record types.
|
||||
|
||||
By default, only the tables `pages`, `sys_category`, `sys_file_reference`, and
|
||||
`sys_file_collection` are allowed if this option is not overridden.
|
||||
|
||||
The defaults are extended if TCA tables enable the option
|
||||
`ctrl.security.ignorePageTypeRestriction`. Again, this is not considered if
|
||||
:php:`allowedRecordTypes` is set. These tables must then also be configured
|
||||
there.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The allowed record types for pages can now be configured in TCA. This
|
||||
centralizes the configuration for page types and further reduces the need for
|
||||
:file:`ext_tables.php`, which was used previously.
|
||||
|
||||
.. index:: TCA, ext:core
|
||||
@@ -0,0 +1,54 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108580-1734567890:
|
||||
|
||||
=======================================================
|
||||
Feature: #108580 - Improved page module content preview
|
||||
=======================================================
|
||||
|
||||
See :issue:`108580`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The page module's content element preview functionality has been
|
||||
enhanced to provide editors with a better visual representation of content
|
||||
elements 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 :php:`\TYPO3\CMS\Core\Html\PreviewSanitizerBuilder` has been introduced that creates a sanitizer
|
||||
specifically for backend previews. This sanitizer:
|
||||
|
||||
* Removes clickable links (unwraps :html:`<a>` tags while preserving
|
||||
their content)
|
||||
* Removes heading tags (:html:`<h1>` through :html:`<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 HTML lists in the preview.
|
||||
|
||||
Harmonized menu element rendering
|
||||
---------------------------------
|
||||
|
||||
Preview rendering for menu content elements and "insert records" elements
|
||||
has been harmonized to match the layout used in the record selector wizard.
|
||||
This provides a consistent experience across different parts of the backend.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
These enhancements 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
|
||||
|
||||
.. index:: Backend, HTML, ext:backend, ext:core
|
||||
@@ -0,0 +1,114 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108581-1735479000:
|
||||
|
||||
===========================================================
|
||||
Feature: #108581 - Record type specific label configuration
|
||||
===========================================================
|
||||
|
||||
See :issue:`108581`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Previously, the TCA label configuration (`ctrl['label']`, `ctrl['label_alt']`,
|
||||
and `ctrl['label_alt_force']`) applied globally to all record types in a
|
||||
table. This meant that all content elements in :sql:`tt_content`, regardless of
|
||||
their `CType`, displayed the same field(s) as their label in the backend.
|
||||
|
||||
It is now possible to define type-specific label configuration in the
|
||||
TCA `types` section. These settings override global `ctrl` label
|
||||
configuration for a 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 :sql:`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
|
||||
would show the header field.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Configuration/TCA/tx_my_table.php
|
||||
|
||||
return [
|
||||
'ctrl' => [
|
||||
'label' => 'header',
|
||||
'type' => 'record_type',
|
||||
// ... other ctrl configuration
|
||||
],
|
||||
'types' => [
|
||||
'article' => [
|
||||
'label_alt' => 'teaser',
|
||||
],
|
||||
'event' => [
|
||||
'label_alt' => 'event_date,location',
|
||||
'label_alt_force' => true,
|
||||
],
|
||||
],
|
||||
// ... columns configuration
|
||||
];
|
||||
|
||||
In this example:
|
||||
|
||||
* All types use `header` as the primary label field (from `ctrl['label']`).
|
||||
* The `article` type 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 a table, label configuration can
|
||||
be provided as the third argument `$additionalTypeInformation` of
|
||||
:php-short:`\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addRecordType`.
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Configuration/TCA/Overrides/tx_my_table.php
|
||||
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
|
||||
ExtensionManagementUtility::addRecordType(
|
||||
[
|
||||
'label' => 'LLL:frontend.ttc:CType.shortcut',
|
||||
'value' => 'my-type',
|
||||
'icon' => 'my-icon',
|
||||
'group' => 'special',
|
||||
],
|
||||
'my-header',
|
||||
[
|
||||
'label' => 'header',
|
||||
'label_alt' => 'records',
|
||||
]
|
||||
);
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
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 :sql:`tt_content` with different `CType`
|
||||
values
|
||||
* Tables with different record types serving different purposes
|
||||
* Plugin records with varying functionality for each type
|
||||
* Any table where the record type changes the record's purpose or meaning
|
||||
|
||||
All occurrences of
|
||||
:php-short:`\TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle()` in TYPO3
|
||||
automatically benefit from this feature without any code changes. This includes
|
||||
record lists, page trees, history views, workspaces, and similar backend
|
||||
modules.
|
||||
|
||||
Functionality such as FormEngine records cannot benefit from this option yet,
|
||||
as FormEngine does not support the Schema API yet.
|
||||
|
||||
.. index:: TCA, Backend, ext:core
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108648-1770305451:
|
||||
|
||||
===================================================================
|
||||
Feature: #108648 - Option to modify src attribute for Vimeo/YouTube
|
||||
===================================================================
|
||||
|
||||
See :issue:`108648`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The new configuration option `srcAttribute` for the `YouTubeRenderer`
|
||||
and `VimeoRenderer` can be used to modify the previously hard-coded `src`
|
||||
attribute in the resulting iframe HTML code. This can be useful if
|
||||
the iframe should not be immediately loaded because of privacy concerns. An
|
||||
alternative such as `data-src` can be used in the initial
|
||||
HTML markup.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<f:media
|
||||
file="{youtubeVideo}"
|
||||
additionalConfig="{srcAttribute: 'data-src'}"
|
||||
/>
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The `src` attribute for YouTube and Vimeo embeds can now be renamed.
|
||||
|
||||
.. index:: Frontend, ext:core
|
||||
@@ -0,0 +1,166 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108653-1767199420:
|
||||
|
||||
=====================================================
|
||||
Feature: #108653 - Database storage for form extension
|
||||
=====================================================
|
||||
|
||||
See :issue:`108653`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :composer:`typo3/cms-form` extension has been extended to include a new database
|
||||
storage adapter (:php-short:`\TYPO3\CMS\Form\Storage\DatabaseStorageAdapter`),
|
||||
allowing form definitions to be stored in the database table :sql:`form_definition`
|
||||
instead of relying on file system storage only.
|
||||
|
||||
Form definitions can now be stored in three ways:
|
||||
|
||||
* **Database storage** (new, recommended) – stored as records in the
|
||||
:sql:`form_definition` table
|
||||
* **File mounts (FAL)** – stored as :file:`.form.yaml` files in FAL storage
|
||||
(deprecated, see :ref:`deprecation-108653-1741600000`)
|
||||
* **Extension paths** – shipped with extensions (read-only or configurable)
|
||||
|
||||
Storage adapter architecture
|
||||
----------------------------
|
||||
|
||||
The storage layer uses the Chain of Responsibility pattern. Each storage
|
||||
adapter implements the
|
||||
:php-short:`\TYPO3\CMS\Form\Storage\StorageAdapterInterface` and declares
|
||||
which persistence identifiers it can handle via its :php:`supports()` method.
|
||||
The :php-short:`\TYPO3\CMS\Form\Storage\StorageAdapterFactory` iterates
|
||||
through all registered adapters sorted by priority and delegates to the first
|
||||
matching adapter.
|
||||
|
||||
Three adapters are shipped:
|
||||
|
||||
* :php-short:`\TYPO3\CMS\Form\Storage\DatabaseStorageAdapter` (priority 100)
|
||||
* :php-short:`\TYPO3\CMS\Form\Storage\ExtensionStorageAdapter` (priority 75)
|
||||
* :php-short:`\TYPO3\CMS\Form\Storage\FileMountStorageAdapter` (priority 50,
|
||||
deprecated)
|
||||
|
||||
Database table :sql:`form_definition`
|
||||
-------------------------------------
|
||||
|
||||
A new TCA-managed table :sql:`form_definition` stores the form definitions
|
||||
with the following fields:
|
||||
|
||||
* :sql:`label` – the human-readable form name
|
||||
* :sql:`identifier` – the unique form identifier (e.g., `contact-form`)
|
||||
* :sql:`configuration` – the full form definition as JSON
|
||||
|
||||
Records are read-only in the standard TCA editing interface. All write and
|
||||
delete operations go through
|
||||
:php-short:`\TYPO3\CMS\Core\DataHandling\DataHandler`, ensuring proper
|
||||
permission checks, history tracking, and hook execution.
|
||||
|
||||
Form Manager wizard
|
||||
-------------------
|
||||
|
||||
A new **Storage** wizard step lets editors choose the storage type (file
|
||||
mount, extension, database) when creating or duplicating forms. When only
|
||||
one storage adapter is accessible, the step auto-advances.
|
||||
|
||||
The Form Manager now also shows a **record history** action in the dropdown menu
|
||||
for database-stored forms, linking to the TYPO3 record history module.
|
||||
|
||||
Record list integration
|
||||
-----------------------
|
||||
|
||||
Two event listeners customize the record list of :sql:`form_definition`
|
||||
records:
|
||||
|
||||
* The standard **edit** action is replaced with a link to the Form Editor
|
||||
module.
|
||||
* The standard **delete** action is removed. Deletion is only possible
|
||||
through the Form Manager.
|
||||
* Clicking the **record title** opens the Form Editor instead of the TCA
|
||||
editing form.
|
||||
|
||||
Creation of :sql:`form_definition` records via the "New Record" wizard
|
||||
is denied via page TSconfig:
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
mod.web_list.deniedNewTables := addToList(form_definition)
|
||||
|
||||
CLI command: transfer between storages
|
||||
--------------------------------------
|
||||
|
||||
A new CLI command :bash:`form:definition:transfer` allows form
|
||||
definitions to be transferred between any two storage backends. This is particularly useful for
|
||||
migrating file-based forms to database storage via the command line.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Transfer all forms from file mounts to database
|
||||
bin/typo3 form:definition:transfer --source=filemount --target=database
|
||||
|
||||
# Transfer a specific form by its identifier
|
||||
bin/typo3 form:definition:transfer --source=extension --target=database --form-identifier=contact
|
||||
|
||||
# Move forms (transfer + delete from source)
|
||||
bin/typo3 form:definition:transfer --source=filemount --target=database --move
|
||||
|
||||
# Dry-run: preview what would be transferred without making changes
|
||||
bin/typo3 form:definition:transfer --source=filemount --target=database --dry-run
|
||||
|
||||
# Transfer to a specific target location (PID for database storage)
|
||||
bin/typo3 form:definition:transfer --source=filemount --target=database --target-location=0
|
||||
|
||||
Available options:
|
||||
|
||||
* :bash:`--source` – source storage type (`database`, `extension`,
|
||||
`filemount`)
|
||||
* :bash:`--target` – target storage type
|
||||
* :bash:`--target-location` – target storage location
|
||||
* :bash:`--form-identifier` – transfer only a specific form
|
||||
* :bash:`--move` – delete the source form after successful transfer
|
||||
* :bash:`--dry-run` – preview without making changes
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Backend users must have table access rights for the :sql:`form_definition`
|
||||
table.
|
||||
|
||||
.. important::
|
||||
|
||||
**Permission model differences between file-based and database storage**
|
||||
|
||||
The file-based storage allows granular access control through TYPO3 file
|
||||
mounts. Different backend user groups can be restricted to different
|
||||
storage folders effectively isolating which forms each group can see and
|
||||
edit.
|
||||
|
||||
Database storage currently relies on TCA table permissions
|
||||
(:sql:`tables_select` / :sql:`tables_modify` for :sql:`form_definition`).
|
||||
This means that all backend users who have table access can see
|
||||
**all** database-stored form definitions — there is no
|
||||
equivalent to the file mount–based isolation yet.
|
||||
|
||||
A dedicated access control mechanism (comparable to file mount
|
||||
isolation) for database-stored forms is planned but not yet implemented.
|
||||
|
||||
If your installation depends on separate permission boundaries for
|
||||
different editor groups, it is recommended to **not migrate** to database
|
||||
storage at this time and continue using file-based storage until the
|
||||
permission feature is available.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Editors can store new form definitions in the database by selecting the "Database"
|
||||
storage type in the Form Manager creation wizard.
|
||||
|
||||
File-based storage (file mounts) will remain functional during the deprecation
|
||||
period but will trigger :php:`E_USER_DEPRECATED` errors. See
|
||||
:ref:`deprecation-108653-1741600000` for migration instructions. Existing
|
||||
file-based forms are not affected by this change.
|
||||
|
||||
Extension-based storage will continue to work without change.
|
||||
|
||||
.. index:: Backend, Database, TCA, ext:form
|
||||
@@ -0,0 +1,40 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108720-1769035130:
|
||||
|
||||
=====================================================
|
||||
Feature: #108720 - QR code button for frontend preview
|
||||
=====================================================
|
||||
|
||||
See :issue:`108720`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new QR code button has been added next to the :guilabel:`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:
|
||||
|
||||
* :guilabel:`Content > Web` module (Layout view and Language Comparison view)
|
||||
* :guilabel:`Web > List` module
|
||||
* :guilabel:`Web > View` module
|
||||
* :guilabel:`Web > Workspaces` module
|
||||
|
||||
Inside a workspace a QR code contains a special preview URI that will
|
||||
work without backend authentication. This makes it easy to share workspace
|
||||
previews with colleagues and clients, or to quickly check draft versions on a
|
||||
mobile device by scanning the code.
|
||||
|
||||
The QR code can be downloaded as PNG or SVG from the modal.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Editors can benefit from a streamlined workflow by sharing page previews and
|
||||
testing pages on mobile devices. Workspace-aware preview URIs eliminate the
|
||||
need to be logged in when scanning the QR code, making it particularly useful
|
||||
for reviewing processes involving external stakeholders.
|
||||
|
||||
.. index:: Backend, ext:backend, ext:workspaces
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108726-1769073579:
|
||||
|
||||
=================================================================================================
|
||||
Feature: #108726 - Add PSR-14 events ModifyRenderedContentAreaEvent and ModifyRenderedRecordEvent
|
||||
=================================================================================================
|
||||
|
||||
See :issue:`108726`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :php:`\TYPO3\CMS\Fluid\Event\ModifyRenderedRecordEvent` allows developers to
|
||||
intercept the rendering of individual records in Fluid templates and modify the output.
|
||||
This depends on records being rendered with the new :html:`<f:render.contentArea>`, see
|
||||
:ref:`Introduce Fluid f:render.contentArea ViewHelper <feature-108726-1769071158>`, or
|
||||
:html:`<f:render.record>` ViewHelpers in Fluid templates, see
|
||||
:ref:`Introduce Fluid f:render.record ViewHelper <feature-108726-1769503907>`.
|
||||
|
||||
Note that any alterations will be output as is and will not be escaped. If you
|
||||
process insecure content inside an event listener, be sure to escape it properly,
|
||||
for example by applying :php:`htmlspecialchars()` to it.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
An example event listener could look like this:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/EventListener/ModifyRenderedContentEventListener.php
|
||||
|
||||
namespace MyVendor\MyExtension\EventListener;
|
||||
|
||||
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||
use TYPO3\CMS\Fluid\Event\ModifyRenderedContentAreaEvent;
|
||||
use TYPO3\CMS\Fluid\Event\ModifyRenderedRecordEvent;
|
||||
|
||||
final class ModifyRenderedContentEventListener
|
||||
{
|
||||
#[AsEventListener]
|
||||
public function modifyContentArea(ModifyRenderedContentAreaEvent $event): void
|
||||
{
|
||||
$content = 'before area<hr />' . $event->getRenderedContentArea()
|
||||
. '<hr />after area';
|
||||
$event->setRenderedContentArea($content);
|
||||
}
|
||||
|
||||
#[AsEventListener]
|
||||
public function modifyRecord(ModifyRenderedRecordEvent $event): void
|
||||
{
|
||||
$content = 'before record<hr />' . $event->getRenderedRecord()
|
||||
. '<hr />after record';
|
||||
$event->setRenderedRecord($content);
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The new events can be used by extension authors to enhance the output of
|
||||
content areas and records rendered in themes.
|
||||
|
||||
.. index:: Frontend, ext:fluid
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108726-1769071158:
|
||||
|
||||
==================================================================
|
||||
Feature: #108726 - Introduce Fluid f:render.contentArea ViewHelper
|
||||
==================================================================
|
||||
|
||||
See :issue:`108726`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Instead of using :html:`<f:cObject>` and :html:`<f:for>` ViewHelpers to render content areas,
|
||||
the new :html:`<f:render.contentArea>` ViewHelper can be used.
|
||||
|
||||
It allows content areas to be rendered 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 :php-short:`\TYPO3\CMS\Fluid\Event\ModifyRenderedContentAreaEvent` and modify the output.
|
||||
|
||||
You need to use the `PAGEVIEW` config like this:
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
page = PAGE
|
||||
page.10 = PAGEVIEW
|
||||
page.10.paths.10 = EXT:my_site_package/Resources/Private/Templates/
|
||||
|
||||
.. code-block:: html
|
||||
:caption: MyPage.fluid.html
|
||||
|
||||
<f:render.contentArea contentArea="{content.left}"/>
|
||||
or
|
||||
{content.left -> f:render.contentArea()}
|
||||
|
||||
The ViewHelper also supports wrapping each content element with additional markup
|
||||
if combined with the `<f:render.record> ViewHelper <https://docs.typo3.org/permalink/t3viewhelper:typo3-fluid-render-record>`_:
|
||||
|
||||
.. code-block:: html
|
||||
:caption: MyPage.fluid.html
|
||||
|
||||
<f:render.contentArea contentArea="{content.main}" recordAs="record">
|
||||
before {record.fullType}
|
||||
<f:render.record record="{record}" />
|
||||
after {record.fullType}
|
||||
</f:render.contentArea>
|
||||
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Theme creators are encouraged to use the :html:`<f:render.contentArea>` ViewHelper
|
||||
to allow other extensions to modify the output via EventListeners.
|
||||
|
||||
|
||||
.. index:: Frontend, ext:fluid
|
||||
@@ -0,0 +1,81 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108726-1769503907:
|
||||
|
||||
=============================================================
|
||||
Feature: #108726 - Introduce Fluid f:render.record ViewHelper
|
||||
=============================================================
|
||||
|
||||
See :issue:`108726`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Instead of using the :html:`<f:cObject>` ViewHelper to render database records,
|
||||
the new :html:`<f:render.record>` ViewHelper can be used.
|
||||
|
||||
It allows records to be rendered while enabling other extensions to modify the
|
||||
output via PSR-14 event listeners.
|
||||
|
||||
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 event listeners
|
||||
can listen to the
|
||||
:php-short:`\TYPO3\CMS\Fluid\Event\ModifyRenderedRecordEvent` and modify the
|
||||
output.
|
||||
|
||||
Usage with the :typoscript:`record-transformation` data processor:
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
dataProcessing {
|
||||
10 = record-transformation
|
||||
}
|
||||
|
||||
.. code-block:: html
|
||||
:caption: MyContentElement.fluid.html
|
||||
|
||||
<f:render.record record="{record}" />
|
||||
or
|
||||
{record -> f:render.record()}
|
||||
|
||||
You can render not only :sql:`tt_content` records, but any database record by
|
||||
defining the rendering in TypoScript.
|
||||
|
||||
.. code-block:: 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
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Theme creators are encouraged to use the :html:`<f:render.record>` ViewHelper
|
||||
to allow other extensions to modify the output via event listeners.
|
||||
|
||||
.. index:: Frontend, ext:fluid
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108763-1769331943:
|
||||
|
||||
=============================================================
|
||||
Feature: #108763 - Console command to analyze Fluid templates
|
||||
=============================================================
|
||||
|
||||
See :issue:`108763`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :bash:`typo3 fluid:analyze` console command is introduced, which analyzes
|
||||
Fluid templates in the current project for correct Fluid syntax and reports
|
||||
deprecations that are emitted during template parsing.
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vendor/bin/typo3 fluid:analyze
|
||||
|
||||
Example output:
|
||||
|
||||
.. code-block::
|
||||
|
||||
[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
|
||||
|
||||
In its initial implementation, the command automatically finds all Fluid
|
||||
templates within the current project based on the `*.fluid.*` file extension
|
||||
(see
|
||||
:ref:`Feature: #108166 - Fluid file extension and template resolving <feature-108166-1763400992>`)
|
||||
and analyzes them. By default, TYPO3 system extensions are skipped. This can
|
||||
be adjusted by specifying the :bash:`--include-system-extensions` CLI option.
|
||||
|
||||
The following errors and deprecations are currently supported:
|
||||
|
||||
* Fluid syntax errors (for example, invalid nesting of ViewHelper tags)
|
||||
* Usage of invalid ViewHelpers or ViewHelper namespaces
|
||||
* Usage of variable names that start with `_`
|
||||
(see :ref:`Breaking: #108148 - Disallow Fluid variable names with underscore prefix <breaking-108148-1763288414>`)
|
||||
* Usage of deprecated ViewHelpers or ViewHelper arguments (if deprecation
|
||||
is triggered during parse time, see
|
||||
:ref:`Deprecating ViewHelpers <feature-108763-1769331943-deprecating-viewhelpers>`
|
||||
and
|
||||
:ref:`Deprecating ViewHelper arguments <feature-108763-1769331943-deprecating-viewhelper-arguments>`)
|
||||
|
||||
If exceptions are caught during the parsing process of at least one template,
|
||||
the console command will have a return status of 1 (error). Otherwise, it will return 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 will
|
||||
not make the pipeline fail.
|
||||
|
||||
Verbose output allows users to get feedback on the analyzed templates
|
||||
and the number of errors and deprecations, or success.
|
||||
|
||||
.. _feature-108763-1769331943-tool-integration:
|
||||
|
||||
Integration with other tools
|
||||
----------------------------
|
||||
|
||||
The command also supports input of a template string via `STDIN` as well as
|
||||
machine-readable output as JSON. This enables better integration with other
|
||||
development-related tools.
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
echo "<formvh:form.timePicker /> {_invalidVariable}" | vendor/bin/typo3 fluid:analyze --stdin --json
|
||||
|
||||
Example output (formatted):
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"identifier": "template__5adb1a7702b9dcbf",
|
||||
"path": "php:\/\/stdin",
|
||||
"errors": [
|
||||
{
|
||||
"file": "\/var\/www\/html\/vendor\/typo3fluid\/fluid\/src\/Core\/Parser\/TemplateParser.php",
|
||||
"line": 130,
|
||||
"message": "Fluid parse error in template php:\/\/stdin, line 2 at character 27. Error: Variable identifiers cannot start with a \"_\": _invalidVariable (error code 1765900762). Template source chunk: {_invalidVariable}\n",
|
||||
"templateLocation": {
|
||||
"identifierOrPath": "php:\/\/stdin",
|
||||
"line": 2,
|
||||
"character": 27
|
||||
}
|
||||
}
|
||||
],
|
||||
"deprecations": [
|
||||
{
|
||||
"file": "\/var\/www\/html\/typo3\/sysext\/form\/Classes\/ViewHelpers\/Form\/TimePickerViewHelper.php",
|
||||
"line": 143,
|
||||
"message": "The TimePickerViewHelper is deprecated since TYPO3 v14 and will be removed in v15."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
.. _feature-108763-1769331943-deprecating-viewhelpers:
|
||||
|
||||
Deprecating ViewHelpers
|
||||
-----------------------
|
||||
|
||||
The :bash:`fluid:analyze` console command can catch deprecations of whole
|
||||
ViewHelpers if the deprecation is emitted during the parse time of a template.
|
||||
This is possible by implementing the
|
||||
:php-short:`\TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperNodeInitializedEventInterface`:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: ObsoleteViewHelper.php
|
||||
|
||||
use TYPO3Fluid\Fluid\Core\Parser\ParsingState;
|
||||
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperNodeInitializedEventInterface;
|
||||
|
||||
/**
|
||||
* @deprecated since X, will be removed in Y.
|
||||
*/
|
||||
final class ObsoleteViewHelper extends AbstractViewHelper implements ViewHelperNodeInitializedEventInterface
|
||||
{
|
||||
// ...
|
||||
|
||||
public static function nodeInitializedEvent(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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.. _feature-108763-1769331943-deprecating-viewhelper-arguments:
|
||||
|
||||
Deprecating ViewHelper arguments
|
||||
--------------------------------
|
||||
|
||||
The :php-short:`\TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperNodeInitializedEventInterface`
|
||||
can be used to deprecate a ViewHelper argument. The deprecation is only
|
||||
triggered if the argument is actually used in a template.
|
||||
|
||||
.. code-block:: php
|
||||
:caption: SomeViewHelper.php
|
||||
|
||||
use TYPO3Fluid\Fluid\Core\Parser\ParsingState;
|
||||
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperNodeInitializedEventInterface;
|
||||
|
||||
final class SomeViewHelper extends AbstractViewHelper implements ViewHelperNodeInitializedEventInterface
|
||||
{
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
// @deprecated since X, will be removed in Y.
|
||||
$this->registerArgument('obsoleteArgument', 'string', 'Original description. Deprecated since X, will be removed in Y');
|
||||
}
|
||||
|
||||
public static function nodeInitializedEvent(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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The new :bash:`typo3 fluid:analyze` console command can be used to check basic
|
||||
validity of Fluid templates in projects that use the `*.fluid.*` file
|
||||
extension and to discover deprecated functionality in template files.
|
||||
|
||||
.. index:: CLI, Fluid, ext:fluid
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108776-1769518546:
|
||||
|
||||
=====================================================================================
|
||||
Feature: #108776 - Allow to set user interface language when using CLI to create user
|
||||
=====================================================================================
|
||||
|
||||
See :issue:`108776`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The CLI command :bash:`typo3 backend:user:create` now supports the
|
||||
:bash:`--language` option, or :bash:`-l`, that sets the desired user interface
|
||||
language.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vendor/bin/typo3 backend:user:create --language=de
|
||||
|
||||
User creation using environment variables:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
TYPO3_BE_USER_NAME=username \
|
||||
TYPO3_BE_USER_EMAIL=admin@example.com \
|
||||
TYPO3_BE_USER_GROUPS=<comma-separated-list-of-group-ids> \
|
||||
TYPO3_BE_USER_LANGUAGE=de \
|
||||
TYPO3_BE_USER_ADMIN=0 \
|
||||
TYPO3_BE_USER_MAINTAINER=0 \
|
||||
vendor/bin/typo3 backend:user:create --no-interaction
|
||||
|
||||
.. index:: CLI, ext:backend
|
||||
@@ -0,0 +1,95 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108796-1738078800:
|
||||
|
||||
=================================================
|
||||
Feature: #108796 - Centralize bookmark management
|
||||
=================================================
|
||||
|
||||
See :issue:`108796`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
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 user TSconfig using
|
||||
:typoscript:`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 their own personal organization and are stored in
|
||||
a new database table :sql:`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 user TSconfig in EXT:backend, making them
|
||||
fully customizable. The functionality remains the same, but administrators now
|
||||
have complete control over which groups are available.
|
||||
|
||||
The :typoscript:`options.bookmarkGroups` setting should only be modified in
|
||||
the global scope and not on a per-user basis, as inconsistent group
|
||||
configuration between users can lead to unexpected behavior:
|
||||
|
||||
.. code-block:: typoscript
|
||||
:caption: EXT:my_ext/Configuration/user.tsconfig
|
||||
|
||||
# Remove a specific default group (for example, 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
|
||||
|
||||
Group labels support the TYPO3 translation domain syntax, allowing extensions
|
||||
to provide translated group names. The format is
|
||||
:typoscript:`extension_key.messages:translation_key`, which resolves to the
|
||||
default language file at
|
||||
:file:`EXT:extension_key/Resources/Private/Language/locallang.xlf`.
|
||||
|
||||
As before, group ID :typoscript:`-100` has special behavior as it is 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 an entire TYPO3
|
||||
installation.
|
||||
|
||||
Bookmark Manager
|
||||
----------------
|
||||
|
||||
A new modal-based :guilabel:`Bookmark Manager` provides a centralized
|
||||
interface for managing 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 via inline editing.
|
||||
|
||||
The :guilabel:`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 :guilabel:`Bookmark Manager`. Users can
|
||||
create custom bookmark groups to better organize 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 code base.
|
||||
|
||||
.. index:: Backend, TSConfig, ext:backend
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108799-1738094060:
|
||||
|
||||
==================================================================================
|
||||
Feature: #108799 - LocalizationRepository methods for fetching record translations
|
||||
==================================================================================
|
||||
|
||||
See :issue:`108799`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
TYPO3 has historically provided helper methods for localization in various
|
||||
places in the TYPO3 code. This patch centralizes localization-related functionality by marking
|
||||
:php:`\TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository`
|
||||
as public (non-internal) and adding new methods as modern, DI-friendly
|
||||
alternatives to the static
|
||||
:php-short:`\TYPO3\CMS\Backend\Utility\BackendUtility` methods.
|
||||
|
||||
getRecordTranslation()
|
||||
----------------------
|
||||
|
||||
Fetches a single translated version of a record for a specific language.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function getRecordTranslation(
|
||||
string|TcaSchema $tableOrSchema,
|
||||
int|array|RecordInterface $recordOrUid,
|
||||
int|LanguageAspect $language,
|
||||
int $workspaceId = 0,
|
||||
bool $includeDeletedRecords = false,
|
||||
): ?RawRecord
|
||||
|
||||
getRecordTranslations()
|
||||
-----------------------
|
||||
|
||||
Fetches all translations of a record. This method can also be used to count
|
||||
translations by using :php:`count()` on the result, replacing the need for
|
||||
:php:`BackendUtility::translationCount()`.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function getRecordTranslations(
|
||||
string|TcaSchema $tableOrSchema,
|
||||
int|array|RecordInterface $recordOrUid,
|
||||
array $limitToLanguageIds = [],
|
||||
int $workspaceId = 0,
|
||||
bool $includeDeletedRecords = false,
|
||||
): array
|
||||
|
||||
Returns an array of translated :php-short:`\TYPO3\CMS\Core\Domain\RawRecord`
|
||||
objects indexed by language ID.
|
||||
|
||||
getPageTranslations()
|
||||
---------------------
|
||||
|
||||
Fetches all page translations for a page.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function getPageTranslations(
|
||||
int $pageUid,
|
||||
array $limitToLanguageIds = [],
|
||||
int $workspaceId = 0,
|
||||
bool $includeDeletedRecords = false,
|
||||
): array
|
||||
|
||||
Returns an array of page translation records as
|
||||
:php-short:`\TYPO3\CMS\Core\Domain\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 :php:`BackendUtility::getRecordLocalization()`,
|
||||
:php:`BackendUtility::getExistingPageTranslations()`, and
|
||||
:php:`BackendUtility::translationCount()` remain available for backward
|
||||
compatibility until they are migrated completely.
|
||||
|
||||
.. index:: Backend, PHP-API, ext:backend
|
||||
@@ -0,0 +1,92 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108815-1738249200:
|
||||
|
||||
========================================================
|
||||
Feature: #108815 - CLI commands for system configuration
|
||||
========================================================
|
||||
|
||||
See :issue:`108815`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
New CLI commands have been introduced to manage TYPO3 system configuration
|
||||
(stored in :file:`config/system/settings.php`) directly from the command line.
|
||||
|
||||
The following commands are now available:
|
||||
|
||||
configuration:show
|
||||
------------------
|
||||
|
||||
Shows a configuration value. By default, if the active value differs from the
|
||||
local value (for example, due to overrides in
|
||||
:file:`config/system/additional.php`), both values are displayed with the
|
||||
difference highlighted.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Show configuration (with diff if overridden)
|
||||
vendor/bin/typo3 configuration:show SYS/sitename
|
||||
|
||||
# Show active (effective runtime) value
|
||||
vendor/bin/typo3 configuration:show SYS/sitename --type=active
|
||||
|
||||
# Show local (settings.php) value only
|
||||
vendor/bin/typo3 configuration:show DB/Connections/Default --type=local
|
||||
|
||||
# Output as JSON
|
||||
vendor/bin/typo3 configuration:show BE/debug --type=active --json
|
||||
|
||||
configuration:set
|
||||
-----------------
|
||||
|
||||
Sets a configuration value in :file:`config/system/settings.php`.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Set a string value
|
||||
vendor/bin/typo3 configuration:set SYS/sitename "My Site"
|
||||
|
||||
# Set boolean or integer values using --json
|
||||
vendor/bin/typo3 configuration:set BE/debug true --json
|
||||
vendor/bin/typo3 configuration:set SYS/displayErrors 1 --json
|
||||
|
||||
# Set an array value
|
||||
vendor/bin/typo3 configuration:set EXTENSIONS/my_extension '{"key": "value"}' --json
|
||||
|
||||
The :bash:`--json` option parses the value as JSON, which allows
|
||||
booleans, integers, and arrays to be set with proper types.
|
||||
|
||||
configuration:remove
|
||||
--------------------
|
||||
|
||||
Removes configuration value or values from :file:`config/system/settings.php`.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Remove a single path (asks for confirmation)
|
||||
vendor/bin/typo3 configuration:remove EXTENSIONS/my_extension/setting
|
||||
|
||||
# Remove without confirmation
|
||||
vendor/bin/typo3 configuration:remove EXTENSIONS/my_extension/setting --force
|
||||
|
||||
# Remove multiple paths (comma-separated)
|
||||
vendor/bin/typo3 configuration:remove "EXTCONF/ext1,EXTCONF/ext2" --force
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
These commands provide a convenient way to manage TYPO3 system configuration
|
||||
from the command line, which is especially useful for:
|
||||
|
||||
* automated deployment and provisioning scripts
|
||||
* CI/CD pipelines that need to adjust configuration
|
||||
* quick configuration changes without needing to access the Install Tool
|
||||
* scripting and automation tasks
|
||||
|
||||
The commands respect TYPO3 configuration path restrictions and only allow
|
||||
writing to paths that are defined in the default configuration or explicitly
|
||||
allowed (such as :php:`EXTENSIONS`, :php:`EXTCONF`, :php:`DB`).
|
||||
|
||||
.. index:: CLI, LocalConfiguration, ext:lowlevel
|
||||
@@ -0,0 +1,77 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108817-1739494800:
|
||||
|
||||
=================================================================
|
||||
Feature: #108817 - Introduce web component-based form editor tree
|
||||
=================================================================
|
||||
|
||||
See :issue:`108817`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The Form Editor tree component has been completely modernized. It has been migrated from
|
||||
a legacy jQuery-based implementation to a modern web component 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 and 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 remembers expanded and collapsed states. After drag
|
||||
and drop operations, the tree maintains your current view and selection,
|
||||
preventing disorientating 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
|
||||
configuration.
|
||||
|
||||
.. index:: Backend, JavaScript, ext:form
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108819-1738329600:
|
||||
|
||||
==========================================================================
|
||||
Feature: #108819 - RecordFieldPreviewProcessor for custom PreviewRenderers
|
||||
==========================================================================
|
||||
|
||||
See :issue:`108819`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new service
|
||||
:php:`\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
|
||||
:php-short:`\TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer`,
|
||||
which required custom preview renderers to extend that class to access them.
|
||||
|
||||
Instead, this service uses the composition-over-inheritance pattern.
|
||||
|
||||
The new service provides the following methods:
|
||||
|
||||
prepareFieldWithLabel()
|
||||
-----------------------
|
||||
|
||||
Renders a field value with its TCA label prepended in bold.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Core\Domain\RecordInterface;
|
||||
|
||||
public function prepareFieldWithLabel(
|
||||
RecordInterface $record,
|
||||
string $fieldName,
|
||||
): ?string
|
||||
|
||||
prepareField()
|
||||
--------------
|
||||
|
||||
Renders a processed field value without a label.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Core\Domain\RecordInterface;
|
||||
|
||||
public function prepareField(
|
||||
RecordInterface $record,
|
||||
string $fieldName,
|
||||
): ?string
|
||||
|
||||
prepareText()
|
||||
-------------
|
||||
|
||||
Processes larger text fields (for example, RTE content) with truncation and
|
||||
HTML stripping.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Core\Domain\RecordInterface;
|
||||
|
||||
public function prepareText(
|
||||
RecordInterface $record,
|
||||
string $fieldName,
|
||||
int $maxLength = 1500,
|
||||
): ?string
|
||||
|
||||
preparePlainHtml()
|
||||
------------------
|
||||
|
||||
Renders plain HTML content with line limiting.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Core\Domain\RecordInterface;
|
||||
|
||||
public function preparePlainHtml(
|
||||
RecordInterface $record,
|
||||
string $fieldName,
|
||||
int $maxLines = 100,
|
||||
): ?string
|
||||
|
||||
prepareFiles()
|
||||
--------------
|
||||
|
||||
Renders thumbnails for file references.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Core\Resource\FileReference;
|
||||
|
||||
public function prepareFiles(
|
||||
iterable|FileReference $fileReferences,
|
||||
): ?string
|
||||
|
||||
linkToEditForm()
|
||||
----------------
|
||||
|
||||
Wraps content in an edit link if the user has the appropriate permissions.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Core\Domain\RecordInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
public function linkToEditForm(
|
||||
string $linkText,
|
||||
RecordInterface $record,
|
||||
ServerRequestInterface $request,
|
||||
): string
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Extension developers implementing custom preview renderers can now inject
|
||||
:php-short:`\TYPO3\CMS\Backend\Preview\RecordFieldPreviewProcessor`
|
||||
to access common field rendering helpers without extending
|
||||
:php-short:`\TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer`.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Backend\Preview\PreviewRendererInterface;
|
||||
use TYPO3\CMS\Backend\Preview\RecordFieldPreviewProcessor;
|
||||
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem;
|
||||
|
||||
final class MyCustomPreviewRenderer implements PreviewRendererInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly RecordFieldPreviewProcessor $fieldProcessor,
|
||||
) {}
|
||||
|
||||
public function renderPageModulePreviewContent(
|
||||
GridColumnItem $item,
|
||||
): string {
|
||||
$record = $item->getRecord();
|
||||
$content = $this->fieldProcessor->prepareFieldWithLabel(
|
||||
$record,
|
||||
'header',
|
||||
);
|
||||
$content .= $this->fieldProcessor->prepareFiles($record->get('image'));
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
||||
.. index:: Backend, PHP-API, ext:backend
|
||||
@@ -0,0 +1,68 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108826-1770219994:
|
||||
|
||||
=======================================
|
||||
Feature: #108826 - Add Short URL module
|
||||
=======================================
|
||||
|
||||
See :issue:`108826`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new backend module :guilabel:`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 :sql:`sys_redirect` records with a
|
||||
dedicated record type :sql:`short_url`, providing a streamlined editing form that
|
||||
hides redirect-specific fields that are irrelevant to short URL use cases.
|
||||
|
||||
Creating short URLs
|
||||
-------------------
|
||||
|
||||
Short URLs can be created in two ways:
|
||||
|
||||
* **Manual entry**: Editors type a custom path (for example, `/promo`) into the
|
||||
source path field.
|
||||
* **Auto-generation**: Clicking the :guilabel:`Generate Short URL` button
|
||||
generates a random 8-character path (for example, `/aBcDeFgH`). The
|
||||
path is guaranteed to be unique due to server-side collision checking.
|
||||
|
||||
Uniqueness enforcement
|
||||
----------------------
|
||||
|
||||
Short URL paths must be unique to each 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, the
|
||||
:php-short:`\TYPO3\CMS\Core\DataHandling\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 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 editing form.
|
||||
|
||||
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.
|
||||
|
||||
.. index:: Backend, ext:redirects
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108831-1738522284:
|
||||
|
||||
================================================================
|
||||
Feature: #108831 - Extend workspace preview links to all records
|
||||
================================================================
|
||||
|
||||
See :issue:`108831`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :guilabel:`Workspaces` module previously generated shareable preview links
|
||||
(with `ADMCMD_prev` token) for pages only. This change extends the functionality
|
||||
to support any record type.
|
||||
|
||||
The preview page for non-page records is determined via existing user TSconfig
|
||||
options:
|
||||
|
||||
* :typoscript:`options.workspaces.previewPageId.<table>`
|
||||
* :typoscript:`TCEMAIN.preview.<table>.previewPageId`
|
||||
|
||||
Additional query parameters can be configured via:
|
||||
|
||||
* :typoscript:`TCEMAIN.preview.<table>.fieldToParameterMap`
|
||||
* :typoscript:`TCEMAIN.preview.<table>.additionalGetParameters`
|
||||
|
||||
Example configuration for a custom record type:
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
TCEMAIN.preview.tx_myext_domain_model_item {
|
||||
previewPageId = 42
|
||||
fieldToParameterMap {
|
||||
uid = tx_myext_pi1[item]
|
||||
}
|
||||
additionalGetParameters {
|
||||
type = 9818
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The QR code and shareable link button in the :guilabel:`Workspaces` module now
|
||||
work for all record types that have a preview configuration. This allows editors
|
||||
to share workspace previews of custom records with colleagues or clients without
|
||||
requiring them to have a backend login.
|
||||
|
||||
.. index:: Backend, TSConfig, ext:workspaces
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108832-1738500000:
|
||||
|
||||
==================================================================================
|
||||
Feature: #108832 - Introduce UserSettings object for backend user profile settings
|
||||
==================================================================================
|
||||
|
||||
See :issue:`108832`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new :php:`\TYPO3\CMS\Core\Authentication\UserSettings` object provides structured access to backend user
|
||||
profile settings defined in :php:`$GLOBALS['TYPO3_USER_SETTINGS']`.
|
||||
|
||||
UserSettings object
|
||||
-------------------
|
||||
|
||||
The :php-short:`\TYPO3\CMS\Core\Authentication\UserSettings` object can be retrieved via the backend user:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
/** @var \TYPO3\CMS\Core\Authentication\UserSettings $userSettings */
|
||||
$userSettings = $GLOBALS['BE_USER']->getUserSettings();
|
||||
|
||||
// Check if a setting exists
|
||||
if ($userSettings->has('colorScheme')) {
|
||||
$scheme = $userSettings->get('colorScheme');
|
||||
}
|
||||
|
||||
// Get all settings as an array
|
||||
$allSettings = $userSettings->toArray();
|
||||
|
||||
// Typed access via dedicated methods
|
||||
$emailOnLogin = $userSettings->isEmailMeAtLoginEnabled();
|
||||
$showUploadFields = $userSettings->isUploadFieldsInTopOfEBEnabled();
|
||||
|
||||
The class implements :php-short:`Psr\Container\ContainerInterface` with :php:`has()`
|
||||
and :php:`get()` methods. The :php:`get()` method throws
|
||||
:php-short:`\TYPO3\CMS\Core\Authentication\Exception\UserSettingsNotFoundException`
|
||||
if the setting does not exist.
|
||||
|
||||
New JSON storage with backward compatibility
|
||||
--------------------------------------------
|
||||
|
||||
Profile settings are now stored in a new :sql:`be_users.user_settings` JSON
|
||||
field, providing a structured and queryable format. For backward compatibility,
|
||||
the existing serialized :sql:`uc` blob continues to be written alongside:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
// 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
|
||||
|
||||
An upgrade wizard, "Migrate user profile settings to JSON format", migrates
|
||||
existing settings from the :sql:`uc` blob to the new :sql:`user_settings` field.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Backend user profile settings can now be accessed via the
|
||||
:php-short:`\TYPO3\CMS\Core\Authentication\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.
|
||||
|
||||
.. index:: Backend, PHP-API, ext:core
|
||||
@@ -0,0 +1,28 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108842-1770128495:
|
||||
|
||||
============================================================
|
||||
Feature: #108842 - Add badge for slide mode in Layout module
|
||||
============================================================
|
||||
|
||||
See :issue:`108842`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
This feature introduces a visual badge in the :guilabel:`Content > Layout` module to
|
||||
indicate when slide mode is active. The badge is a clear indicator for
|
||||
editors, enhancing the user experience by providing immediate feedback on the
|
||||
current mode of operation.
|
||||
|
||||
Each slide mode has a corresponding badge and description text:
|
||||
|
||||
* For :php:`slideMode = none`, no badge is shown.
|
||||
* For :php:`slideMode = slide`, a badge with the text "Slide" is shown, but only
|
||||
if there are no content elements on the current page.
|
||||
* For :php:`slideMode = collect`, a badge with the text "Collect" is shown.
|
||||
* For :php:`slideMode = collectReverse`, a badge with the text "CollectReverse"
|
||||
is shown.
|
||||
|
||||
.. index:: Backend, ext:backend, ext:workspaces
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108843-1738600001:
|
||||
|
||||
============================================================
|
||||
Feature: #108843 - User settings configuration migrated to TCA
|
||||
============================================================
|
||||
|
||||
See :issue:`108843`
|
||||
See :issue:`108832`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The backend user profile settings configuration that was previously stored in
|
||||
:php:`$GLOBALS['TYPO3_USER_SETTINGS']` is now available in TCA at
|
||||
:php:`$GLOBALS['TCA']['be_users']['columns']['user_settings']`.
|
||||
|
||||
This allows user settings to benefit from TCA-based tooling and provides
|
||||
a consistent API that extensions already use for other configuration.
|
||||
|
||||
A new method
|
||||
:php:`ExtensionManagementUtility::addUserSetting()` has been introduced to
|
||||
simplify adding custom fields to user profile settings.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Extensions can add custom fields to backend user profile settings using
|
||||
the new :php:`addUserSetting()` method in
|
||||
:file:`Configuration/TCA/Overrides/be_users.php`:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
// Configuration/TCA/Overrides/be_users.php
|
||||
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addUserSetting(
|
||||
'myCustomSetting',
|
||||
[
|
||||
'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang.xlf:myCustomSetting',
|
||||
'config' => [
|
||||
'type' => 'check',
|
||||
'renderType' => 'checkboxToggle',
|
||||
],
|
||||
],
|
||||
'after:emailMeAtLogin'
|
||||
);
|
||||
|
||||
Alternatively, extensions can directly modify the TCA:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
// Configuration/TCA/Overrides/be_users.php
|
||||
$GLOBALS['TCA']['be_users']['columns']['user_settings']['columns']['myCustomSetting'] = [
|
||||
'label' => 'LLL:my_extension.messages:myCustomSetting',
|
||||
'config' => [
|
||||
'type' => 'check',
|
||||
'renderType' => 'checkboxToggle',
|
||||
],
|
||||
];
|
||||
|
||||
// Add to showitem
|
||||
$GLOBALS['TCA']['be_users']['columns']['user_settings']['showitem'] .= ',myCustomSetting';
|
||||
|
||||
Structure
|
||||
---------
|
||||
|
||||
The :php:`user_settings` TCA column has the following structure:
|
||||
|
||||
:php:`columns`
|
||||
Array of field configurations, each containing:
|
||||
|
||||
:php:`label`
|
||||
The field label (LLL reference or string)
|
||||
|
||||
:php:`config`
|
||||
Standard TCA config array (type, renderType, items, etc.)
|
||||
|
||||
:php:`table` (optional)
|
||||
Set to :php:`'be_users'` if the field is stored in a
|
||||
:sql:`be_users` table column
|
||||
|
||||
:php:`showitem`
|
||||
Comma-separated list of fields to display; supports
|
||||
:php:`--div--;` for tabs
|
||||
|
||||
Available field types
|
||||
---------------------
|
||||
|
||||
* :php:`input` - Text input field
|
||||
* :php:`number` - Number input field
|
||||
* :php:`email` - Email input field
|
||||
* :php:`password` - Password input field
|
||||
* :php:`check` with :php:`renderType => 'checkboxToggle'` -
|
||||
Checkbox or toggle
|
||||
* :php:`select` with :php:`renderType => 'selectSingle'` -
|
||||
Select field
|
||||
* :php:`language` - Language selector
|
||||
|
||||
Backward compatibility
|
||||
----------------------
|
||||
|
||||
For backward compatibility, the legacy
|
||||
:php:`$GLOBALS['TYPO3_USER_SETTINGS']` array is still supported. Third-party
|
||||
additions are automatically migrated to TCA after all
|
||||
:file:`ext_tables.php` files have been loaded. However, this approach is
|
||||
deprecated, and extensions should migrate to the new TCA-based API.
|
||||
|
||||
.. index:: Backend, TCA, PHP-API, ext:setup
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108846-1770196894:
|
||||
|
||||
==========================================================================
|
||||
Feature: #108846 - Console command to inspect global ViewHelper namespaces
|
||||
==========================================================================
|
||||
|
||||
See :issue:`108846`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The new console command `typo3 fluid:namespaces` has been introduced. It
|
||||
lists all the available global ViewHelper namespaces in the current project and
|
||||
can be used to verify the current configuration. The `--json` option
|
||||
can be used to access the information in a machine-readable way.
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vendor/bin/typo3 fluid:namespaces
|
||||
|
||||
Example output:
|
||||
|
||||
.. code-block::
|
||||
|
||||
+--------+------------------------------+
|
||||
| Alias | Namespace(s) |
|
||||
+--------+------------------------------+
|
||||
| core | TYPO3\CMS\Core\ViewHelpers |
|
||||
+--------+------------------------------+
|
||||
| formvh | TYPO3\CMS\Form\ViewHelpers |
|
||||
+--------+------------------------------+
|
||||
| f | TYPO3Fluid\Fluid\ViewHelpers |
|
||||
| | TYPO3\CMS\Fluid\ViewHelpers |
|
||||
+--------+------------------------------+
|
||||
|
||||
The same information is available in the
|
||||
:guilabel:`System > Configuration` module in the TYPO3 backend.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The new console command allows developers and integrators to inspect
|
||||
global ViewHelper namespaces in the current project.
|
||||
|
||||
.. index:: CLI, Fluid, ext:fluid
|
||||
@@ -0,0 +1,158 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108868-1770281522:
|
||||
|
||||
===========================================================
|
||||
Feature: #108868 - Introduce Fluid f:render.text ViewHelper
|
||||
===========================================================
|
||||
|
||||
See :issue:`108868`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new :html:`<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 needs to be
|
||||
displayed in the frontend.
|
||||
|
||||
The ViewHelper is record-aware. It receives the full record and field name,
|
||||
and renders the field according to the field's TCA configuration. This includes
|
||||
both plain text and rich text fields.
|
||||
|
||||
By default, accessing a field that is not available in a record
|
||||
raises an exception. In order to support shared templates that need to be rendered even
|
||||
if a field is missing, the optional boolean argument :html:`optional` can be set
|
||||
to :html:`true`. The ViewHelper will then return :html:`null` instead.
|
||||
|
||||
The input can be a :php-short:`\TYPO3\CMS\Core\Domain\RecordInterface`,
|
||||
:php-short:`\TYPO3\CMS\Frontend\Page\PageInformation`, or a
|
||||
:php-short:`\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface`.
|
||||
|
||||
This means records, ContentBlockData objects, PageInformation
|
||||
objects, and Extbase models can be input. PageInformation objects and Extbase models are
|
||||
converted internally to a RecordInterface.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Usage with the :typoscript:`record-transformation` data processor:
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
dataProcessing {
|
||||
10 = record-transformation
|
||||
}
|
||||
|
||||
Based on the field's TCA configuration of the record in question, the ViewHelper
|
||||
chooses the appropriate processing of the field (plain text, multiline text, or
|
||||
rich text) without needing further configuration in the template.
|
||||
|
||||
.. code-block:: html
|
||||
:caption: MyContentElement.fluid.html
|
||||
|
||||
<f:render.text record="{record}" field="title" />
|
||||
or
|
||||
<f:render.text field="title">{record}</f:render.text>
|
||||
or
|
||||
{f:render.text(record: record, field: 'title')}
|
||||
or
|
||||
{record -> f:render.text(field: 'title')}
|
||||
|
||||
Usage with optional fields:
|
||||
|
||||
.. code-block:: html
|
||||
:caption: SharedHeader.fluid.html
|
||||
|
||||
<f:variable name="header">{record -> f:render.text(field: 'header', optional: true)}</f:variable>
|
||||
|
||||
This is useful for shared partials, for example in
|
||||
:html:`fluid_styled_content`. A header partial can be reused by content
|
||||
elements whose transformed record does not provide a :html:`header` or
|
||||
:html:`subheader` field. Without :html:`optional="true"`, rendering such a
|
||||
partial would raise a :php:`RecordPropertyNotFoundException`. With
|
||||
:html:`optional="true"`, the ViewHelper returns :html:`null` and the partial can
|
||||
continue to handle the missing value gracefully.
|
||||
|
||||
Usage with an Extbase model (property name differs from database field name):
|
||||
|
||||
The :html:`field` argument always refers to the database or 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 to 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
|
||||
:html:`<f:render.text record="{contentModel}" field="bodytext" />`.
|
||||
|
||||
.. code-block:: html
|
||||
:caption: Blog/Templates/Post/Show.fluid.html
|
||||
|
||||
<f:render.text record="{post}" field="short_description" />
|
||||
|
||||
<!-- Example: Post->shortDescription maps to DB field "short_description";
|
||||
use field="short_description" here. -->
|
||||
|
||||
Previously, you needed to choose different processing for plain text and rich
|
||||
text fields. You can now use the same ViewHelper for all field types.
|
||||
|
||||
**For reference, similar results could previously be achieved using:**
|
||||
|
||||
.. code-block:: html
|
||||
:caption: MyContentElement.fluid.html
|
||||
|
||||
{record.title}
|
||||
|
||||
or multiline text:
|
||||
|
||||
.. code-block:: html
|
||||
:caption: MyContentElement.fluid.html
|
||||
|
||||
<f:format.nl2br>{record.description}</f:format.nl2br>
|
||||
or
|
||||
{record.description -> f:format.nl2br()}
|
||||
|
||||
or, for rich text:
|
||||
|
||||
.. code-block:: html
|
||||
:caption: MyContentElement.fluid.html
|
||||
|
||||
<f:format.html>{record.bodytext}</f:format.html>
|
||||
or
|
||||
{record.bodytext -> f:format.html()}
|
||||
|
||||
Migration
|
||||
=========
|
||||
|
||||
Extensions that previously accessed field values with
|
||||
:html:`{record.title}` can continue to do so. However, using
|
||||
:html:`<f:render.text>` is recommended instead 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 :html:`<f:format.nl2br>` or
|
||||
:html:`<f:format.html>` to :html:`<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.
|
||||
|
||||
If a template intentionally accesses fields that might not be available in every
|
||||
record, for example shared :html:`fluid_styled_content` header partials
|
||||
used by custom content elements that do not have a visible :html:`header` field, use the
|
||||
:html:`optional` argument to preserve the previous behavior of treating the
|
||||
missing field as empty output.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Theme creators are encouraged to use the :html:`<f:render.text>` ViewHelper for
|
||||
rendering text-based fields (plain text and rich text) as it provides a
|
||||
standardized, record-aware approach that can be built upon in future versions.
|
||||
|
||||
The ViewHelper takes both the record and the field name as arguments so the
|
||||
rendering process has access to the complete record context. This makes the
|
||||
ViewHelper more flexible than directly accessing the field value.
|
||||
|
||||
.. index:: Frontend, ext:fluid
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108904-1771065699:
|
||||
|
||||
===========================================================================================
|
||||
Feature: #108904 - Add generic error action for custom HTTP status codes in ErrorController
|
||||
===========================================================================================
|
||||
|
||||
See :issue:`108904`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :php:`TYPO3\CMS\Frontend\Controller\ErrorController` has been enhanced with
|
||||
a new method :php:`customErrorAction()` which allows custom error handling
|
||||
for HTTP status codes.
|
||||
|
||||
The new method can be used with TYPO3 site error handling, allowing site
|
||||
administrators to configure dedicated error handling (for example, rendering a
|
||||
Fluid template) for a given status code.
|
||||
|
||||
Example of usage in an Extbase action:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use TYPO3\CMS\Core\Http\PropagateResponseException;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\Controller\ErrorController;
|
||||
|
||||
$response = GeneralUtility::makeInstance(ErrorController::class)->customErrorAction(
|
||||
$this->request,
|
||||
429,
|
||||
'Rate limit exceeded.',
|
||||
'You have exceeded the rate limit.'
|
||||
);
|
||||
throw new PropagateResponseException($response, 1771065101);
|
||||
|
||||
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 error handling in the main site configuration.
|
||||
|
||||
.. index:: Frontend, ext:core
|
||||
@@ -0,0 +1,53 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108915-1742484720:
|
||||
|
||||
===========================================
|
||||
Feature: #108915 - New page creation wizard
|
||||
===========================================
|
||||
|
||||
See :issue:`108915`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The TYPO3 backend now features a new guided "Page Creation Wizard" designed to
|
||||
streamline the page creation process. This interface replaces the traditional,
|
||||
technically complex workflow with a modular and accessible step-by-step
|
||||
process.
|
||||
|
||||
The primary goals of the wizard are to ensure data integrity by enforcing
|
||||
mandatory fields during creation which improves accessibility, and provides a modern,
|
||||
responsive user experience that does not require deep TYPO3-specific expertise.
|
||||
|
||||
Key features
|
||||
------------
|
||||
|
||||
* **Guided workflow:** A step-by-step process including positioning, type
|
||||
selection, data entry, and a final review before persistence.
|
||||
* **Data integrity:** Validation of required fields (for example, page title)
|
||||
occurs at each step to prevent broken or incomplete page records.
|
||||
* **Context-aware:** The wizard can be triggered from various entry points
|
||||
(for example, page tree, :guilabel:`Content > Records` module) and respects
|
||||
predefined parameters like position and page type.
|
||||
* **Modular and extensible:** Built using a generic architecture that allows
|
||||
integrators to add custom steps or extend existing configuration for
|
||||
specific page types.
|
||||
* **FormEngine integration:** Dynamic steps are rendered using FormEngine,
|
||||
ensuring that all TCA-based rules and field configurations are respected.
|
||||
* **Post-creation actions:** After successful creation, users can choose whether to
|
||||
jump to the :guilabel:`Content > Layout` module, create another page,
|
||||
or return to their previous task.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Editors benefit from a faster, less error-prone way to build page structures.
|
||||
The intuitive interface significantly lowers the barrier to entry for new users
|
||||
while maintaining the flexibility required by power users.
|
||||
|
||||
Developers and integrators can leverage the modular design to customize the
|
||||
creation process for custom `doktype` values or even adapt the wizard concept
|
||||
for other TYPO3 workflows in the future.
|
||||
|
||||
.. index:: Backend, ext:backend
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108941-1770902109:
|
||||
|
||||
========================================================================
|
||||
Feature: #108941 - Provide language labels as virtual JavaScript modules
|
||||
========================================================================
|
||||
|
||||
See :issue:`108941`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
JavaScript modules can now import language labels as code.
|
||||
The labels are exposed as an object with a `get()` method
|
||||
and allows placeholder substitution conforming to the ICU message format.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
// 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 MessageFormat 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>`
|
||||
|
||||
This means controllers do not need to inject arbitrary labels into the
|
||||
global `TYPO3.lang` configuration, which impeded writing generic web
|
||||
components.
|
||||
|
||||
Virtual JavaScript modules (schema `~labels/{language.domain}`)
|
||||
are created that resolve the labels for the specified language domain
|
||||
provided after the `~labels/` prefix. This mapping is
|
||||
implemented technically by using an import map path prefix which instructs the
|
||||
JavaScript engine to append a specified suffix to the mapped prefix.
|
||||
|
||||
The labels can be cached client-side with a far-future cache lifetime,
|
||||
similar to static resources. TYPO3 therefore generates version-specific
|
||||
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 labels to be preloaded globally or per module, reducing the
|
||||
risk of missing labels and also simplifying developer workflows.
|
||||
|
||||
Workarounds such as pushing labels to the top frame,
|
||||
loading labels globally, and adding labels to component attributes
|
||||
have previously been used and are replaced by this infrastructure.
|
||||
|
||||
.. index:: Backend, JavaScript, ext:backend
|
||||
@@ -0,0 +1,159 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108966-1738963200:
|
||||
|
||||
===============================================================
|
||||
Feature: #108966 - Rich text editor support in TYPO3 form editor
|
||||
===============================================================
|
||||
|
||||
See :issue:`108966`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The TYPO3 form editor now supports rich text editing for textarea fields with
|
||||
CKEditor 5. Form elements can be configured to use any available RTE preset,
|
||||
which provides a consistent editing experience across the TYPO3 backend.
|
||||
|
||||
The implementation includes a new
|
||||
:php:`\TYPO3\CMS\Form\Service\RichTextConfigurationService` that resolves
|
||||
CKEditor configuration from global TYPO3 RTE presets and prepares it for use
|
||||
in the form editor context. External plugins, such as the TYPO3 link browser,
|
||||
are configured automatically.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Form integrators can now enable rich text editing in 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.
|
||||
* Confirmation finisher - formatted confirmation messages
|
||||
|
||||
All textarea fields in custom form elements can be configured to use the RTE.
|
||||
|
||||
Basic configuration
|
||||
-------------------
|
||||
|
||||
Enable rich text editing for a form element:
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: EXT:my_extension/Configuration/Form/MyFormSetup.yaml
|
||||
|
||||
prototypes:
|
||||
standard:
|
||||
formElementsDefinition:
|
||||
StaticText:
|
||||
formEditor:
|
||||
editors:
|
||||
300:
|
||||
identifier: staticText
|
||||
templateName: Inspector-TextareaEditor
|
||||
label: formEditor.elements.StaticText.editor.staticText.label
|
||||
propertyPath: properties.text
|
||||
enableRichtext: true
|
||||
richtextConfiguration: form-label
|
||||
|
||||
The `richtextConfiguration` option accepts any registered RTE preset name, for
|
||||
example:
|
||||
|
||||
* `form-label` - simple formatting for labels (bold, italic, link) - default
|
||||
* `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`
|
||||
:aspect:`Data type`
|
||||
boolean
|
||||
|
||||
:aspect:`Default`
|
||||
false
|
||||
|
||||
:aspect:`Description`
|
||||
Enables rich text editing for this textarea field.
|
||||
|
||||
`richtextConfiguration`
|
||||
:aspect:`Data type`
|
||||
string
|
||||
|
||||
:aspect:`Default`
|
||||
`form-label`
|
||||
|
||||
:aspect:`Description`
|
||||
Name of the RTE preset to use. The preset must be registered in
|
||||
:php:`$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 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:
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: EXT:my_extension/Configuration/RTE/MyPreset.yaml
|
||||
|
||||
processing:
|
||||
HTMLparser_db:
|
||||
htmlSanitize:
|
||||
build: \MyVendor\MyExtension\Html\MySanitizerBuilder
|
||||
|
||||
Frontend customization
|
||||
----------------------
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: html
|
||||
:caption: EXT:my_extension/Resources/Private/Frontend/Partials/StaticText.html
|
||||
|
||||
{formvh:translateElementProperty(element: element, property: 'text')
|
||||
-> f:sanitize.html(build: 'myCustomBuild')
|
||||
-> f:transform.html()}
|
||||
|
||||
**Option 2: Register a custom default sanitizer**
|
||||
|
||||
Register a custom sanitizer builder as the default sanitizer globally:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/ext_localconf.php
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['htmlSanitizer']['default']
|
||||
= \MyVendor\MyExtension\Html\MySanitizerBuilder::class;
|
||||
|
||||
.. index:: Backend, RTE, ext:form, ext:rte_ckeditor
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108975-1770984757:
|
||||
|
||||
=============================================================================
|
||||
Feature: #108975 - Add configuration provider for Extbase class configuration
|
||||
=============================================================================
|
||||
|
||||
See :issue:`108975`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Extbase *class configuration* (persistence mapping) is now exposed in the
|
||||
backend :guilabel:`System > Configuration` module. The module is available
|
||||
if the system extension :composer:`typo3/cms-lowlevel` is installed.
|
||||
|
||||
The displayed configuration reflects the configured mapping that Extbase uses
|
||||
at runtime. It is built by collecting and merging all
|
||||
:file:`EXT:my_extension/Configuration/Extbase/Persistence/Classes.php`
|
||||
definitions from active packages.
|
||||
|
||||
.. seealso::
|
||||
|
||||
* :ref:`Connecting the model to the database <t3coreapi:extbase-manual-mapping>`
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
This is a read-only usability improvement. Developers and integrators can
|
||||
inspect and verify resolved Extbase persistence class mapping such as
|
||||
extension overrides in the backend, without having to dump configuration
|
||||
arrays or manually check each
|
||||
:file:`EXT:my_extension/Configuration/Extbase/Persistence/Classes.php` file.
|
||||
|
||||
.. index:: Backend, ext:extbase, ext:lowlevel
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108982-1771078311:
|
||||
|
||||
==============================================================
|
||||
Feature: #108982 - Introduce rate limiting for Extbase actions
|
||||
==============================================================
|
||||
|
||||
See :issue:`108982`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
Extbase now supports rate limiting for controller actions using the new PHP
|
||||
attribute :php:`\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.
|
||||
|
||||
Rate limiting is based on the client's IP address and uses Symfony's
|
||||
RateLimiter component with caching framework storage.
|
||||
|
||||
The :php:`#[RateLimit]` attribute supports the following properties:
|
||||
|
||||
* :php:`limit`: The maximum number of requests allowed (default: 5).
|
||||
* :php:`interval`: The time window for the limit (for example,
|
||||
`15 minutes`, `1 hour`) (default: `15 minutes`).
|
||||
* :php:`policy`: The rate limiting policy to use (for example,
|
||||
`sliding_window`, `fixed_window`) (default: `sliding_window`).
|
||||
* :php:`message`: An optional, localizable translation key for the error
|
||||
message shown when the limit is reached, for example
|
||||
`messages.rate_limit_message` (the translation domain, such as
|
||||
`my_extension`, is added automatically and must not be part of the key),
|
||||
or
|
||||
`LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:rate_limit_message`.
|
||||
|
||||
When a rate limit is exceeded, Extbase returns a response with HTTP status code
|
||||
429 by default (:abbr:`Too Many Requests (Too Many Requests)`).
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Apply a rate limit to an Extbase action by adding a :php:`#[RateLimit]` attribute
|
||||
to the action method:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Controller/MyController.php
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TYPO3\CMS\Extbase\Attribute\RateLimit;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
|
||||
class MyController extends ActionController
|
||||
{
|
||||
#[RateLimit(limit: 3, interval: '1 minute', message: 'message.ratelimitexceeded')]
|
||||
public function createAction(): ResponseInterface
|
||||
{
|
||||
// Business logic for creating an entity
|
||||
return $this->redirect('index');
|
||||
}
|
||||
}
|
||||
|
||||
PSR-14 event: BeforeActionRateLimitResponseEvent
|
||||
------------------------------------------------
|
||||
|
||||
The new PSR-14 event
|
||||
:php:`\TYPO3\CMS\Extbase\Event\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, and enqueuing
|
||||
a flash message.
|
||||
|
||||
The following example implementation shows how to throw a custom error if a rate
|
||||
limit is reached. It is handled by a configured site error handler.
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/EventListener/ModifyRateLimitResponse.php
|
||||
|
||||
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||
use TYPO3\CMS\Core\Http\PropagateResponseException;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Event\BeforeActionRateLimitResponseEvent;
|
||||
use TYPO3\CMS\Frontend\Controller\ErrorController;
|
||||
|
||||
final readonly class MyEventListener
|
||||
{
|
||||
#[AsEventListener('my_extension/modify-rate-limit-response')]
|
||||
public function __invoke(BeforeActionRateLimitResponseEvent $event): void
|
||||
{
|
||||
$response = GeneralUtility::makeInstance(ErrorController::class)
|
||||
->accessDeniedAction(
|
||||
$event->getRequest(),
|
||||
$event->getRateLimit()->message,
|
||||
);
|
||||
throw new PropagateResponseException($response, 1771077885);
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Developers can now protect sensitive Extbase actions (for example, form
|
||||
submissions, login attempts, and heavy API endpoints) from abuse, spam, or
|
||||
brute-force attacks with minimal effort.
|
||||
|
||||
.. index:: Frontend, ext:extbase
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-108992-1739706000:
|
||||
|
||||
=======================================================================
|
||||
Feature: #108992 - New PSR-14 event for workspace dependency resolution
|
||||
=======================================================================
|
||||
|
||||
See :issue:`108992`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new PSR-14 event
|
||||
:php:`\TYPO3\CMS\Workspaces\Event\IsReferenceConsideredForDependencyEvent`
|
||||
has been added. It is dispatched for each :sql:`sys_refindex` row when the
|
||||
workspace dependency resolver evaluates which references constitute structural
|
||||
dependencies during publish, stage, discard, and display operations.
|
||||
|
||||
Listeners decide whether a particular 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 has the following methods:
|
||||
|
||||
* :php:`getTableName()`: The table owning the field
|
||||
(:sql:`sys_refindex.tablename`).
|
||||
* :php:`getRecordId()`: The record owning the field
|
||||
(:sql:`sys_refindex.recuid`).
|
||||
* :php:`getFieldName()`: The TCA field name (:sql:`sys_refindex.field`).
|
||||
* :php:`getReferenceTable()`: The referenced table
|
||||
(:sql:`sys_refindex.ref_table`).
|
||||
* :php:`getReferenceId()`: The referenced record ID
|
||||
(:sql:`sys_refindex.ref_uid`).
|
||||
* :php:`getAction()`: The
|
||||
:php:`\TYPO3\CMS\Workspaces\Dependency\DependencyCollectionAction` enum
|
||||
value (:php:`Publish`, :php:`StageChange`, :php:`Discard`, or
|
||||
:php:`Display`).
|
||||
* :php:`getWorkspaceId()`: The current workspace ID.
|
||||
* :php:`isDependency()` / :php:`setDependency()`: Read or change whether this
|
||||
reference is a structural dependency.
|
||||
|
||||
TYPO3 Core registers a listener that marks :php:`type=inline`,
|
||||
:php:`type=file` (with :php:`foreign_field`), and :php:`type=flex` fields as
|
||||
dependencies.
|
||||
|
||||
A new enum
|
||||
:php:`\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:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/EventListener/WorkspaceDependencyListener.php
|
||||
|
||||
namespace Vendor\MyPackage\EventListener;
|
||||
|
||||
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||
use TYPO3\CMS\Workspaces\Event\
|
||||
IsReferenceConsideredForDependencyEvent;
|
||||
|
||||
#[AsEventListener('my-package/workspace-dependency')]
|
||||
final class WorkspaceDependencyListener
|
||||
{
|
||||
public function __invoke(
|
||||
IsReferenceConsideredForDependencyEvent $event
|
||||
): void {
|
||||
if ($event->getFieldName() === 'tx_mypackage_parent') {
|
||||
$event->setDependency(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
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`) that was previously used has been removed. This is an
|
||||
internal change that does not affect the public API.
|
||||
|
||||
.. index:: Backend, PHP-API, ext:workspaces
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-109018-1769714898:
|
||||
|
||||
====================================================================
|
||||
Feature: #109018 - PSR-14 event to modify indexed_search result sets
|
||||
====================================================================
|
||||
|
||||
See :issue:`109018`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new PSR-14 event
|
||||
:php:`\TYPO3\CMS\IndexedSearch\Event\AfterSearchResultSetsAreGeneratedEvent`
|
||||
has been introduced to modify search result sets in
|
||||
:php-short:`\TYPO3\CMS\IndexedSearch\Controller\SearchController`.
|
||||
|
||||
The event is dispatched in :php:`searchAction()` after all the result sets have
|
||||
been built. Event listeners can manipulate complete result sets, including
|
||||
pagination, rows, section data, and category metadata.
|
||||
|
||||
The event has the following methods:
|
||||
|
||||
* :php:`getResultSets()`: Returns all the result sets from the current search.
|
||||
* :php:`setResultSets(array $resultSets)`: Replaces the result sets.
|
||||
* :php:`getSearchData()`: Returns the search configuration array.
|
||||
* :php:`getSearchWords()`: Returns an array of search words.
|
||||
* :php:`getView()`: Returns the view instance.
|
||||
* :php:`getRequest()`: Returns the current server request.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
The following example replaces every result set pagination with
|
||||
:php-short:`\TYPO3\CMS\Core\Pagination\SlidingWindowPagination`:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/EventListener/ModifySearchPaginationListener.php
|
||||
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyVendor\MyExtension\EventListener;
|
||||
|
||||
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||
use TYPO3\CMS\Core\Pagination\SimplePagination;
|
||||
use TYPO3\CMS\Core\Pagination\SlidingWindowPagination;
|
||||
use TYPO3\CMS\IndexedSearch\Event\AfterSearchResultSetsAreGeneratedEvent;
|
||||
|
||||
#[AsEventListener(identifier: 'my-extension/modify-search-result-sets')]
|
||||
final readonly class ModifySearchPaginationListener
|
||||
{
|
||||
public function __invoke(
|
||||
AfterSearchResultSetsAreGeneratedEvent $event
|
||||
): void {
|
||||
$resultSets = $event->getResultSets();
|
||||
|
||||
foreach ($resultSets as $key => $resultSet) {
|
||||
if (($resultSet['pagination'] ?? null)
|
||||
instanceof SimplePagination
|
||||
) {
|
||||
$resultSets[$key]['pagination']
|
||||
= new SlidingWindowPagination(
|
||||
$resultSet['pagination']->getPaginator(),
|
||||
5
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$event->setResultSets($resultSets);
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
This event allows search result sets to be modified in a single listener
|
||||
call. It enables custom pagination strategies, as well as advanced search
|
||||
result transformations.
|
||||
|
||||
.. index:: Frontend, PHP-API, ext:indexed_search
|
||||
@@ -0,0 +1,52 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-109031:
|
||||
|
||||
=================================================
|
||||
Feature: #109031 - Page position select component
|
||||
=================================================
|
||||
|
||||
See :issue:`109031`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new component has been added, based on the `page-browser`, that allows
|
||||
a page to be selected and an insertion position to be defined. Possible
|
||||
positions are `inside` and `after`.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* When a page node in the tree is selected insertion options are displayed.
|
||||
Options include `Insert` and `After`. `After` is applicable to all
|
||||
child pages.
|
||||
|
||||
* On first render, the selected node is scrolled into view
|
||||
using `scrollNodeIntoViewIfNeeded`.
|
||||
|
||||
* The component emits a custom event
|
||||
`typo3:page-position-select-tree:insert-position-change` whenever the
|
||||
insertion position changes. The event payload contains `pageUid` (the
|
||||
selected page ID) and `position` (the chosen insertion position),
|
||||
allowing other modules to react accordingly.
|
||||
|
||||
Example usage
|
||||
=============
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<typo3-backend-component-page-position-select
|
||||
activePageId="1"
|
||||
insertPosition="inside"
|
||||
>
|
||||
</typo3-backend-component-page-position-select>
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
This component can be used anywhere in the backend where page selection and
|
||||
insertion position are needed, replacing previous workflows
|
||||
with more intuitive controls.
|
||||
|
||||
.. index:: Backend
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-109080-1740000001:
|
||||
|
||||
==================================================================
|
||||
Feature: #109080 - Unified RateLimiterFactory with admin overrides
|
||||
==================================================================
|
||||
|
||||
See :issue:`109080`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
TYPO3's :php:`\TYPO3\CMS\Core\RateLimiter\RateLimiterFactory` has been
|
||||
refactored to serve as the single entry point for rate limiting across the
|
||||
system. A new
|
||||
:php:`\TYPO3\CMS\Core\RateLimiter\RateLimiterFactoryInterface`
|
||||
extends Symfony's :php:`RateLimiterFactoryInterface` with additional
|
||||
convenience methods for request-based and login rate limiting.
|
||||
|
||||
Previously, backend and frontend password recovery features and
|
||||
Extbase rate limiting each created Symfony rate limiter factories,
|
||||
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
|
||||
:php-short:`\TYPO3\CMS\Core\RateLimiter\RateLimiterFactoryInterface`
|
||||
when injecting the factory.
|
||||
|
||||
Admin overrides via TYPO3_CONF_VARS
|
||||
-----------------------------------
|
||||
|
||||
A new configuration option
|
||||
:php:`$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:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: config/system/additional.php
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['rateLimiter']['login-be'] = [
|
||||
'limit' => 3,
|
||||
'interval' => '5 minutes',
|
||||
];
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['rateLimiter']['backend-password-recovery'] = [
|
||||
'limit' => 1,
|
||||
'interval' => '1 hour',
|
||||
];
|
||||
|
||||
Known limiter IDs:
|
||||
|
||||
* `login-be` — backend login
|
||||
* `login-fe` — frontend login
|
||||
* `backend-password-recovery` — backend password reset
|
||||
* `felogin-password-recovery` — frontend password recovery
|
||||
* `extbase-<classSlug>-<actionMethod>` — Extbase :php:`#[RateLimit]`
|
||||
actions
|
||||
|
||||
Example limiter ID for Extbase action
|
||||
-------------------------------------
|
||||
|
||||
The limiter ID for an Extbase action with the :php:`#[RateLimit]`
|
||||
attribute is constructed using the "slugified" class name and the action
|
||||
method name.
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Controller/MyController.php
|
||||
|
||||
namespace Vendor\MyExtension\Controller;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TYPO3\CMS\Extbase\Attribute\RateLimit;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
|
||||
class MyController extends ActionController
|
||||
{
|
||||
#[RateLimit(
|
||||
limit: 5,
|
||||
interval: '10 minutes',
|
||||
message: 'ratelimit.dosomething',
|
||||
)]
|
||||
public function doSomethingAction(): ResponseInterface
|
||||
{
|
||||
return $this->redirect('index');
|
||||
}
|
||||
}
|
||||
|
||||
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 :php:`createRequestBasedLimiter()` method is the recommended entry point
|
||||
for request-scoped rate limiting. It extracts the client's
|
||||
remote IP from the PSR-7 request and uses it as the limiter key:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Service/MyService.php
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\RateLimiter\RateLimiterFactoryInterface;
|
||||
|
||||
class MyService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly RateLimiterFactoryInterface $rateLimiterFactory,
|
||||
) {}
|
||||
|
||||
public function doSomething(ServerRequestInterface $request): void
|
||||
{
|
||||
$limiter = $this->rateLimiterFactory->createRequestBasedLimiter(
|
||||
$request,
|
||||
[
|
||||
'id' => 'my-extension-action',
|
||||
'policy' => 'sliding_window',
|
||||
'limit' => 10,
|
||||
'interval' => '1 hour',
|
||||
],
|
||||
);
|
||||
|
||||
$limit = $limiter->consume();
|
||||
if (!$limit->isAccepted()) {
|
||||
// Handle rate limit exceeded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
In cases where a custom key is needed, for example a user ID instead of the
|
||||
IP address, the :php:`createLimiter()` method accepts an explicit
|
||||
configuration array and key:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$limiter = $this->rateLimiterFactory->createLimiter(
|
||||
[
|
||||
'id' => 'my-extension-action',
|
||||
'policy' => 'sliding_window',
|
||||
'limit' => 10,
|
||||
'interval' => '1 hour',
|
||||
],
|
||||
$userId,
|
||||
);
|
||||
|
||||
Preconfigured named services can also be defined in :file:`Services.yaml`.
|
||||
They are then injectable with the :php:`create()` method from the
|
||||
:php:`RateLimiterFactoryInterface`:
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: EXT:my_extension/Configuration/Services.yaml
|
||||
|
||||
myRateLimiter:
|
||||
class: TYPO3\CMS\Core\RateLimiter\RateLimiterFactory
|
||||
arguments:
|
||||
$config:
|
||||
id: 'my-custom-limiter'
|
||||
policy: 'sliding_window'
|
||||
limit: 5
|
||||
interval: '10 minutes'
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
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
|
||||
:php:`$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.
|
||||
|
||||
.. index:: PHP-API, LocalConfiguration, ext:core
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-109087:
|
||||
|
||||
===============================================================================
|
||||
Feature: #109087 - Introduce BeforeBackendPageRenderEvent for BackendController
|
||||
===============================================================================
|
||||
|
||||
See :issue:`109087`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new PSR-14 event :php:`\TYPO3\CMS\Backend\Controller\Event\BeforeBackendPageRenderEvent`
|
||||
has been introduced. It is dispatched in :php-short:`\TYPO3\CMS\Backend\Controller\BackendController`
|
||||
before the main backend page is rendered. It provides access to:
|
||||
|
||||
* :php:`$view` (:php-short:`\TYPO3\CMS\Core\View\ViewInterface`) – assign template
|
||||
variables to the backend top frame view
|
||||
* :php:`$javaScriptRenderer` (:php-short:`\TYPO3\CMS\Core\Page\JavaScriptRenderer`) – add
|
||||
custom JavaScript modules to the backend top frame
|
||||
* :php:`$pageRenderer` (:php-short:`\TYPO3\CMS\Core\Page\PageRenderer`) – add assets
|
||||
such as CSS files (marked :php:`@internal`)
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/EventListener/BeforeBackendPageRenderEventListener.php
|
||||
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyVendor\MyExtension\EventListener;
|
||||
|
||||
use TYPO3\CMS\Backend\Controller\Event\BeforeBackendPageRenderEvent;
|
||||
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
|
||||
|
||||
#[AsEventListener(identifier: 'my-extension/before-backend-page-render')]
|
||||
final class BeforeBackendPageRenderEventListener
|
||||
{
|
||||
public function __invoke(BeforeBackendPageRenderEvent $event): void
|
||||
{
|
||||
$event->javaScriptRenderer->addJavaScriptModuleInstruction(
|
||||
JavaScriptModuleInstruction::create(
|
||||
'@my-vendor/my-extension/backend-module.js'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
It is now possible to add custom JavaScript modules and other assets to the
|
||||
TYPO3 backend top frame using the new PSR-14 event
|
||||
:php:`\TYPO3\CMS\Backend\Controller\Event\BeforeBackendPageRenderEvent`.
|
||||
|
||||
.. index:: Backend, PHP-API, ext:backend
|
||||
@@ -0,0 +1,75 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-109110-1742558400:
|
||||
|
||||
====================================================
|
||||
Feature: #109110 - Introduce scheduler task priority
|
||||
====================================================
|
||||
|
||||
See :issue:`109110`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new :sql:`priority` column has been added to the
|
||||
:sql:`tx_scheduler_task` table, allowing administrators to control the
|
||||
execution order of scheduler tasks. Three levels are available:
|
||||
|
||||
* **High** (150)
|
||||
* **Regular** (100, default)
|
||||
* **Low** (50)
|
||||
|
||||
The scheduler now selects the next executable task ordered by
|
||||
:sql:`priority DESC` first, using :sql:`nextexecution ASC` as a secondary
|
||||
tiebreaker. This means a high-priority task is always
|
||||
executed before a lower-priority task, regardless of how long the
|
||||
lower-priority task has been waiting.
|
||||
|
||||
The priority field is exposed as a select field in the **Timing** tab of
|
||||
the task editing form in all registered task types. The priority of each
|
||||
task is also visible in the scheduler backend module list view.
|
||||
|
||||
Extending priority levels
|
||||
=========================
|
||||
|
||||
Extensions can add custom priority levels by extending the TCA of
|
||||
:sql:`tx_scheduler_task`. The :sql:`priority` field is a plain integer
|
||||
column, so any positive integer value is valid. The scheduler module
|
||||
automatically resolves the label of any registered TCA item, so that custom
|
||||
values are displayed correctly in the list view. The TCA item's
|
||||
:php:`label` key must point to a valid language label. If no matching item
|
||||
is found, the raw integer is shown.
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Configuration/TCA/Overrides/tx_scheduler_task.php
|
||||
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
|
||||
ExtensionManagementUtility::addTcaSelectItem(
|
||||
'tx_scheduler_task',
|
||||
'priority',
|
||||
[
|
||||
'label' => 'LLL:my_extension.messages:priority.critical',
|
||||
'value' => 200,
|
||||
],
|
||||
150,
|
||||
'after',
|
||||
);
|
||||
|
||||
Choose integer values that fit naturally into the existing scale (50 /
|
||||
100 / 150). Values above 150 are executed before **High**, values below
|
||||
50 after **Low**.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Administrators can now assign a priority to each scheduler task. Tasks
|
||||
with **High** priority are picked up before **Regular** tasks, and
|
||||
**Regular** before **Low** tasks. If multiple tasks share the same
|
||||
priority, the longest-overdue task is still selected first, preserving
|
||||
the previous behavior as a tiebreaker.
|
||||
|
||||
Existing tasks receive the default priority **Regular** (100)
|
||||
automatically via the schema update — no data migration is required.
|
||||
|
||||
.. index:: Backend, Database, TCA, ext:scheduler
|
||||
@@ -0,0 +1,49 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-109114-1772123512:
|
||||
|
||||
=============================================================
|
||||
Feature: #109114 - Autocomplete for components via XSD schema
|
||||
=============================================================
|
||||
|
||||
See :issue:`109114`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :ref:`existing CLI command <feature-104114-1719419341>`
|
||||
:bash:`typo3 fluid:schema:generate` has been extended to cover
|
||||
Fluid components. When executed, the command creates `*.xsd` files in
|
||||
:path:`var/transient/` for all available ViewHelpers and components,
|
||||
which can be used by IDEs for autocompletion.
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vendor/bin/typo3 fluid:schema:generate
|
||||
|
||||
In order to work correctly the responsible component collection
|
||||
needs to implement the new
|
||||
:php-short:`TYPO3Fluid\Fluid\Core\Component\ComponentListProviderInterface`.
|
||||
TYPO3's :ref:`Fluid components integration <feature-108508-1765987901>`
|
||||
already implements this, so these components are supported out of the box.
|
||||
|
||||
Fluid Standalone has a default implementation of custom component
|
||||
collections that are based on
|
||||
:php-short:`TYPO3Fluid\Fluid\Core\Component\AbstractComponentCollection`,
|
||||
which should cover components that were created before the official components
|
||||
integration (such as those created with TYPO3 v13). However, if a custom
|
||||
folder structure is used by overriding the default
|
||||
:php:`resolveTemplateName()`, a custom implementation of
|
||||
:php:`getAvailableComponents()` must be provided. In most cases, it
|
||||
is easier to switch to the TYPO3 integration and remove the custom class.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
The CLI command :bash:`typo3 fluid:schema:generate` now creates XSD
|
||||
schema files for Fluid components, enabling autocompletion in supporting
|
||||
IDEs.
|
||||
|
||||
.. index:: CLI, Fluid, ext:fluid
|
||||
@@ -0,0 +1,45 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-109126-1740000000:
|
||||
|
||||
=====================================================
|
||||
Feature: #109126 - Introduce date editor for ext:form
|
||||
=====================================================
|
||||
|
||||
See :issue:`109126`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
A new web component :html:`<typo3-form--date-editor>` has been
|
||||
introduced in the form editor backend. It replaces the plain text
|
||||
input in the :yaml:`DateRange` validator minimum/maximum fields and the
|
||||
:yaml:`defaultValue` field in the :yaml:`Date` form element.
|
||||
|
||||
Previously editors had to manually type date values or relative expressions
|
||||
like :yaml:`-18 years` into a plain text field. The new structured editor
|
||||
provides a user-friendly UI which has five modes:
|
||||
|
||||
* **No value** - Clears the constraint (empty value)
|
||||
* **Today** - Sets the value to :yaml:`today`
|
||||
* **Absolute date** - A native HTML5 date picker that produces `Y-m-d` values
|
||||
* **Relative date** - Structured input with direction (past/future), amount
|
||||
and unit (days, weeks, months, years), which produces expressions like
|
||||
:yaml:`-18 years` or :yaml:`+1 month`
|
||||
* **Custom relative expression** - Free-text input for arbitrary relative
|
||||
date expressions that go beyond the structured input, such as compound
|
||||
expressions like :yaml:`+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 in :yaml:`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 arbitrary relative date expressions
|
||||
with real-time validation to be entered. Existing form definitions are not affected and
|
||||
continue to work without change.
|
||||
|
||||
.. index:: Backend, ext:form
|
||||
@@ -0,0 +1,42 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-109130-1772489836:
|
||||
|
||||
=============================================================
|
||||
Feature: #109130 - Context-aware editing in the layout module
|
||||
=============================================================
|
||||
|
||||
See :issue:`109130`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
The :guilabel:`Content > Layout` module now features a **context panel**
|
||||
for editing page properties and content elements. Clicking on an edit
|
||||
button opens a slide-in panel next to the page layout. The editing form
|
||||
is displayed inside the panel with the page layout remaining visible in
|
||||
the background.
|
||||
|
||||
The panel supports all FormEngine fields in an improved UI. The panel
|
||||
header displays the record title as well as **Save** and **Close**
|
||||
buttons. 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 :guilabel:`User Settings` via the
|
||||
:guilabel:`Use quick editing for records in the page module` option. When
|
||||
disabled, edit buttons navigate to the full record editing form
|
||||
as before. The setting takes effect immediately.
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
Editors can now edit records in the :guilabel:`Content > Layout` module
|
||||
without leaving the page layout context. The full editing form remains
|
||||
accessible for more complex editing tasks.
|
||||
|
||||
.. index:: Backend, ext:backend
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-109163-1772708896:
|
||||
|
||||
===============================================================
|
||||
Feature: #109163 - Implement public system resources publishing
|
||||
===============================================================
|
||||
|
||||
See :issue:`109163`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
When implementing the new system resources API
|
||||
(:ref:`feature-107537-1759136314`), resource publishing was skipped and has now
|
||||
been implemented.
|
||||
|
||||
The most visible feature of this implementation is the new
|
||||
`asset:publish` command. This command can publish public
|
||||
extension resources from their `Resources/Public` folder to the document root
|
||||
directory (`public` by default in Composer mode).
|
||||
|
||||
To maintain backward compatibility for Composer mode installations, this
|
||||
command is automatically executed during `composer install`. This means
|
||||
that after Composer has done its job installing packages, extension assets
|
||||
are already published.
|
||||
|
||||
Public extension resources are also published when extensions are set up
|
||||
with the `extension:setup` command or when an extension is activated in the Extension
|
||||
Manager. Because of this, and because it might not be desirable or applicable
|
||||
to publish assets at Composer build time, it is now possible to skip publishing
|
||||
during `composer install` by setting an environment variable
|
||||
`TYPO3_SKIP_ASSET_PUBLISH`, for example:
|
||||
`TYPO3_SKIP_ASSET_PUBLISH=1 composer install`.
|
||||
Not publishing assets at `composer install` is likely to become default behavior
|
||||
in future TYPO3 versions.
|
||||
|
||||
TYPO3 ships file system-based publishing only. From now on, however, there is
|
||||
an additional strategy available besides symlink publishing (*nix systems) and
|
||||
junction publishing (Windows systems). TYPO3 can now copy all files and
|
||||
folders from their private locations to the document root. This is useful for
|
||||
many use cases such as container builds, deployments with read-only file
|
||||
systems, restrictive hosting environments, and others.
|
||||
|
||||
By default, the linking strategy is being kept, particularly for backward
|
||||
compatibility reasons. It is, however, possible to influence the behavior by
|
||||
setting the following configuration option:
|
||||
|
||||
Default behavior: always link:
|
||||
|
||||
:php:`$GLOBALS['TYPO3_CONF_VARS']['SYS']['SystemResources']['filesystemPublishingType'] = 'link';`
|
||||
|
||||
Always copy / mirror files:
|
||||
|
||||
:php:`$GLOBALS['TYPO3_CONF_VARS']['SYS']['SystemResources']['filesystemPublishingType'] = 'mirror';`
|
||||
|
||||
Copy / mirror files in a `Production` context and link folders in a `Development`
|
||||
context:
|
||||
|
||||
:php:`$GLOBALS['TYPO3_CONF_VARS']['SYS']['SystemResources']['filesystemPublishingType'] = 'auto';`
|
||||
|
||||
Beyond file system publishing
|
||||
-----------------------------
|
||||
|
||||
Although TYPO3 Core only delivers file system-based publishing, third-party
|
||||
extensions can now implement other ways of publishing public system resources.
|
||||
|
||||
By implementing
|
||||
:php:`\TYPO3\CMS\Core\SystemResource\Publishing\SystemResourcePublisherInterface`
|
||||
and registering the implementing class as an alias of the interface, TYPO3
|
||||
will use this not only to publish system resources, but also to generate URIs
|
||||
that reflect their new location, for example on a CDN.
|
||||
|
||||
This also works in TYPO3 classic mode, because publishing is now part of
|
||||
extension activation.
|
||||
|
||||
Simple example of how to generate URIs for a CDN:
|
||||
|
||||
.. code-block:: php
|
||||
:caption: EXT:my_extension/Classes/Service/ExampleResourcePublisher.php
|
||||
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyVendor\MyExtension\Service;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Core\Http\Uri;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
|
||||
use TYPO3\CMS\Core\Package\PackageInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\Publishing\DefaultSystemResourcePublisher;
|
||||
use TYPO3\CMS\Core\SystemResource\Publishing\SystemResourcePublisherInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\Publishing\UriGenerationOptions;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\PublicPackageFile;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\PublicResourceInterface;
|
||||
|
||||
#[Autoconfigure(public: true), AsAlias(SystemResourcePublisherInterface::class, public: true)]
|
||||
final readonly class ExampleResourcePublisher implements SystemResourcePublisherInterface
|
||||
{
|
||||
private const CDN_URL = 'https://my.awsome.cdn/files/';
|
||||
|
||||
public function __construct(
|
||||
private DefaultSystemResourcePublisher
|
||||
$defaultSystemResourcePublisher,
|
||||
) {}
|
||||
|
||||
public function publishResources(
|
||||
PackageInterface $package,
|
||||
): FlashMessageQueue {
|
||||
// Additional logic to publish files to a CDN could be added
|
||||
// here. For this example, the CDN loads the assets from the
|
||||
// source automatically, so resources are published as usual.
|
||||
return $this->defaultSystemResourcePublisher->publishResources(
|
||||
$package,
|
||||
);
|
||||
}
|
||||
|
||||
public function generateUri(
|
||||
PublicResourceInterface $publicResource,
|
||||
?ServerRequestInterface $request,
|
||||
?UriGenerationOptions $options = null,
|
||||
): UriInterface {
|
||||
$defaultUri = $this->defaultSystemResourcePublisher->generateUri(
|
||||
$publicResource,
|
||||
$request,
|
||||
new UriGenerationOptions(
|
||||
uriPrefix: '',
|
||||
absoluteUri: false,
|
||||
cacheBusting: false,
|
||||
),
|
||||
);
|
||||
if ($publicResource instanceof PublicPackageFile) {
|
||||
return new Uri(self::CDN_URL . $defaultUri);
|
||||
}
|
||||
return $defaultUri;
|
||||
}
|
||||
}
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
There is no apparent impact for any TYPO3 installation, as the changes are mostly internal
|
||||
and the public API and behavior are the same as before. For deployments, nothing needs to
|
||||
be changed, as asset publishing is still performed at `composer install`, and also
|
||||
by the `extension:setup` command, both of which are already part of any deployment workflow.
|
||||
|
||||
Users, however, now have more control over when and how publishing is performed, by setting
|
||||
the environment variable `TYPO3_SKIP_ASSET_PUBLISH=1` for `composer install` or by configuring
|
||||
the `mirror` strategy for publishing by setting
|
||||
:php:`$GLOBALS['TYPO3_CONF_VARS']['SYS']['SystemResources']['filesystemPublishingType'] = 'mirror';`
|
||||
in `config/system/additional.php`.
|
||||
|
||||
.. index:: ext:core
|
||||
@@ -0,0 +1,100 @@
|
||||
.. include:: /Includes.rst.txt
|
||||
|
||||
.. _feature-109167-1773174150:
|
||||
|
||||
=========================================================
|
||||
Feature: #109167 - Improved exceptions in Fluid templates
|
||||
=========================================================
|
||||
|
||||
See :issue:`109167`
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
In an effort to simplify debugging Fluid templates, TYPO3 14 enhances
|
||||
exception messages thrown by Fluid in several ways:
|
||||
|
||||
* Templates that contain invalid syntax or refer to undeclared ViewHelper
|
||||
arguments now contain both the full path to the template file and the
|
||||
affected line number in that file.
|
||||
* Most ViewHelper-related error messages now contain the full path to the
|
||||
template file.
|
||||
* Fluid Standalone 5.2 (also backported to Fluid 4.6) introduces more granular
|
||||
exception classes that can be used by ViewHelpers to classify runtime errors.
|
||||
These classifications are also part of the error message.
|
||||
* When a referenced Fluid template cannot be found, the exception message
|
||||
contains a full list of the candidates that have been tried. Also,
|
||||
the exception contains the context in which the template file is missing
|
||||
(for example `FLUIDTEMPLATE` or `PAGEVIEW`).
|
||||
|
||||
In order for this to work with custom ViewHelper implementations, ViewHelpers
|
||||
need to use the base ViewHelper exception class or one of its child classes:
|
||||
|
||||
* :php:`\TYPO3Fluid\Fluid\Core\ViewHelper\Exception` for general exceptions
|
||||
* :php:`\TYPO3Fluid\Fluid\Core\ViewHelper\InvalidArgumentException` for
|
||||
general exceptions related to ViewHelper arguments
|
||||
* :php:`\TYPO3Fluid\Fluid\Core\ViewHelper\InvalidArgumentValueException` for
|
||||
invalid ViewHelper argument values (e.g. wrong type, empty, invalid format)
|
||||
* :php:`\TYPO3Fluid\Fluid\Core\ViewHelper\MissingArgumentException` if
|
||||
a required ViewHelper argument has not been supplied
|
||||
* :php:`\TYPO3Fluid\Fluid\Core\ViewHelper\UndeclaredArgumentException` if
|
||||
a ViewHelper is called with an argument that has not been defined
|
||||
|
||||
If any of these exception classes are used in a ViewHelper, Fluid's internal
|
||||
error handler automatically adds the full path to the current template file
|
||||
to the exception. It is not necessary for ViewHelpers to do this themselves.
|
||||
Note that this leads to nested exceptions. The original exception can be
|
||||
accessed via :php:`$e->getPrevious()`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: plaintext
|
||||
:caption: Parse error in template
|
||||
|
||||
#1238169398 TYPO3Fluid\Fluid\Core\Parser\Exception
|
||||
Fluid parse error in template /var/www/html/typo3conf/ext/theme/Resources/Private/Components/Test/Test.fluid.html, line 11 at character 15.
|
||||
Error: Not all tags were closed! (error code 1238169398). Template source chunk: test
|
||||
|
||||
.. code-block:: plaintext
|
||||
:caption: Undeclared ViewHelper argument
|
||||
|
||||
#1773227091 TYPO3Fluid\Fluid\Core\ViewHelper\Exception
|
||||
TYPO3Fluid\Fluid\Core\ViewHelper\UndeclaredArgumentException in /var/www/html/typo3conf/ext/theme/Resources/Private/Components/Test/Test.fluid.html:
|
||||
Undeclared arguments passed to ViewHelper TYPO3Fluid\Fluid\ViewHelpers\Format\TrimViewHelper: foo. Valid arguments are: value, characters, side
|
||||
(/var/www/html/vendor/typo3fluid/fluid/src/Core/ViewHelper/AbstractViewHelper.php:314)
|
||||
|
||||
.. code-block:: plaintext
|
||||
:caption: Custom validation by ViewHelper implementation
|
||||
|
||||
#1669191560 TYPO3Fluid\Fluid\Core\ViewHelper\Exception
|
||||
TYPO3Fluid\Fluid\Core\ViewHelper\InvalidArgumentValueException in /var/www/html/typo3conf/ext/theme/Resources/Private/Components/Test/Test.fluid.html:
|
||||
The side "none" supplied to Fluid's format.trim ViewHelper is not supported.
|
||||
(/var/www/html/vendor/typo3fluid/fluid/src/ViewHelpers/Format/TrimViewHelper.php:118)
|
||||
|
||||
.. code-block:: plaintext
|
||||
:caption: Missing template file for PAGEVIEW
|
||||
|
||||
#1742058289 TYPO3Fluid\Fluid\View\Exception\InvalidTemplateResourceException
|
||||
PAGEVIEW TypoScript object: Failed to resolve a template file for page layout "default". See also: https://docs.typo3.org/permalink/t3tsref:cobj-pageview@14.2.
|
||||
The following paths were checked:
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/Default/default.fluid.html",
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/Default/default.html",
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/Default/default",
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/Default/Default.fluid.html",
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/Default/Default.html",
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/Default/Default",
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/default.fluid.html",
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/default.html",
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/default",
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/Default.fluid.html",
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/Default.html",
|
||||
"/var/www/html/typo3conf/ext/dummy/Resources/Private/Templates/Pages/Default"
|
||||
|
||||
Impact
|
||||
======
|
||||
|
||||
To make debugging easier, exceptions that originate from Fluid templates now
|
||||
contain more context, such as the full path to the template file.
|
||||
|
||||
.. index:: Fluid, ext:fluid
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user