Skip to content

Commit 24b4f95

Browse files
brosuao-ba
authored andcommitted
[!!!][FEATURE] Add PSR-14 Event before renderable is rendered
A new PSR-14 event TYPO3\CMS\Form\Event\BeforeRenderableIsRenderedEvent has been introduced which serves as an replacement for the now removed hook $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRendering']. Resolves: #107569 Releases: main Change-Id: Ic57fe2a7e8a359bf344eb5f69f72f24ff922c8f6 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/90977 Reviewed-by: Sven Liebert <mail@sven-liebert.de> Reviewed-by: Simon Schaufelberger <simonschaufi+typo3@gmail.com> Reviewed-by: Oli Bartsch <bo@cedev.de> Tested-by: Simon Schaufelberger <simonschaufi+typo3@gmail.com> Tested-by: Sven Liebert <mail@sven-liebert.de> Tested-by: Oli Bartsch <bo@cedev.de> Tested-by: core-ci <typo3@b13.com>
1 parent 68e83f8 commit 24b4f95

File tree

11 files changed

+436
-36
lines changed

11 files changed

+436
-36
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.. include:: /Includes.rst.txt
2+
3+
.. _breaking-107569-1759906416:
4+
5+
==================================================
6+
Breaking: #107569 - Removed "beforeRendering" hook
7+
==================================================
8+
9+
See :issue:`107569`
10+
11+
Description
12+
===========
13+
14+
The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRendering']`
15+
has been removed in favor of the more powerful PSR-14 :php:`TYPO3\CMS\Form\Event\BeforeRenderableIsRenderedEvent`.
16+
17+
Impact
18+
======
19+
20+
Any hook implementation registered is not executed anymore in TYPO3 v14.0+.
21+
22+
Affected installations
23+
======================
24+
25+
TYPO3 installations with custom extensions using this hook. The extension
26+
scanner reports any usage as weak match.
27+
28+
Migration
29+
=========
30+
31+
The hook is removed without deprecation in order to allow extensions
32+
to work with TYPO3 v13 (using the hook) and v14+ (using the new event)
33+
when implementing the event as well without any further deprecations.
34+
Use the :ref:`PSR-14 Event <feature-107569-1759906422>` to allow greater
35+
influence in the functionality. Especially because the event is dispatched
36+
later, it allows more modifications than the previous hook.
37+
38+
.. index:: Backend, ext:form, FullyScanned
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
.. include:: /Includes.rst.txt
2+
3+
.. _feature-107569-1759906422:
4+
5+
=============================================================
6+
Feature: #107569 - PSR-14 Event before renderable is rendered
7+
=============================================================
8+
9+
See :issue:`107569`
10+
11+
Description
12+
===========
13+
14+
A new PSR-14 event :php:`TYPO3\CMS\Form\Event\BeforeRenderableIsRenderedEvent`
15+
has been introduced which serves as an replacement for the now
16+
:ref:`removed <breaking-107569-1759906416>` hook
17+
:php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRendering']`.
18+
19+
The new event is dispatched just right before a renderable is rendered.
20+
21+
The event provides the following public properties:
22+
23+
* :php:`$renderable`: The form element
24+
* :php:`$formRuntime`: The form runtime
25+
26+
Example
27+
=======
28+
29+
An example event listener could look like:
30+
31+
.. code-block:: php
32+
33+
use TYPO3\CMS\Core\Attribute\AsEventListener;
34+
use TYPO3\CMS\Form\Event\BeforeRenderableIsRenderedEvent;
35+
36+
class MyEventListener {
37+
38+
#[AsEventListener(
39+
identifier: 'my-extension/before-renderable-is-rendered',
40+
)]
41+
public function __invoke(BeforeRenderableIsRenderedEvent $event): void
42+
{
43+
$renderable = $event->renderable;
44+
if ($renderable->getType() !== 'Date') {
45+
return;
46+
}
47+
$date = $event->formRuntime[$renderable->getIdentifier()];
48+
if ($date instanceof \DateTime) {
49+
$event->formRuntime[$renderable->getIdentifier()] = $date->format('Y-m-d');
50+
}
51+
}
52+
}
53+
54+
Impact
55+
======
56+
57+
With the new PSR-14 :php:`BeforeRenderableIsRenderedEvent`, it's now
58+
possible to modify the renderable before it's rendered or modify the form runtime.
59+
60+
.. index:: Backend, ext:form

typo3/sysext/form/Classes/Domain/Renderer/FluidFormRenderer.php

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@
2121

2222
namespace TYPO3\CMS\Form\Domain\Renderer;
2323

24+
use Psr\EventDispatcher\EventDispatcherInterface;
2425
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
25-
use TYPO3\CMS\Core\Utility\GeneralUtility;
2626
use TYPO3\CMS\Core\View\ViewFactoryData;
2727
use TYPO3\CMS\Core\View\ViewFactoryInterface;
2828
use TYPO3\CMS\Fluid\View\FluidViewAdapter;
2929
use TYPO3\CMS\Form\Domain\Exception\RenderingException;
30+
use TYPO3\CMS\Form\Event\BeforeRenderableIsRenderedEvent;
3031
use TYPO3\CMS\Form\ViewHelpers\RenderRenderableViewHelper;
3132

3233
/**
@@ -131,6 +132,7 @@ class FluidFormRenderer extends AbstractElementRenderer
131132
{
132133
public function __construct(
133134
protected readonly ViewFactoryInterface $viewFactory,
135+
private readonly EventDispatcherInterface $eventDispatcher,
134136
) {}
135137

136138
/**
@@ -174,12 +176,7 @@ public function render(): string
174176
->getViewHelperVariableContainer()
175177
->addOrUpdate(RenderRenderableViewHelper::class, 'formRuntime', $this->formRuntime);
176178
}
177-
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRendering'] ?? [] as $className) {
178-
$hookObj = GeneralUtility::makeInstance($className);
179-
if (method_exists($hookObj, 'beforeRendering')) {
180-
$hookObj->beforeRendering($this->formRuntime, $this->formRuntime->getFormDefinition());
181-
}
182-
}
179+
$this->eventDispatcher->dispatch(new BeforeRenderableIsRenderedEvent($this->formRuntime->getFormDefinition(), $this->formRuntime));
183180
return $view->render($this->formRuntime->getTemplateName());
184181
}
185182
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the TYPO3 CMS project.
7+
*
8+
* It is free software; you can redistribute it and/or modify it under
9+
* the terms of the GNU General Public License, either version 2
10+
* of the License, or any later version.
11+
*
12+
* For the full copyright and license information, please read the
13+
* LICENSE.txt file that was distributed with this source code.
14+
*
15+
* The TYPO3 project - inspiring people to share!
16+
*/
17+
18+
namespace TYPO3\CMS\Form\Event;
19+
20+
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
21+
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
22+
23+
/**
24+
* Listeners to this Event will be able to modify the renderable before it is rendered.
25+
*/
26+
final class BeforeRenderableIsRenderedEvent
27+
{
28+
public function __construct(
29+
public RootRenderableInterface $renderable,
30+
public FormRuntime $formRuntime,
31+
) {}
32+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the TYPO3 CMS project.
7+
*
8+
* It is free software; you can redistribute it and/or modify it under
9+
* the terms of the GNU General Public License, either version 2
10+
* of the License, or any later version.
11+
*
12+
* For the full copyright and license information, please read the
13+
* LICENSE.txt file that was distributed with this source code.
14+
*
15+
* The TYPO3 project - inspiring people to share!
16+
*/
17+
18+
namespace TYPO3\CMS\Form\EventListener;
19+
20+
use TYPO3\CMS\Core\Attribute\AsEventListener;
21+
use TYPO3\CMS\Form\Event\BeforeRenderableIsRenderedEvent;
22+
23+
class BeforeRenderableIsRenderedEventListener
24+
{
25+
#[AsEventListener('form-framework/format-date-before-rendered')]
26+
public function __invoke(BeforeRenderableIsRenderedEvent $event): void
27+
{
28+
$renderable = $event->renderable;
29+
if ($renderable->getType() !== 'Date') {
30+
return;
31+
}
32+
$date = $event->formRuntime[$renderable->getIdentifier()];
33+
if ($date instanceof \DateTime) {
34+
// @see https://www.w3.org/TR/2011/WD-html-markup-20110405/input.date.html#input.date.attrs.value
35+
// 'Y-m-d' = https://tools.ietf.org/html/rfc3339#section-5.6 -> full-date
36+
$event->formRuntime[$renderable->getIdentifier()] = $date->format('Y-m-d');
37+
}
38+
}
39+
}

typo3/sysext/form/Classes/Hooks/FormElementHooks.php

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
use TYPO3\CMS\Core\Utility\GeneralUtility;
1919
use TYPO3\CMS\Extbase\Validation\Error;
2020
use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface;
21-
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
2221
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
2322
use TYPO3\CMS\Form\Service\TranslationService;
2423

@@ -57,20 +56,4 @@ public function afterSubmit(FormRuntime $formRuntime, RenderableInterface $rende
5756

5857
return $elementValue;
5958
}
60-
61-
/**
62-
* This is a hook that is invoked by the rendering system **before**
63-
* the corresponding element is rendered.
64-
*/
65-
public function beforeRendering(FormRuntime $formRuntime, RootRenderableInterface $renderable)
66-
{
67-
if ($renderable->getType() === 'Date') {
68-
$date = $formRuntime[$renderable->getIdentifier()];
69-
if ($date instanceof \DateTime) {
70-
// @see https://www.w3.org/TR/2011/WD-html-markup-20110405/input.date.html#input.date.attrs.value
71-
// 'Y-m-d' = https://tools.ietf.org/html/rfc3339#section-5.6 -> full-date
72-
$formRuntime[$renderable->getIdentifier()] = $date->format('Y-m-d');
73-
}
74-
}
75-
}
7659
}

typo3/sysext/form/Classes/ViewHelpers/RenderRenderableViewHelper.php

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121

2222
namespace TYPO3\CMS\Form\ViewHelpers;
2323

24-
use TYPO3\CMS\Core\Utility\GeneralUtility;
24+
use Psr\EventDispatcher\EventDispatcherInterface;
2525
use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface;
2626
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
2727
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
28+
use TYPO3\CMS\Form\Event\BeforeRenderableIsRenderedEvent;
2829
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
2930

3031
/**
@@ -42,6 +43,10 @@ final class RenderRenderableViewHelper extends AbstractViewHelper
4243
*/
4344
protected $escapeOutput = false;
4445

46+
public function __construct(
47+
private readonly EventDispatcherInterface $eventDispatcher,
48+
) {}
49+
4550
public function initializeArguments(): void
4651
{
4752
$this->registerArgument('renderable', RootRenderableInterface::class, 'A RenderableInterface instance', true);
@@ -54,17 +59,9 @@ public function render(): string
5459
->getViewHelperVariableContainer()
5560
->get(self::class, 'formRuntime');
5661
$renderable = $this->arguments['renderable'];
57-
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRendering'] ?? [] as $className) {
58-
$hookObj = GeneralUtility::makeInstance($className);
59-
if (method_exists($hookObj, 'beforeRendering')) {
60-
$hookObj->beforeRendering(
61-
$formRuntime,
62-
$renderable
63-
);
64-
}
65-
}
62+
$this->eventDispatcher->dispatch(new BeforeRenderableIsRenderedEvent($renderable, $formRuntime));
6663
$content = '';
67-
if ($renderable instanceof FormRuntime || $renderable instanceof RenderableInterface && $renderable->isEnabled()) {
64+
if ($renderable instanceof FormRuntime || ($renderable instanceof RenderableInterface && $renderable->isEnabled())) {
6865
$content = $this->renderChildren();
6966
}
7067
// Wrap every renderable with a span with an identifier path data attribute if previewMode is active

0 commit comments

Comments
 (0)