Deprecation: #91787 - Inline JavaScript in fieldChangeFunc
See forge#91787
Description
Custom Form
nodes allow to use internal property field
to add or modify client-side JavaScript behavior when field values are changed.
In the past these declarations basically were inline JavaScript, provided in
PHP and forwarded to the browser via HTML onchange
or onclick
event attributes. In favor of introducing content security policy headers and
to reduce inline JavaScript, those functionality shall be defined in a
structured way & custom client-side behavior shall be provided by corresponding
JavaScript modules instead.
As a result, field
declarations are not using plain inline
JavaScript (as scalar string
) anymore, but make use of corresponding objects
implementing new \TYPO3\
.
This interface provides both a new structured and declarative approach via
JSON
- but also allows to fallback to legacy inline JavaScript in case it
is required in combination with legacy 3rd party extensions.
Using field
with scalar string
values has been marked as deprecated and has to
be substituted with specific implementations of On
.
Impact
Using field
with scalar string
values will trigger a
PHP E_
error.
Affected Installations
Installations implementing custom Form
components (wizards, nodes,
render-types, ...) that provide inline JavaScript using field
.
// examples
$this->data['parameterArray']['fieldChangeFunc']['example'] = "alert('demo');";
$parameterArray['fieldChangeFunc']['example'] = "alert('demo');";
Migration
The following steps provide a brief overview of the new components in order to avoid inline JavaScript. A complete and installable example is available with ext:demo_91787.
PHP OnFieldChangeInterface
instance
<?php
namespace TYPO3\Example;
use TYPO3\CMS\Backend\Form\Behavior\OnFieldChangeInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class AlertOnFieldChange implements OnFieldChangeInterface
{
protected string $value = 'demo';
public function __toString(): string
{
// provides `alert('demo')` as plain inline JavaScript
return sprintf(
'alert(%s)',
// always make sure to encode data, mitigating XSS
GeneralUtility::quoteJSvalue($this->value)
);
}
public function toArray(): array
{
// provides structured representation
return [
// handler `name` as registered with `FormEngine.js`
'name' => 'example-alert',
// fixed `data` segment
'data' => [
// ... can contain any arbitrary & custom payload
'value' => $this->value,
]
];
}
}
PHP FormEngine
consumer
<?php
namespace TYPO3\Example;
use TYPO3\CMS\Backend\Form\Element\InputTextElement;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
// just extending `input` TCA render-type, to keep it simple
class ConsumingElement extends InputTextElement
{
public function render()
{
// uses custom `OnFieldChangeInterface` implementation from above
// (whenever the value of this field is changed, an alert message shall be shown)
$this->data['parameterArray']['fieldChangeFunc']['example'] = new AlertOnFieldChange();
// side-note: before having `OnFieldChangeInterface`, it looked like this using inline code
// $this->data['parameterArray']['fieldChangeFunc']['example'] = "alert('demo');";
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
// registers RequireJS module to register & handle that `fieldChangeFunc` instruction
// (JavaScript module is loaded from `ext:example/Resources/Public/JavaScript/Demo.js`)
$pageRenderer->loadRequireJsModule('TYPO3/CMS/Example/Demo');
// just use parent method to render that `<input type="text">` field
return parent::render();
}
}
JavaScript FormEngine
registration
JavaScript module TYPO3/
is fetched via RequireJS from
resource path ext:
.
define(['TYPO3/CMS/Backend/FormEngine'], (FormEngine) => {
FormEngine.registerOnFieldChangeHandler(
// `example-alert` as defined in `name` segment from PHP `AlertOnFieldChange::toArray()`
'example-alert',
// `data` segment from PHP `AlertOnFieldChange::toArray()`
(data) => { alert(data.title); }
);
})