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); }
);
})