TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:24 +02:00
commit aad9daaefd
1506 changed files with 94005 additions and 0 deletions
@@ -0,0 +1,39 @@
.. include:: /Includes.rst.txt
.. _concepts-autocomplete:
============
Autocomplete
============
The :guilabel:`Autocomplete` select in the form editor can be used to
define :html:`autocomplete` properties for input fields. This extension
predefines the most common of the input purposes that are widely
recognized by assistive technologies and
`recommended by the W3C <https://www.w3.org/TR/WCAG21/#input-purposes>`__. The
HTML standard allows arbitrary values.
If you need to provide additional fields, you can reconfigure the autocomplete
field with additional select options:
.. _concepts-autocomplete-add-options:
Add Autocomplete options to the backend editor
==============================================
Create a form set in your extension and add a :file:`config.yaml` with the
additional autocomplete options. The file is auto-discovered — no PHP or
TypoScript registration is required.
.. code-block:: none
:caption: Required directory layout
EXT:my_sitepackage/
Configuration/
Form/
SitePackage/
config.yaml
.. literalinclude:: _config.yaml
:language: yaml
:caption: EXT:my_sitepackage/Configuration/Form/SitePackage/config.yaml
@@ -0,0 +1,12 @@
prototypes:
standard:
formElementsDefinition:
Text:
formEditor:
editors:
600:
selectOptions:
# Choose an index that is not in use yet
12345:
value: 'cc-name'
label: 'cc-name - Full name as given on the payment instrument'
@@ -0,0 +1,17 @@
name: my-sitepackage/form
label: 'My Sitepackage — Form Configuration'
priority: 200
prototypes:
standard:
formElementsDefinition:
Text:
formEditor:
editors:
600:
selectOptions:
# Choose an index that is not in use yet
12345:
value: 'cc-name'
label: 'cc-name - Full name as given on the payment instrument'
@@ -0,0 +1,379 @@
.. include:: /Includes.rst.txt
.. _concepts-configuration:
Configuration
=============
.. _concepts-configuration-whysomuchconfiguration:
A lot of configuration. Why?
----------------------------
Building forms in a declarative and programmatic way is complex. Dynamic forms need
program code that is as generic as possible. But generic
program code means a lot of configurative overhead.
Having so much configuration may seem overwhelming, but it has a lot of
advantages. Many aspects of EXT:form can be manipulated purely
by configuration and without having to involve a developer.
The configuration in EXT:form is mainly located in places which make sense to a
user. However, this means that certain settings have to be
defined in multiple places in order to avoid unpredictable behaviour. There is
no magic in the form framework - it is all about configuration.
.. _concepts-configuration-whyyaml:
Why YAML?
---------
Previous versions of EXT:form used a subset of TypoScript to describe form definitions and
form element behavior. This led to a lot of confusion among integrators because the
definition language looked like TypoScript but did not behave
like TypoScript.
Form and form element definitions had to be declarative, so YAML was chosen as it is
a declarative language.
.. _concepts-configuration-yamlregistration:
YAML registration
-----------------
YAML configuration files are discovered automatically — no PHP or TypoScript
registration is required.
Place your YAML files in :file:`EXT:my_extension/Configuration/Form/<SetName>/` and
add a :file:`config.yaml` with a unique set name. TYPO3 scans all active
extensions and loads the files automatically for both frontend and backend.
.. tip::
For debugging purposes or to get an overview of the configuration
use the :guilabel:`System > Configuration` module. Select
the :guilabel:`Form: YAML Configuration` item in the menu to display
parsed YAML form setup. Make sure you have the lowlevel
system extension installed.
.. tip::
We recommend using a `site package <https://de.slideshare.net/benjaminkott/typo3-the-anatomy-of-sitepackages>`_.
This will make your life easier if you need to do a lot of customization of EXT:form.
.. _concepts-configuration-yaml-autodiscovery:
.. _concepts-configuration-yamlregistration-frontend:
.. _concepts-configuration-yamlregistration-backend:
.. _concepts-configuration-yamlregistration-backend-addtyposcriptsetup:
Auto-discovery directory convention
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: none
EXT:my_extension/
Configuration/
Form/
MyFormSet/
config.yaml
The sub-directory name (``MyFormSet``) is arbitrary. An extension may ship
multiple sets in separate sub-directories.
.. code-block:: yaml
:caption: EXT:my_extension/Configuration/Form/MyFormSet/config.yaml
name: my-vendor/my-form-set
label: 'My Custom Form Set'
# Load order: lower = loaded first. Core base set uses priority 10.
# Extension sets should use > 10 (default: 100) to overlay the base.
priority: 200
# Form configuration goes directly below the metadata:
persistenceManager:
allowedExtensionPaths:
10: 'EXT:my_extension/Resources/Private/Forms/'
.. _concepts-configuration-yamlloading:
YAML loading
------------
TYPO3 uses a ':ref:`YAML loader<t3coreapi:yamlFileLoader>`' for handling
YAML, based on the Symfony YAML package. This YAML loader is able to resolve
environment variables. In addition, EXT:form comes with its own YAML loader, but it
has some restrictions, especially when resolving environment
variables. This is for security reasons.
EXT:form differentiates between :ref:`form configuration and form definition<concepts-formdefinition-vs-formconfiguration>`.
A form definition can be :ref:`stored<concepts-form-file-storages>`
in the file system (FAL) or can be shipped with an extension. The type of YAML loader
used depends on the setup.
.. t3-field-list-table::
:header-rows: 1
- :a: YAML file
:b: YAML loader
- :a: YAML configuration
:b: TYPO3 core
- :a: YAML definition stored in file system (default when using the ``form editor``)
:b: TYPO3 Form Framework
- :a: YAML definition stored in an extension
:b: TYPO3 core
.. _concepts-configuration-configurationaspects:
Configuration aspects
---------------------
Four things can be configured in EXT:form:
- frontend rendering,
- the ``form editor``,
- the ``form manager``, and
- the ``form plugin``.
All configuration is placed in a single :file:`config.yaml` per form set and
is loaded for both frontend and backend. It is up to you whether you want to
keep all configuration in one set or spread it across multiple form sets with
different priorities.
.. _concepts-configuration-inheritances:
Inheritance
-----------
The final YAML configuration does not produce one huge file. Instead, it is
a sequential compilation process:
- Registered configuration files are parsed as YAML and
are combined according to their order.
- Finally, all configuration entries with a value of ``null`` are deleted.
Instead of inheritance, you can also extend/override the frontend configuration
using TypoScript:
.. code-block:: typoscript
plugin.tx_form {
settings {
yamlSettingsOverrides {
...
}
}
}
.. note::
TypoScript overrides like this are ignored by the backend ``form editor``.
.. note::
This process makes life easier. If you are working
with your :ref:`own configuration files <concepts-configuration-yamlregistration>`,
you only have to define things that are different to what was in the previously
loaded configuration files.
An example of overriding the EXT:form Fluid templates. Place the configuration
in :file:`EXT:my_site_package/Configuration/Form/SitePackage/config.yaml`
(auto-discovered, no PHP or TypoScript registration required):
.. code-block:: yaml
prototypes:
standard:
formElementsDefinition:
Form:
renderingOptions:
templateRootPaths:
20: 'EXT:my_site_package/Resources/Private/Templates/Form/Frontend/'
partialRootPaths:
20: 'EXT:my_site_package/Resources/Private/Partials/Form/Frontend/'
layoutRootPaths:
20: 'EXT:my_site_package/Resources/Private/Layouts/Form/Frontend/'
The values in your own configuration file will be merged on top of the EXT:form
base set (:file:`EXT:form/Configuration/Form/Base/config.yaml`).
.. _concepts-configuration-prevent-duplication:
Prevent duplication
^^^^^^^^^^^^^^^^^^^
You can avoid duplication in your YAML files by using anchors (&), aliases (*) and overrides (<<:).
.. code-block:: yaml
customEditor: &customEditor
1761226183:
identifier: custom
templateName: Inspector-TextEditor
label: Custom editor
propertyPath: custom
otherCustomEditor: &otherCustomEditor
identifier: otherCustom
templateName: Inspector-TextEditor
label: Other custom editor
propertyPath: otherCustom
prototypes:
standard:
formElementsDefinition:
Text:
formEditor:
editors:
<<: *customEditor
1761226184: *otherCustomEditor
.. _concepts-configuration-placeholders:
Referencing values with placeholders
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition to anchors and aliases, the TYPO3 YAML loader supports ``%...%``
placeholders. Unlike anchors, they work *across* imported files, because they
are resolved *after* all files have been parsed and merged.
A placeholder is a dot-separated path into the merged configuration. The
referenced value is looked up and substituted:
``%path.to.value%``
How the result is inserted depends on where the placeholder is used:
* **Whole value** if the placeholder is the *only* content of a value, it is
replaced by the referenced value as-is. This may be a scalar **or a complete
array/subtree**.
* **Inside a string** if the placeholder is embedded in a larger string, the
referenced value must be scalar (string or numeric) and is interpolated.
Placeholders can be nested and are resolved recursively. If a referenced path
does not exist, the placeholder is left unchanged.
Reusing a single value from an existing form element works the same way here
the new ``CustomText`` element takes over the label of the core ``Text``
element:
.. code-block:: yaml
prototypes:
standard:
formElementsDefinition:
CustomText:
formEditor:
label: '%prototypes.standard.formElementsDefinition.Text.formEditor.label%'
.. _concepts-configuration-inherit-across-files:
Inheriting a complete element across files
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Because a whole-value placeholder substitutes a complete subtree, it can be used
to base a new form element on the complete configuration of an existing (core) element and
override only a few properties.
A placeholder is resolved *after* parsing and replaces a whole value. The
inheritance and the overrides therefore live in two files: the imported file
copies the complete element subtree, the importing file merges its overrides on
top.
Imported file, copies the whole ``Text`` element to ``CustomText``:
.. code-block:: yaml
:caption: EXT:my_extension/Configuration/Form/CustomElement/CustomTextInherit.yaml
imports:
- { resource: 'EXT:form/Configuration/Form/Base/FormElements/Text.yaml' }
prototypes:
standard:
formElementsDefinition:
CustomText: '%prototypes.standard.formElementsDefinition.Text%'
Importing file, overrides only single properties:
.. code-block:: yaml
:caption: EXT:my_extension/Configuration/Form/CustomElement/config.yaml
imports:
- { resource: 'EXT:my_extension/Configuration/Form/CustomElement/CustomTextInherit.yaml' }
prototypes:
standard:
formElementsDefinition:
CustomText:
formEditor:
label: 'Custom Text'
group: custom
iconIdentifier: form-text
``CustomText`` now inherits the complete configuration of the core ``Text``
element, while only the listed properties are overridden.
.. _concepts-configuration-prototypes:
Prototypes
----------
Most of the form framework configuration is defined
in ``prototypes``. ``standard`` is the default prototype in EXT:form. Prototypes
contain form element definitions - including frontend rendering, ``form editor``
and ``form plugin``. When you create a new form, your form *definition* references
a prototype *configuration*.
This allows you to do a lot of clever stuff. For example:
- depending on which prototype is referenced, the same form can load different
- ...templates
- ...``form editor`` configurations
- ...``form plugin`` finisher overrides
- in the ``form manager``, depending on the selected prototype
- ...different ``form editor`` configurations can be loaded
- ...different pre-configured form templates (boilerplates) can be chosen
- prototypes can define different/ extended form elements and
display them in the frontend/ ``form editor``
The following use case illustrates the prototype concept. Imagine that two
prototypes are defined: "noob" and
"poweruser".
.. t3-field-list-table::
:header-rows: 1
- :a:
:b: Prototype "noob"
:c: Prototype "poweruser"
- :a: **Form elements in the ``form editor``**
:b: Just Text, Textarea
:c: No changes. Default behaviour.
- :a: **Finisher in the ``form editor``**
:b: Only the email finisher is available. It has a field for setting
the subject of the email. The rest of the fields are hidden and filled
with default values.
:c: No changes. Default behaviour.
- :a: **Finisher overrides in the ``form plugin``**
:b: It is not possible to override the finisher configuration.
:c: No changes. Default behaviour.
@@ -0,0 +1,268 @@
.. include:: /Includes.rst.txt
.. _concepts-finishers-customfinisherimplementations:
===============
Custom finisher
===============
.. include:: /Includes/_NoteFinisher.rst
.. contents:: Table of contents
:local:
.. _concepts-finishers-custom-howtowrite:
Write a custom finisher
=======================
To make your finisher configurable by users in the backend form editor, see
:ref:`here <concepts-finishers-customfinisherimplementations-extend-gui>`.
Add a new finisher to the form configuration prototype by defining a
`finishersDefinition`. Set the `implementationClassName` property to your new implementation class.
.. literalinclude:: _codesnippets/_finishersDefinition.yaml
:caption: EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml
`Register <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-custom-extend-gui-configuration>`_
your custom form definition.
Add options to your finisher with the `options` property. Options
are default values which can be overridden in the `form definition`.
.. _concepts-finishers-custom-default-value:
Define default values
---------------------
.. literalinclude:: _codesnippets/_CustomFinisher.yaml
:caption: EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml
.. _concepts-finishers-custom-option-override:
Override options using the `form definition`
--------------------------------------------
.. literalinclude:: _codesnippets/_my_form.yaml
:caption: public/fileadmin/forms/my_form.yaml
A finisher must implement :php-short:`TYPO3\CMS\Form\Domain\Finishers\FinisherInterface`
and should extend :php-short:`TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher`.
In doing so, in the logic of the
finisher the method `executeInternal()` will be called first.
.. _concepts-finishers-customfinisherimplementations-accessingoptions:
Accessing finisher options
==========================
If your finisher class extends :php-short:`TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher`,
you can access the option values in the finisher using method `parseOption()`:
.. code-block:: php
$yourCustomOption = $this->parseOption('yourCustomOption');
`parseOption()` looks for 'yourCustomOption' in your
`form definition`.
.. literalinclude:: _codesnippets/_CustomFinisher.yaml
:caption: EXT:my_site_package/Classes/Domain/Finishers/CustomFinisher.yaml
If it can't find it, `parseOption()` checks
1. for a default value in the `prototype` configuration,
2. for `$defaultOptions` inside your finisher class:
If it doesn't find anything, `parseOption()` returns `null`.
If it finds the option, the process checks whether the option value will
access :ref:`FormRuntime values <concepts-finishers-customfinisherimplementations-accessingoptions-formruntimeaccessor>`.
If the `FormRuntime` returns a positive result, it is checked whether the
option value :ref:`can access values of preceding finishers <concepts-finishers-customfinisherimplementations-finishercontext-sharedatabetweenfinishers>`.
At the end, it :ref:`translates the finisher options <concepts-frontendrendering-translation-finishers>`.
.. _concepts-finishers-customfinisherimplementations-accessingoptions-formruntimeaccessor:
Accessing form runtime values
=============================
You can populate finisher options with
submitted form values using the `parseOption()` method.
You can access values of the `FormRuntime` and therefore values in every
form element by encapsulating option values with `{}`. Below, if there is a
form element with the `identifier` 'subject', you can access the value
in the finisher configuration:
.. literalinclude:: _codesnippets/_my_form_extended.yaml
:caption: public/fileadmin/forms/my_form.yaml
.. code-block:: php
// $yourCustomOption contains the value of the form element with the
// identifier 'subject'
$yourCustomOption = $this->parseOption('yourCustomOption');
You can use `{__currentTimestamp}` as an option value to return the
current UNIX timestamp.
.. _concepts-finishers-customfinisherimplementations-finishercontext:
Finisher Context
================
The :php-short:`TYPO3\CMS\Form\Domain\Finishers\FinisherContext` class takes care of
transferring a finisher context to each finisher. If your finisher class extends
:php-short:`TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher` the
finisher context will be available via:
.. code-block:: php
$this->finisherContext
The `cancel` method prevents the execution of successive finishers:
.. code-block:: php
$this->finisherContext->cancel();
The method `getFormValues` returns the submitted form values.
.. code-block:: php
$this->finisherContext->getFormValues();
The method `getFormRuntime` returns the `FormRuntime`:
.. code-block:: php
$this->finisherContext->getFormRuntime();
.. _concepts-finishers-customfinisherimplementations-finishercontext-sharedatabetweenfinishers:
Share data between finishers
============================
The method `getFinisherVariableProvider` returns an
object (:php-short:`TYPO3\CMS\Form\Domain\Finishers\FinisherVariableProvider`) which allows you
to store data and transfer it to other finishers. The data
can be easily accessed programmatically or inside your configuration:
.. code-block:: php
$this->finisherContext->getFinisherVariableProvider();
The data is stored in :php-short:`TYPO3\CMS\Form\Domain\Finishers\FinisherVariableProvider` and is accessed
by a user-defined 'finisher identifier' and a custom option value path. The
name of the 'finisher identifier' should consist of the name of the finisher
without the 'Finisher' appendix. If your finisher class extends
:php-short:`TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher`, the finisher
identifier name is stored in the following variable:
.. code-block:: php
$this->shortFinisherIdentifier
For example, if the name of your finisher class is 'CustomFinisher', this
variable will contain 'Custom'.
There are 4 methods to access and manage data in the `FinisherVariableProvider`:
* Add data:
.. code-block:: php
$this->finisherContext->getFinisherVariableProvider()->add(
$this->shortFinisherIdentifier,
'unique.value.identifier',
$value
);
* Get data:
.. code-block:: php
$this->finisherContext->getFinisherVariableProvider()->get(
$this->shortFinisherIdentifier,
'unique.value.identifier',
'default value'
);
* Check the existence of data:
.. code-block:: php
$this->finisherContext->getFinisherVariableProvider()->exists(
$this->shortFinisherIdentifier,
'unique.value.identifier'
);
* Delete data:
.. code-block:: php
$this->finisherContext->getFinisherVariableProvider()->remove(
$this->shortFinisherIdentifier,
'unique.value.identifier'
);
In this way, finishers can access `FinisherVariableProvider` data programmatically.
However, it is also possible to access `FinisherVariableProvider` data using form configuration.
Assuming that a finisher called 'Custom' adds data to a `FinisherVariableProvider`:
.. code-block:: php
$this->finisherContext->getFinisherVariableProvider()->add(
$this->shortFinisherIdentifier,
'unique.value.identifier',
'Wouter'
);
other finishers can access the value 'Wouter' by setting
`{Custom.unique.value.identifier}` in the form definition file.
.. literalinclude:: _codesnippets/_my_form_custom.yaml
:caption: public/fileadmin/forms/my_form.yaml
.. _concepts-finishers-customfinisherimplementations-extend-gui:
Add finisher to backend UI
==========================
After registering a new finisher in the yaml form definition file, you can also
add it to the backend form editor for your backend users ( `formEditor:`
section below) to work with in the GUI:
.. literalinclude:: _codesnippets/_backend-ui.yaml
:caption: EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml
:linenos:
.. important::
Make sure to define an `iconIdentifier` in the `finishersDefinition` of your
finisher, otherwise the button to remove the finisher from the
form will not be visible.
.. _concepts-finishers-custom-extend-gui-configuration:
Configuration registration
--------------------------
Place your YAML files in a form set directory — no PHP registration needed:
.. code-block:: none
EXT:my_extension/
Configuration/
Form/
MyFinisher/
config.yaml
.. seealso::
:ref:`concepts-configuration-yaml-autodiscovery`
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace MyVendor\MySitePackage\Domain\Finishers;
class CustomFinisher extends \TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher
{
protected $defaultOptions = [
'yourCustomOption' => 'Olli',
];
// ...
protected function executeInternal()
{
// TODO: Implement executeInternal() method.
}
}
@@ -0,0 +1,7 @@
prototypes:
standard:
finishersDefinition:
CustomFinisher:
implementationClassName: 'MyVendor\MySitePackage\Domain\Finishers\CustomFinisher'
options:
yourCustomOption: 'Ralf'
@@ -0,0 +1,64 @@
prototypes:
standard:
formElementsDefinition:
Form:
formEditor:
editors:
900:
# Extend finisher drop down
selectOptions:
35:
value: 'CustomFinisher'
label: 'Custom Finisher'
propertyCollections:
finishers:
# add finisher fields
25:
identifier: 'CustomFinisher'
editors:
100:
identifier: header
templateName: Inspector-CollectionElementHeaderEditor
label: "Custom Finisher"
# custom field (input, required)
110:
identifier: 'customField'
templateName: 'Inspector-TextEditor'
label: 'Custom Field'
propertyPath: 'options.customField'
propertyValidators:
10: 'NotEmpty'
# email field
120:
identifier: 'email'
templateName: 'Inspector-TextEditor'
label: 'Subscribers email'
propertyPath: 'options.email'
enableFormelementSelectionButton: true
propertyValidators:
10: 'NotEmpty'
20: 'FormElementIdentifierWithinCurlyBracesInclusive'
9999:
identifier: removeButton
templateName: Inspector-RemoveElementEditor
finishersDefinition:
CustomFinisher:
formEditor:
iconIdentifier: 'form-finisher'
label: 'Custom Finisher'
predefinedDefaults:
options:
customField: ''
email: ''
# displayed when overriding finisher settings
FormEngine:
label: 'Custom Finisher'
elements:
customField:
label: 'Custom Field'
config:
type: 'text'
email:
label: 'Subscribers email'
config:
type: 'text'
@@ -0,0 +1,6 @@
prototypes:
standard:
finishersDefinition:
CustomFinisher:
implementationClassName: 'MyVendor\MySitePackage\Domain\Finishers\CustomFinisher'
@@ -0,0 +1,13 @@
identifier: sample-form
label: 'Simple Contact Form'
prototype: standard
type: Form
finishers:
-
identifier: CustomFinisher
options:
yourCustomOption: 'Björn'
renderables:
# ...
@@ -0,0 +1,15 @@
identifier: sample-form
label: 'Simple Contact Form'
prototype: standard
type: Form
finishers:
-
identifier: Custom
options:
yourCustomOption: 'Frans'
-
identifier: SomeOtherStuff
options:
someOtherCustomOption: '{Custom.unique.value.identifier}'
@@ -0,0 +1,16 @@
identifier: simple-contact-form
label: 'Simple Contact Form'
prototype: standard
type: Form
finishers:
-
identifier: Custom
options:
yourCustomOption: '{subject}'
renderables:
-
identifier: subject
label: 'Subject'
type: Text
@@ -0,0 +1,40 @@
:navigation-title: Finishers
.. include:: /Includes.rst.txt
.. _concepts-finishers:
============================================
Finishers: post-submission actions for forms
============================================
When a form has been submitted in TYPO3, finishers decide what happens
next - sending an email, redirecting to another page, or showing a
confirmation message. This page gives you a quick tour of built-in finishers.
For more details, see :ref:`Finisher Options <apireference-finisheroptions>`.
There is also a dedicated chapter on
:ref:`translations of finisher options <concepts-frontendrendering-translation-finishers>`.
.. toctree::
:maxdepth: 2
:titlesonly:
ReadyToUseFinishers/Index
CustomFinisherImplementations/Index
.. _concepts-finishers-execution-order:
Finisher execution order
========================
.. important::
Finishers are executed in the order that is defined in your form definition. The
`Redirect finisher <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-redirectfinisher>`_
terminates all finishers.
If you are using the `redirect finisher <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-redirectfinisher>`_, make sure it is the last finisher
that is executed. The redirect finisher stops the
execution of all subsequent finishers in order to perform a redirect. Finishers
that are defined after a redirect finisher will be ignored.
.. literalinclude:: ReadyToUseFinishers/RedirectFinisher/_codesnippets/_example-redirect.yaml
@@ -0,0 +1,43 @@
.. include:: /Includes.rst.txt
.. _concepts-finishers-closurefinisher:
================
Closure finisher
================
The "Closure finisher" can only be used in programmatically-created forms. It allows
you to execute your own finisher code without implementing/ declaring a finisher.
.. contents:: Table of contents
.. include:: /Includes/_NoteFinisher.rst
.. _apireference-finisheroptions-closurefinisher-options:
Closure finisher option
=======================
.. _apireference-finisheroptions-closurefinisher-options-closure:
.. confval:: closure
:name: closurefinisher-closure
:required: true
:type: `?\Closure`
:default: `null`
The name of the field as shown in the form.
.. _apireference-finisheroptions-closurefinisher:
Using the closure finisher programmatically
===========================================
This finisher can only be used in programmatically-created forms. It allows
you to execute your own finisher code without implementing/ declaring a finisher.
Code example:
.. literalinclude:: _codesnippets/_finisher.php.inc
:language: php
This finisher is implemented in :php:`TYPO3\CMS\Form\Domain\Finishers\ClosureFinisher`.
@@ -0,0 +1,20 @@
<?php
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Form\Domain\Finishers\ClosureFinisher;
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
class SomeClass
{
private function addClosureFinisher(FormDefinition $formDefinition)
{
$closureFinisher = GeneralUtility::makeInstance(ClosureFinisher::class);
$closureFinisher->setOption('closure', function ($finisherContext)
{
$formRuntime = $finisherContext->getFormRuntime();
// ...
});
$formDefinition->addFinisher($closureFinisher);
}
}
@@ -0,0 +1,109 @@
.. include:: /Includes.rst.txt
.. _concepts-finishers-confirmationfinisher:
.. _finishers-confirmation-message:
=====================
Confirmation finisher
=====================
A basic finisher that outputs a text or content element.
.. contents:: Table of contents
.. include:: /Includes/_NoteFinisher.rst
.. _apireference-finisheroptions-confirmationfinisher-options:
Confirmation finisher options
=============================
This finisher outputs a text or a content element after the form has been submitted.
The settings of the finisher are:
.. _apireference-finisheroptions-confirmationfinisher-options-message:
.. confval:: message
:name: confirmationfinisher-message
:type: string
:default: `The form has been submitted.`
Displays this text if the `contentElementUid` is not set.
.. confval:: contentElementUid
:name: confirmationfinisher-contentElementUid
:type: int
:default: 0
Renders the content element with the supplied ID.
.. confval:: translation.propertiesExcludedFromTranslation
:name: confirmationfinisher-translation-propertiesExcludedFromTranslation
:type: array
:default: `[]`
Defines a list of finisher option properties that should be excluded from
translation.
When specified, the listed properties are not processed by the
:php-short:`\TYPO3\CMS\Form\Service\TranslationService` during translation
of finisher options. This prevents their values from being replaced by
translated equivalents, even if translations exist for those options.
This option is usually generated automatically as soon as FlexForm overrides
are in place and normally does not need to be set manually in the form
definition.
See `Skip translation of overridden form finisher options <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-confirmationfinisher-yaml-propertiesexcludedfromtranslation>`_
for an example.
.. _concepts-finishers-confirmationfinisher-yaml:
Confirmation finisher in the YAML form definition
=================================================
A basic finisher that outputs text or a content element.
Outputs text ``message``:
.. literalinclude:: _codesnippets/_form_with_confirmation_finisher.yaml
:caption: public/fileadmin/forms/my_form.yaml
Outputs content element with id 42:
.. literalinclude:: _codesnippets/_form_with_confirmation_content_element.yaml
:caption: public/fileadmin/forms/my_form.yaml
.. _concepts-finishers-confirmationfinisher-yaml-propertiesExcludedFromTranslation:
Skip translation of overridden form finisher options
====================================================
The following is an example of the `translation.propertiesExcludedFromTranslation <https://docs.typo3.org/permalink/typo3/cms-form:confval-confirmationfinisher-translation-propertiesexcludedfromtranslation>`_
option being used to exclude three properties (subject, recipients and
format) from translation.
Using this translation option, the properties can only be overridden by a FlexForm, not by the
:php-short:`\TYPO3\CMS\Form\Service\TranslationService`.
This option is automatically generated as soon as FlexForm overrides are in place.
The following syntax is only documented for completeness. Nonetheless, it can
also be added to a form definition YAML file.
.. literalinclude:: _codesnippets/_form_with_propertiesExcludedFromTranslation.yaml
:caption: public/fileadmin/forms/my_form.yaml
.. _apireference-finisheroptions-confirmationfinisher:
Using the confirmation finisher in PHP code
===========================================
Developers can use the finisher key `Confirmation` to create
confirmation finishers in their own classes:
.. literalinclude:: _codesnippets/_finisher.php.inc
:language: php
Th confirmation finisher is implemented in
:php:`TYPO3\CMS\Form\Domain\Finishers\ConfirmationFinisher`.
@@ -0,0 +1,19 @@
<?php
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
class SomeClass
{
private function addConfirmationnFinisherWithMessage(FormDefinition $formDefinition, string $message)
{
$formDefinition->createFinisher('Confirmation', [
'message' => $message,
]);
}
private function addConfirmationnFinisherWithContentElement(FormDefinition $formDefinition, int $contentElementUid)
{
$formDefinition->createFinisher('Confirmation', [
'contentElementUid' => $contentElementUid,
]);
}
}
@@ -0,0 +1,10 @@
identifier: example-form
label: 'example'
type: Form
finishers:
-
identifier: Confirmation
options:
contentElementUid: 42
#...
@@ -0,0 +1,10 @@
identifier: example-form
label: 'example'
type: Form
finishers:
-
identifier: Confirmation
options:
message: 'Thx for using TYPO3'
# ...
@@ -0,0 +1,17 @@
identifier: example-form
label: 'example'
type: Form
finishers:
-
options:
identifier: EmailToSender
subject: 'Email to sender'
recipients:
recipient@example.org: 'Some Name'
translation:
propertiesExcludedFromTranslation:
- subject
- recipients
- format
# ...
@@ -0,0 +1,48 @@
.. include:: /Includes.rst.txt
.. _concepts-finishers-deleteuploadsfinisher:
.. _finishers-delete-uploads:
=======================
DeleteUploads finishers
=======================
The "DeleteUploads finisher" removes files that have been submitted. You can use this
finisher after the email finisher if you do not want to keep the files
in your TYPO3 installation.
.. note::
Finishers are only executed when a form is successfully submitted. If a user uploads
a file but does not finish filling out the form, the uploaded files will not
be deleted.
.. contents:: Table of contents
.. include:: /Includes/_NoteFinisher.rst
.. _concepts-finishers-deleteuploadsfinisher-yaml:
DeleteUploads finisher in the YAML form definition
==================================================
Use this finisher after the email finisher if you do not want to keep the files
in your TYPO3 installation.
Finishers are executed in the order they are listed in the form definition
YAML file:
.. literalinclude:: _codesnippets/_form.yaml
:caption: public/fileadmin/forms/my_form.yaml
.. _apireference-finisheroptions-deleteuploadsfinisher:
Using the DeleteUploads finisher in PHP code
============================================
Developers can use the finisher key `DeleteUploads` to create
deleteuploads finishers in their own classes:
.. literalinclude:: _codesnippets/_finisher.php.inc
:language: php
This finisher is implemented in :php:`TYPO3\CMS\Form\Domain\Finishers\DeleteUploadsFinisher`.
@@ -0,0 +1,11 @@
<?php
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
class SomeClass
{
private function addDeleteUploadsFinisherWithMessage(FormDefinition $formDefinition, string $message)
{
$formDefinition->createFinisher('DeleteUploads');
}
}
@@ -0,0 +1,13 @@
identifier: example-form
label: 'example'
type: Form
finishers:
-
identifier: EmailToSender
options:
subject: 'Your Message: {message}'
-
identifier: DeleteUploads
# Define the delete uploads finisher AFTER the email finisher
# ...
@@ -0,0 +1,325 @@
.. include:: /Includes.rst.txt
.. _concepts-finishers-emailfinisher:
==============
Email finisher
==============
The EmailFinisher sends an email to one recipient. EXT:form has two
EmailFinishers with the identifiers EmailToReceiver and EmailToSender.
.. contents:: Table of contents
.. include:: /Includes/_NoteFinisher.rst
.. _concepts-finishers-emailfinisher-backend:
.. _finishers-email-to-sender:
.. _finishers-email-to-receiver:
Using email finishers in the backend form editor
================================================
Editors can use two email finishers in the backend form editor:
Email to sender (form submitter)
This finisher sends an email with the contents of the form to the user
submitting the form .
Email to receiver (you)
This finisher sends an email with the contents of the form to the owner of the
website. The settings of this finisher are the
same as the "Email to sender" finisher
.. _apireference-finisheroptions-emailfinisher-options:
Options of the email finisher
=============================
.. _apireference-finisheroptions-emailfinisher-options-subject:
.. confval:: Subject [subject]
:name: emailfinisher-subject
:type: string
:required: true
Subject of the email.
.. _apireference-finisheroptions-emailfinisher-options-recipients:
.. confval:: Recipients [recipients]
:name: emailfinisher-recipients
:type: array
:required: true
Email addresses and names of the recipients (To).
**Email Address**
Email address of a recipient, e.g. "some.recipient@example.com"
or "{email-1}".
**Name**
Name of a recipient, e.g. "Some Recipient" or "{text-1}".
.. _apireference-finisheroptions-emailfinisher-options-senderaddress:
.. confval:: Sender address [senderAddress]
:name: emailfinisher-senderAddress
:type: string
:required: true
Email address of the sender, for example "your.company@example.org".
If `smtp <https://docs.typo3.org/permalink/t3coreapi:mail-configuration-smtp>`_
is used, this email address needs to be allowed by the
SMTP server. Use `replyToRecipients` if you want to enable the receiver to
reply to the message.
.. _apireference-finisheroptions-emailfinisher-options-sendername:
.. confval:: Sender name [senderName]
:name: emailfinisher-senderName
:type: string
:default: `''`
Name of the sender, for example "Your Company".
.. _apireference-finisheroptions-emailfinisher-options-replytorecipients:
.. confval:: Reply-to Recipients [replyToRecipients]
:name: emailfinisher-replyToRecipients
:type: array
:default: `[]`
Email address which will be used when someone replies to the email.
**Email Address**:
Email address for reply-to.
**Name**
Name for reply-to.
.. _apireference-finisheroptions-emailfinisher-options-carboncopyrecipients:
.. confval:: CC Recipient [carbonCopyRecipients]
:name: emailfinisher-carbonCopyRecipients
:type: array
:default: `[]`
Email address to which a copy of the email is sent. The information is
visible to all other recipients.
**Email Address**:
Email address for CC.
**Name**
Name for CC.
.. _apireference-finisheroptions-emailfinisher-options-blindcarboncopyrecipients:
.. confval:: BCC Recipients [blindCarbonCopyRecipients]
:name: emailfinisher-blindCarbonCopyRecipients
:type: array
:default: `[]`
Email address to which a copy of the email is sent. The information is not
visible to any of the recipients.
**Email Address**:
Email address for BCC.
**Name**
Name for BCC.
.. _apireference-finisheroptions-emailfinisher-options-addhtmlpart:
.. confval:: Add HTML part [addHtmlPart]
:name: emailfinisher-addHtmlPart
:type: bool
:default: `true`
If set, emails will contain plaintext and HTML, otherwise only plaintext.
In this way, HTML can be disabled and plaintext-only emails enforced.
.. _apireference-finisheroptions-emailfinisher-options-attachuploads:
.. confval:: Attach uploads [attachUploads]
:name: emailfinisher-attachUploads
:type: bool
:default: `true`
If set, all uploaded items are attached to the email.
.. _apireference-finisheroptions-emailfinisher-options-title:
.. confval:: Title [title]
:name: emailfinisher-title
:type: string
:required: false
:default: `undefined`
The title shown in the email.
.. _apireference-finisheroptions-emailfinisher-options-translation-language:
.. confval:: Translation language [translation.language]
:name: emailfinisher-translation-language
:type: string
:required: false
:default: `undefined`
If not set, the finisher options are translated depending on the current
frontend language (if translations exist). This option allows you to force
translations for a given language isocode, e.g. `da` or `de`.
See :ref:`Translate finisher options<concepts-frontendrendering-translation-finishers>`.
.. _apireference-finisheroptions-emailfinisher-options-options:
Additional email finisher options
=================================
Additional options can be set in the form definition YAML and
programmatically in the options array but **not** in the backend editor:
.. _apireference-finisheroptions-emailfinisher-options-translation-propertiesExcludedFromTranslation:
.. confval:: Properties excluded from translation [translation.propertiesExcludedFromTranslation]
:name: emailfinisher-translation-propertiesExcludedFromTranslation
:type: array
:required: false
:default: `undefined`
If not set, the finisher options are translated depending on the current frontend language (if translations exists).
This option allows you to force translations for a given language isocode, e.g 'da' or 'de'.
See :ref:`Translate finisher options<concepts-frontendrendering-translation-finishers>`.
It will be skipped for all specified finisher options.
.. _apireference-finisheroptions-emailfinisher-options-translation-translationfiles:
.. confval:: translation.translationFiles
:name: emailfinisher-translation-translationFiles
:type: array
:required: false
:default: `undefined`
If set, this translation file(s) will be used for finisher option
translations. If not set, the translation file(s) from the `Form` element
will be used.
Read :ref:`Translate finisher options<concepts-frontendrendering-translation-finishers>`.
.. _apireference-finisheroptions-emailfinisher-options-layoutrootpaths:
.. confval:: layoutRootPaths
:name: emailfinisher-layoutRootPaths
:type: array
:required: false
:default: `undefined`
Fluid layout paths.
.. _apireference-finisheroptions-emailfinisher-options-partialrootpaths:
.. confval:: partialRootPaths
:name: emailfinisher-partialRootPaths
:type: array
:required: false
:default: `undefined`
Fluid partial paths.
.. _apireference-finisheroptions-emailfinisher-options-templaterootpaths:
.. confval:: templateRootPaths
:name: emailfinisher-templateRootPaths
:type: array
:required: false
:default: `undefined`
Fluid template paths; all templates get the current :php:`FormRuntime`
assigned as :code:`form` and the :php:`FinisherVariableProvider` assigned
as :code:`finisherVariableProvider`.
.. _apireference-finisheroptions-emailfinisher-options-variables:
.. confval:: variables
:name: emailfinisher-variables
:type: array
:required: false
:default: `undefined`
Associative array of variables which are available inside the Fluid template.
.. _concepts-finishers-emailfinisher-yaml:
Email finishers in the YAML form definition
===========================================
This finisher sends an email to one recipient.
EXT:form has two email finishers with identifiers
`EmailToReceiver` and `EmailToSender`.
.. literalinclude:: _codesnippets/_form.yaml
:caption: public/fileadmin/forms/my_form.yaml
.. _apireference-finisheroptions-emailfinisher:
Using Email finishers in PHP code
=================================
Developers can create a confirmation finisher by using the key `EmailToReceiver`
or `EmailToSender`.
.. literalinclude:: _codesnippets/_finisher.php.inc
:language: php
This finisher is implemented in :php:`TYPO3\CMS\Form\Domain\Finishers\EmailFinisher`.
.. _concepts-finishers-emailfinisher-bcc-recipients:
Working with BCC recipients
===========================
Email finishers can work with different recipient types, including Carbon Copy
(CC) and Blind Carbon Copy (BCC). Depending on the configuration of your server
and TYPO3 instance, it may not be possible to send emails to BCC recipients.
The :php:`$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport_sendmail_command']`
configuration value is important here. As documented in :ref:`CORE API <t3coreapi:mail-configuration-sendmail>`,
TYPO3 recommends using the parameter :php:`-bs` (instead of :php:`-t -i`) with
:php:`sendmail`. The parameter :php:`-bs` tells TYPO3 to use the SMTP standard
so that BCC recipients are properly set. `Symfony <https://symfony.com/doc/current/mailer.html#using-built-in-transports>`__
also mentions the :php:`-t` parameter problem. Since TYPO3 7.5
(`#65791 <https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/7.5/Feature-65791-UsePHPConfiguredSendmailPathIfMAILtransportSendmailIsActive.html>`__)
the :php:`transport_sendmail_command` is automatically set from the PHP runtime
configuration and saved. If you have problems sending emails to BCC
recipients, this could be the solution.
.. _concepts-finishers-emailfinisher-fluidemail:
About FluidEmail
================
.. versionchanged:: 12.0
The :php:`EmailFinisher` always sends email via :php:`FluidEmail`.
The FluidEmail finisher allows emails to be sent in a standardized way.
The finisher has an :yaml:`option` property :yaml:`title` that adds an email title to the default
FluidEmail template. Variables can be used in options using the bracket syntax.
These variables can be overwritten by FlexForm configuration in the form plugin
Use these options to customize the fluid templates:
* :yaml:`templateName`: The template name (for both HTML and plaintext, without the
extension)
* :yaml:`templateRootPaths`: The paths to the templates
* :yaml:`partialRootPaths`: The paths to the partials
* :yaml:`layoutRootPaths`: The paths to the layouts
.. note::
The field :yaml:`templatePathAndFilename` is no longer evaluated.
Here is an example finisher configuration:
.. literalinclude:: _codesnippets/_example-email.yaml
:caption: public/fileadmin/forms/my_form_with_email_finisher.yaml
These template files must exist:
* :file:`EXT:my_site_package/Resources/Private/Templates/Email/ContactForm.html`
* :file:`EXT:my_site_package/Resources/Private/Templates/Email/ContactForm.txt`
@@ -0,0 +1,15 @@
identifier: contact
type: Form
prototypeName: standard
finishers:
-
identifier: EmailToSender
options:
subject: 'Your Message: {message}'
title: 'Hello {name}, your confirmation'
templateName: ContactForm
templateRootPaths:
100: 'EXT:my_site_package/Resources/Private/Templates/Email/'
partialRootPaths:
100: 'EXT:my_site_package/Resources/Private/Partials/Email/'
addHtmlPart: true
@@ -0,0 +1,19 @@
<?php
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
class SomeClass
{
private function addEmailToReceiverFinisher(FormDefinition $formDefinition)
{
$formDefinition->createFinisher('EmailToReceiver', [
'subject' => 'Your message',
'recipients' => [
'your.company@example.com' => 'Your Company name',
'ceo@example.com' => 'CEO'
],
'senderAddress' => $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress'],
'senderName' => $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName'],
]);
}
}
@@ -0,0 +1,14 @@
identifier: example-form
label: 'example'
type: Form
finishers:
-
identifier: EmailToReceiver
options:
subject: 'Your message'
recipients:
your.company@example.com: 'Your Company name'
ceo@example.com: 'CEO'
senderAddress: 'form@example.com'
senderName: 'form submitter'
@@ -0,0 +1,116 @@
.. include:: /Includes.rst.txt
.. _concepts-finishers-flashmessagefinisher:
=====================
FlashMessage finisher
=====================
The "FlashMessage finisher" is a basic finisher that adds a message to the
FlashMessageContainer.
.. contents:: Table of contents
.. note::
This finisher cannot be used in the backend form editor. It can only be used
in a form definition YAML file or programmatically.
.. include:: /Includes/_NoteFinisher.rst
.. _apireference-finisheroptions-flashmessagefinisher-options:
FlashMessage finisher options
=============================
The following options can be set (in the form definition YAML or
programmatically):
.. _apireference-finisheroptions-flashmessagefinisher-options-messagebody:
.. confval:: messageBody
:name: flashmessagefinisher-messageBody
:type: string
:required: true
The flash message. May contain placeholders like `%s` that
are replaced with `messageArguments`.
.. _apireference-finisheroptions-flashmessagefinisher-options-messagetitle:
.. confval:: messageTitle
:name: flashmessagefinisher-messageTitle
:type: string
:default: `''`
If set, is the flash message title.
.. _apireference-finisheroptions-flashmessagefinisher-options-messagearguments:
.. confval:: messageArguments
:name: flashmessagefinisher-messageArguments
:type: array
:default: `[]`
If `messageBody` contains placeholders (like `%s`), they will be replaced
by these.
.. _apireference-finisheroptions-flashmessagefinisher-options-messagecode:
.. confval:: messageCode
:name: flashmessagefinisher-messageCode
:type: ?int
:default: `null`
A unique code to identify the message. By convention, the
unix time stamp at the time when the message is created is used,
for example `1758455932`.
.. _apireference-finisheroptions-flashmessagefinisher-options-severity:
.. confval:: severity
:name: flashmessagefinisher-severity
:type: :php:`\TYPO3\CMS\Core\Type\ContextualFeedbackSeverity`
:default: `ContextualFeedbackSeverity::OK`
The severity influences the display (color and icon) of the flash message.
.. confval:: translation.propertiesExcludedFromTranslation
:name: flashmessagefinisher-translation-propertiesExcludedFromTranslation
:type: array
:default: `[]`
Defines a list of finisher option properties to be excluded from
translation.
If set, these properties will not be processed by the
:php-short:`\TYPO3\CMS\Form\Service\TranslationService` during translation
of finisher options. This prevents their values from being replaced by
translated equivalents, even if translations exist for those options.
This option is usually generated automatically as soon as FlexForm overrides
are in place and normally does not need to be set manually in the form
definition.
See `Skip translation of overridden form finisher options <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-confirmationfinisher-yaml-propertiesexcludedfromtranslation>`_
for an example.
.. _concepts-finishers-flashmessagefinisher-yaml:
FlashMessage finisher in a YAML form definition
===============================================
.. literalinclude:: _codesnippets/_form.yaml
:caption: public/fileadmin/forms/my_form.yaml
.. _apireference-finisheroptions-flashmessagefinisher:
Using FlashMessage finishers in PHP code
========================================
Developers can use the finisher key `FlashMessage` to create
flash message finishers in their own classes:
.. literalinclude:: _codesnippets/_finisher.php.inc
:language: php
This finisher is implemented in :php:`TYPO3\CMS\Form\Domain\Finishers\FlashMessageFinisher`.
@@ -0,0 +1,18 @@
<?php
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
class SomeClass
{
private function addFlashMessageFinisher(FormDefinition $formDefinition, string $message)
{
$formDefinition->createFinisher('FlashMessage', [
'messageTitle' => 'Merci',
'messageCode' => 201905041245,
'messageBody' => 'Thx for using %s',
'messageArguments' => ['TYPO3'],
'severity' => ContextualFeedbackSeverity::OK,
]);
}
}
@@ -0,0 +1,14 @@
identifier: example-form
label: 'example'
type: Form
finishers:
-
identifier: FlashMessage
options:
messageTitle: 'Merci'
messageCode: 201905041245
messageBody: 'Thx for using %s'
messageArguments:
- 'TYPO3'
severity: 0
@@ -0,0 +1,62 @@
.. include:: /Includes.rst.txt
.. _concepts-finishers-ready-to-use:
.. _apireference-finisheroptions:
======================
Ready-to-use finishers
======================
The TYPO3 Form Framework provides several built-in finishers that can be
used out of the box. These handle common post submission tasks such as
sending emails, showing confirmation messages, and saving data.
In addition, third-party extensions may provide further finishers, which
can be found in the `TYPO3 Extension Repository (TER) <https://extensions.typo3.org/>`_.
.. card-grid::
:columns: 1
:columns-md: 2
:gap: 4
:class: pb-4
:card-height: 100
.. card:: `Closure finisher <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-closurefinisher>`_
Executes a custom PHP closure after a successful submission—use
for ad-hoc logic without creating a full class.
.. card:: `Confirmation finisher <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-confirmationfinisher>`_
Renders a confirmation/thank-you message (or view) once the form
is submitted.
.. card:: `DeleteUploads finisher <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-deleteuploadsfinisher>`_
Removes files uploaded during the submission—useful if after
emailing them you dont want to keep the files on the server.
.. card:: `Email finisher <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-emailfinisher>`_
Sends an email with the submitted data; supports Fluid
templates and placeholders for field values.
.. card:: :doc:`Flash message finisher <FlashMessageFinisher/Index>`
Shows a flash message to the user after submit (e.g., success or
info notice).
.. card:: `Redirect finisher <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-redirectfinisher>`_
Redirects to another page or route after submit; must be last
finisher since it stops subsequent finishers.
.. card:: `SaveToDatabase finisher <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-savetodatabasefinisher>`_
Persists submitted form values to a database table according to
your mapping/configuration.
.. toctree::
:hidden:
:glob:
*/Index
@@ -0,0 +1,135 @@
.. include:: /Includes.rst.txt
.. _concepts-finishers-redirectfinisher:
.. _finishers-redirect:
=================
Redirect finisher
=================
This finisher redirects the user to a particular page after the form has been submitted.
Parameters can be added to the URL.
.. contents:: Table of contents
.. important::
Finishers are executed in the order defined in your form definition.
This finisher stops the execution of all subsequent finishers in order to perform
the redirect. Therefore, this finisher should always be the last finisher to be
executed. Finishers placed after this one in the form definition will be ignored.
.. _apireference-finisheroptions-redirectfinisher-options:
Redirect finisher options
=========================
.. _apireference-finisheroptions-redirectfinisher-options-pageuid:
.. confval:: Page: [pageUid]
:name: redirectfinisher-pageUid
:type: int
:required: true
:default: `1`
ID of the page to redirect to. Button :guilabel:`Page` can be used to select
a page from the page tree.
.. _apireference-finisheroptions-redirectfinisher-options-additionalparameters:
.. confval:: Additional parameters: [additionalParameters]
:name: redirectfinisher-additionalParameters
:type: string
:required: false
:default: `''`
URL parameters which will be appended to the URL.
.. _apireference-finisheroptions-redirectfinisher-options-fragment:
.. confval:: URL fragment: [fragment]
:name: redirectfinisher-fragment
:type: string
:required: false
:default: `''`
ID of a content element identifier or a custom fragment
identifier. This will be appended to the URL and used as section anchor.
Adds a fragment (e.g. :html:`#c9` or :html:`#foo`) to the redirect link.
The :html:`#` character can be omitted.
.. _apireference-finisheroptions-redirectfinisher-options-additional:
Additional redirect finisher options
====================================
These options can be set in the form definition YAML or
programmatically in the options array. They cannot be set in the backend form editor:
.. _apireference-finisheroptions-redirectfinisher-options-delay:
.. confval:: delay
:name: redirectfinisher-delay
:type: int
:required: false
:default: `0`
The redirect delay in seconds.
.. _apireference-finisheroptions-redirectfinisher-options-statuscode:
.. confval:: statusCode
:name: redirectfinisher-statusCode
:type: int
:required: false
:default: `303`
The HTTP status code for the redirect. Default is "303 See Other".
.. confval:: translation.propertiesExcludedFromTranslation
:name: redirectfinisher-translation-propertiesExcludedFromTranslation
:type: array
:default: `[]`
Defines a list of finisher option properties to be excluded from
translation.
If set, these properties are not processed by the
:php-short:`\TYPO3\CMS\Form\Service\TranslationService` during translation.
This prevents their values from being replaced by
translated equivalents, even if translations exist for those options.
This option is usually generated automatically shen FlexForm overrides
are in place and normally does not need to be set manually in the form
definition.
See `Skip translation of overridden form finisher options <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-confirmationfinisher-yaml-propertiesexcludedfromtranslation>`_
for an example.
.. _concepts-finishers-redirectfinisher-yaml:
Redirect finisher in a YAML form definition
===========================================
.. literalinclude:: _codesnippets/_form.yaml
:caption: public/fileadmin/forms/my_form.yaml
.. _concepts-finishers-redirectfinisher-last:
Example: Load the redirect finisher last
========================================
.. literalinclude:: _codesnippets/_example-redirect.yaml
:caption: public/fileadmin/forms/my_form_with_multiple_finishers.yaml
.. _apireference-finisheroptions-redirectfinisher:
Using a Redirect finisher in PHP code
=====================================
Developers can use the finisher key `Redirect` to create redirect finishers in their own classes:
.. literalinclude:: _codesnippets/_finisher.php.inc
:language: php
This finisher is implemented in :php:`TYPO3\CMS\Form\Domain\Finishers\RedirectFinisher`.
@@ -0,0 +1,17 @@
identifier: contact
type: Form
prototypeName: standard
finishers:
-
identifier: EmailToSender
options:
subject: 'Your Message: {message}'
## ...
-
identifier: DeleteUploads
-
# Attention! The Redirect finisher stops the execution of all finishers
identifier: Redirect
options:
pageUid: 1
additionalParameters: 'param1=value1&param2=value2'
@@ -0,0 +1,14 @@
<?php
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
class SomeClass
{
private function addRedirectFinisher(FormDefinition $formDefinition)
{
$formDefinition->createFinisher('Redirect', [
'pageUid' => 1,
'additionalParameters' => 'param1=value1&param2=value2',
]);
}
}
@@ -0,0 +1,10 @@
identifier: example-form
label: 'example'
type: Form
finishers:
-
identifier: Redirect
options:
pageUid: 1
additionalParameters: 'param1=value1&param2=value2'
@@ -0,0 +1,245 @@
.. include:: /Includes.rst.txt
.. _concepts-finishers-savetodatabasefinisher:
=======================
SaveToDatabase finisher
=======================
The "SaveToDatabase finisher" saves data from a submitted form into a
database table.
.. contents:: Table of contents
.. note::
This finisher cannot be used in the backend form editor. It can only be
used in a form definition YAML file or programmatically.
.. include:: /Includes/_NoteFinisher.rst
.. _apireference-finisheroptions-savetodatabasefinisher-options:
SaveToDatabase finisher options
===============================
The finisher options can be set in the form definition YAML file or
programmatically:
.. _apireference-finisheroptions-savetodatabasefinisher-options-table:
.. confval:: table
:name: savetodatabasefinisher-table
:type: string
:required: true
Insert or update values in this table.
.. _apireference-finisheroptions-savetodatabasefinisher-options-mode:
.. confval:: mode
:name: savetodatabasefinisher-mode
:type: string
:default: `'insert'`
`insert`
will create a new database row with the values from the submitted form
and/or some predefined values. See also :confval:`savetodatabasefinisher-elements` and
:confval:`savetodatabasefinisher-databaseColumnMappings`.
`update`
will update a database row with the values from the submitted form
and/or some predefined values. In this case :confval:`savetodatabasefinisher-whereClause` is required.
.. _apireference-finisheroptions-savetodatabasefinisher-options-whereclause:
.. confval:: whereClause
:name: savetodatabasefinisher-whereClause
:type: array
:required: true (if mode = update)
:default: `[]`
The ``where`` clause for a database update action.
.. _apireference-finisheroptions-savetodatabasefinisher-options-elements:
.. confval:: elements
:name: savetodatabasefinisher-elements
:type: array
:required: true
Use `options.elements` to map form element values to database columns (they must exist).
Each key in `options.elements` has to match a form element identifier.
The value of each key in `options.elements` is an array containing additional information.
.. _apireference-finisheroptions-savetodatabasefinisher-options-elements-mapondatabasecolumn:
.. confval:: elements.<formElementIdentifier>.mapOnDatabaseColumn
:name: savetodatabasefinisher-elements-mapOnDatabaseColumn
:type: string
:required: true
The value from the submitted form element with the identifier
`<formElementIdentifier>` will be written into this database column.
.. _apireference-finisheroptions-savetodatabasefinisher-options-elements-skipifvalueisempty:
.. confval:: elements.<formElementIdentifier>.skipIfValueIsEmpty
:name: savetodatabasefinisher-elements-skipIfValueIsEmpty
:type: bool
:default: `false`
Set this to true if the database column should not be written if the value from the
submitted form element with the identifier `<formElementIdentifier>` is empty
(e.g. for password fields). Empty means strings without content, whitespace is valid content.
.. _apireference-finisheroptions-savetodatabasefinisher-options-elements-hashed:
.. confval:: elements.<formElementIdentifier>.hashed
:name: savetodatabasefinisher-elements-hashed
:type: bool
:default: `false`
Set this to true if the value from the submitted form element should be hashed before
writing into the database.
.. _apireference-finisheroptions-savetodatabasefinisher-options-elements-savefileidentifierinsteadofuid:
.. confval:: elements.<formElementIdentifier>.saveFileIdentifierInsteadOfUid
:name: savetodatabasefinisher-elements-saveFileIdentifierInsteadOfUid
:type: bool
:default: `false`
By default, the uid of the FAL object will be written into the database column.
Set this to true if you want to store the FAL identifier
(e.g. `1:/user_uploads/some_uploaded_pic.jpg`) instead.
This only applies for form elements which create a FAL object like
`FileUpload` or `ImageUpload`.
.. _apireference-finisheroptions-savetodatabasefinisher-options-elements-dateformat:
.. confval:: elements.<formElementIdentifier>.dateFormat
:name: savetodatabasefinisher-elements-dateFormat
:type: string
:default: `'U'`
If the internal datatype is :php:`\DateTime` (true for the form element type
:yaml:`Date`), the object needs to be converted into a string.
This option defines the format of the date. You can use any format accepted by
the PHP :php:`date()` function.
Default is `'U'` (Unix timestamp).
.. _apireference-finisheroptions-savetodatabasefinisher-options-databasecolumnmappings:
.. confval:: databaseColumnMappings
:name: savetodatabasefinisher-databaseColumnMappings
:type: array
:default: `[]`
Use this to map database columns to values.
Each key within `options.databaseColumnMappings` has to match an existing database column.
Each value in `options.databaseColumnMappings` is an array with
additional information.
This mapping is done *before* :confval:`savetodatabasefinisher-elements` are mapped.
If you map both, the value from :confval:`savetodatabasefinisher-elements` will override the
:confval:`savetodatabasefinisher-databaseColumnMappings-value`.
.. _apireference-finisheroptions-savetodatabasefinisher-options-databasecolumnmappings-value:
.. confval:: databaseColumnMappings.<databaseColumnName>.value
:name: savetodatabasefinisher-databaseColumnMappings-value
:type: string
:required: true
The value which will be written to the database column.
You can also use the :ref:`FormRuntime accessor feature
<concepts-finishers-customfinisherimplementations-accessingoptions-formruntimeaccessor>`
to access properties from the `FormRuntime`, e.g. `{<formElementIdentifier>}`.
.. _apireference-finisheroptions-savetodatabasefinisher-options-databasecolumnmappings-skipifvalueisempty:
.. confval:: databaseColumnMappings.<databaseColumnName>.skipIfValueIsEmpty
:name: savetodatabasefinisher-databaseColumnMappings-skipIfValueIsEmpty
:type: bool
:default: `false`
Set this to true if the database column should not be written if the value from
:confval:`savetodatabasefinisher-databaseColumnMappings-value` is empty.
.. confval:: translation.propertiesExcludedFromTranslation
:name: savetodatabasefinisher-translation-propertiesExcludedFromTranslation
:type: array
:default: `[]`
Defines a list of finisher option properties to be excluded from
translation.
If set, these properties are not processed by the
:php-short:`\TYPO3\CMS\Form\Service\TranslationService` during translation.
This prevents the values from being replaced by
translated equivalents, even if translations exist for those options.
This option is usually generated when FlexForm overrides
exist and normally does not need to be set manually in the form
definition.
See `Skip translation of overridden form finisher options <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-confirmationfinisher-yaml-propertiesexcludedfromtranslation>`_
for an example.
.. _concepts-finishers-savetodatabasefinisher-yaml:
SaveToDatabase finisher in a YAML form definition
=================================================
This finisher saves data from a submitted form into a database table.
.. literalinclude:: _codesnippets/_form.yaml
:linenos:
:caption: public/fileadmin/forms/my_form.yaml
.. _concepts-finishers-savetodatabasefinisher-example-news:
Example: adding uploads to ext:news (fal_related_files and fal_media):
======================================================================
.. literalinclude:: _codesnippets/_example-fal-uploads_news.yaml
:linenos:
:caption: public/fileadmin/forms/my_form_with_multiple_finishers.yaml
.. _apireference-finisheroptions-savetodatabasefinisher:
Using a SaveToDatabase finisher in PHP code
================================================
Developers can use the finisher key `SaveToDatabase` to create
flash message finishers in their own classes:
.. literalinclude:: _codesnippets/_finisher.php.inc
:language: php
:linenos:
This finisher is implemented in :php:`TYPO3\CMS\Form\Domain\Finishers\SaveToDatabaseFinisher`.
.. _concepts-finishers-savetodatabasefinisher-multiple:
Multiple database operations
============================
You can use options to perform multiple database operations.
Example form definition file (performs inserts):
.. literalinclude:: _codesnippets/_example-fal-uploads_news.yaml
:linenos:
:caption: public/fileadmin/forms/my_form_with_multiple_finishers.yaml
Using PHP code (performs an update):
.. literalinclude:: _codesnippets/_finisher.php.inc
:language: php
:linenos:
You can access inserted UIDs with '{SaveToDatabase.insertedUids.<theArrayKeyNumberInsideOptions>}'.
If you perform an insert operation, the inserted values will be stored in the FinisherVariableProvider.
<theArrayKeyNumberInOptions> references the numeric options.* key.
@@ -0,0 +1,73 @@
-
identifier: SaveToDatabase
options:
-
table: tx_news_domain_model_news
mode: insert
elements:
my-field:
mapOnDatabaseColumn: bodytext
imageupload-1:
mapOnDatabaseColumn: fal_media
fileupload-1:
mapOnDatabaseColumn: fal_related_files
databaseColumnMappings:
pid:
value: 3
tstamp:
value: '{__currentTimestamp}'
datetime:
value: '{__currentTimestamp}'
crdate:
value: '{__currentTimestamp}'
hidden:
value: 1
-
table: sys_file_reference
mode: insert
elements:
imageupload-1:
mapOnDatabaseColumn: uid_local
skipIfValueIsEmpty: true
databaseColumnMappings:
tablenames:
value: tx_news_domain_model_news
fieldname:
value: fal_media
tstamp:
value: '{__currentTimestamp}'
crdate:
value: '{__currentTimestamp}'
showinpreview:
value: 1
uid_foreign:
value: '{SaveToDatabase.insertedUids.0}'
-
table: sys_file_reference
mode: insert
elements:
fileupload-1:
mapOnDatabaseColumn: uid_local
skipIfValueIsEmpty: true
databaseColumnMappings:
tablenames:
value: tx_news_domain_model_news
fieldname:
value: fal_related_files
tstamp:
value: '{__currentTimestamp}'
crdate:
value: '{__currentTimestamp}'
uid_foreign:
value: '{SaveToDatabase.insertedUids.0}'
-
table: sys_file_reference
mode: update
whereClause:
uid_foreign: '{SaveToDatabase.insertedUids.0}'
uid_local: 0
databaseColumnMappings:
pid:
value: 0
uid_foreign:
value: 0
@@ -0,0 +1,30 @@
<?php
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Form\Domain\Finishers\ClosureFinisher;
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
class SomeClass
{
private function addDeleteUploadsFinisherWithMessage(FormDefinition $formDefinition, string $message)
{
$formDefinition->createFinisher('SaveToDatabase', [
1 => [
'table' => 'my_table',
'mode' => 'insert',
'databaseColumnMappings' => [
'some_column' => ['value' => 'cool'],
],
],
2 => [
'table' => 'my_other_table',
'mode' => 'update',
'whereClause' => [
'pid' => 1,
],
'databaseColumnMappings' => [
'some_other_column' => ['value' => '{SaveToDatabase.insertedUids.1}'],
],
],
]);
}
}
@@ -0,0 +1,31 @@
<?php
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Form\Domain\Finishers\ClosureFinisher;
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
class SomeClass
{
private function addDeleteUploadsFinisherWithMessage(FormDefinition $formDefinition, string $message)
{
$formDefinition->createFinisher('SaveToDatabase', [
'table' => 'fe_users',
'mode' => 'update',
'whereClause' => [
'uid' => 1,
],
'databaseColumnMappings' => [
'pid' => ['value' => 1],
],
'elements' => [
'textfield-identifier-1' => ['mapOnDatabaseColumn' => 'first_name'],
'textfield-identifier-2' => ['mapOnDatabaseColumn' => 'last_name'],
'textfield-identifier-3' => ['mapOnDatabaseColumn' => 'username'],
'advancedpassword-1' => [
'mapOnDatabaseColumn' => 'password',
'skipIfValueIsEmpty' => true,
'hashed' => true
],
],
]);
}
}
@@ -0,0 +1,22 @@
identifier: example-form
label: 'example'
type: Form
finishers:
-
identifier: SaveToDatabase
options:
1:
table: 'my_table'
mode: insert
databaseColumnMappings:
some_column:
value: 'cool'
2:
table: 'my_other_table'
mode: update
whereClause:
pid: 1
databaseColumnMappings:
some_other_column:
value: '{SaveToDatabase.insertedUids.1}'
@@ -0,0 +1,28 @@
identifier: example-form
label: 'example'
type: Form
finishers:
-
identifier: SaveToDatabase
options:
table: 'fe_users'
mode: update
whereClause:
uid: 1
databaseColumnMappings:
tstamp:
value: '{__currentTimestamp}'
pid:
value: 1
elements:
textfield-identifier-1:
mapOnDatabaseColumn: 'first_name'
textfield-identifier-2:
mapOnDatabaseColumn: 'last_name'
textfield-identifier-3:
mapOnDatabaseColumn: 'username'
advancedpassword-1:
mapOnDatabaseColumn: 'password'
skipIfValueIsEmpty: true
hashed: true
@@ -0,0 +1,113 @@
.. include:: /Includes.rst.txt
.. _concepts-formdefinition-vs-formconfiguration:
Form configuration vs. form definition
======================================
Up to this point, we have mainly looked at form framework configuration.
In short, **form configuration** is based on *prototypes* and allows you to define:
- which form elements, finishers, and validators are available to the system,
- how they are pre-configured,
- how they are displayed in the frontend and backend.
However, a second important part of the form framework is **form definition**,
which is configuration but for *specific* forms, for example the ones users define. Form
definition includes:
- form elements and their validators,
- the order of the form elements on the form
- the finishers that are fired when the form is submitted
- values of form element properties.
In other words, a ``Text`` form element would be defined in **form configuration**
but a ``Text`` form element located on page 1 at position 1 of a specific form
would be defined in a **form definition**. A **form definition** might also define
a placeholder (HTML attribute) with a value of "Your name
here" in a form element. Form definitions are created by the backend ``form editor``.
Example form definition (for a specific form)
---------------------------------------------
.. code-block:: yaml
identifier: ext-form-simple-contact-form-example
label: 'Simple Contact Form'
prototype: standard
type: Form
finishers:
-
identifier: EmailToReceiver
options:
subject: 'Your message'
recipients:
your.company@example.com: 'Your Company name'
ceo@example.com: 'CEO'
senderAddress: '{email}'
senderName: '{name}'
renderables:
-
identifier: page-1
label: 'Contact Form'
type: Page
renderables:
-
identifier: name
label: 'Name'
type: Text
properties:
fluidAdditionalAttributes:
placeholder: 'Name'
defaultValue: ''
validators:
-
identifier: NotEmpty
-
identifier: subject
label: 'Subject'
type: Text
properties:
fluidAdditionalAttributes:
placeholder: 'Subject'
defaultValue: ''
validators:
-
identifier: NotEmpty
-
identifier: email
label: 'Email'
type: Text
properties:
fluidAdditionalAttributes:
placeholder: 'Email address'
defaultValue: ''
validators:
-
identifier: NotEmpty
-
identifier: EmailAddress
-
identifier: message
label: 'Message'
type: Textarea
properties:
fluidAdditionalAttributes:
placeholder: ''
defaultValue: ''
validators:
-
identifier: NotEmpty
-
identifier: hidden
label: 'Hidden Field'
type: Hidden
-
identifier: summarypage
label: 'Summary page'
type: SummaryPage
@@ -0,0 +1,579 @@
.. include:: /Includes.rst.txt
.. _concepts-formeditor:
Form editor
===========
.. _concepts-formeditor-general:
What does it do?
----------------
The ``form editor`` is a powerful graphical user interface in the TYPO3 backend
which allows editors to create ``form definitions`` without writing a single line
of code. These ``form definitions`` are used by the frontend process to
render beautiful forms.
The ``form editor`` is a modular interface which consists of the following
components:
- Stage: main visual component of the backend ``form editor`` where displaying
form elements in an abstract view or a frontend preview (in the middle of the ``form editor``)
- Tree: displays the structure of the form as a tree (on the left)
- Inspector: context specific toolbar which displays
form element options and where options can be edited (on the right)
- Core: core functionality of the ``form editor``
- ViewModel: defines and controls the visual display
- Mediator: delegates component events
- Modals: processes modals
- FormEditor: provides API functions
- Helper: helper functions for the manipulation of DOM elements
The ``Modals``, ``Inspector``, and ``Stage`` components
can be modified by configuration. The ``Inspector`` component
is modular and extremely flexible. Integrators can add
``inspector editors`` (input fields of different types)
to allow backend editors to alter form element
options.
The diagram below shows Javascript module interaction between the form editor and the
core, viewmodel and mediator.
.. figure:: ../../Images/javascript_module_interaction.png
:alt: JavaScript module interaction
JavaScript module interaction
The ``form editor`` configuration is under the following configuration path:
.. code-block:: yaml
prototypes:
standard:
formEditor:
Here you can configure different aspects of the ``form editor`` under the following
configuration paths:
.. code-block:: yaml
prototypes:
standard:
formElementsDefinition:
<formElementTypeIdentifier>:
formEditor:
finishersDefinition:
<finisherIdentifier>
formEditor:
validatorsDefinition:
<validatorIdentifier>
formEditor:
.. _concepts-formeditor-components-in-detail:
Form editor components in detail
--------------------------------
.. _concepts-formeditor-stage:
Stage
^^^^^
The ``Stage`` is the central visual component of the form editor and it
can display form elements in two different modes:
- abstract view: all the form elements on a ``Page`` (a step) presented in an
abstract way,
- frontend preview: renders the form as it will be displayed in
the frontend (to render the form exactly the same as in the frontend, make sure
your frontend CSS is loaded in the backend)
By default, the frontend templates of :t3ext:`form` are based on `Bootstrap`_.
Since the backend of TYPO3 CMS also depends on `Bootstrap`_,
the corresponding CSS files will already loaded in the backend.
Nevertheless, some CSS is overridden and extended in order
to meet the specific needs of the TYPO3 backend, meaning frontend preview
(in the backend) could differ compared to the "real" frontend.
If your frontend preview requires additional CSS or a CSS framework
then go ahead and configure a specific ``prototype`` accordingly.
Beside the frontend templates, there are also templates for the abstract
view, i.e. you can customize the rendering of the abstract view for each
form element. If you have created your own form elements, in most cases you
will fall back to the already existing Fluid templates. But remember, you
are always able to create your own Fluid templates and adapt the abstract view
to suit your needs.
For more information, read the following chapter: ':ref:`Common abstract view form element templates<apireference-formeditor-stage-commonabstractformelementtemplates>`'.
.. _Bootstrap: https://getbootstrap.com/
.. _concepts-formeditor-inspector:
Inspector
^^^^^^^^^
The ``Inspector`` is on the right side of the ``form editor``. It is a modular,
flexible, and context-specific toolbar
and depends on which form element is currently selected. The ``Inspector``
is where you can edit form element options using ``inspector editors``.
The interface is easily customized by YAML configuration. You can define form element
properties and how they can be edited.
You can edit form element properties (like ``properties.placeholder``)
as well as ``property collections``. They are defined at the form element level
in the YAML configuration file. There are two types of ``property collections``:
- validators
- finishers
``Property collections`` are also configured by ``inspector editors`` and this
allows you to do some cool stuff. Imagine that you have a "Number range" validator with
two validator options "Minimum" and "Maximum" and two form elements, "Age
spouse" and "Age infant". You could set the validator for both form elements,
but make "Minimum" non-editable and pre-fill "Maximum" with a value for the "Age
infant" form element only and not the "Age spouse" form element.
.. _concepts-formeditor-translation-formeditor:
Translation of the form editor
------------------------------
All option values below the following configuration keys can be translated:
.. code-block:: yaml
prototypes:
standard:
formEditor:
formElementsDefinition:
<formElementTypeIdentifier>:
formEditor:
finishersDefinition:
<finisherIdentifier>
formEditor:
validatorsDefinition:
<validatorIdentifier>
formEditor:
The ``form editor`` translation files are loaded as follows:
.. code-block:: yaml
prototypes:
standard:
formEditor:
translationFiles:
# custom translation file
20: 'EXT:my_site_package/Resources/Private/Language/Database.xlf'
Option values are searched for in the defined
translation files. If a translation is found, the translated option value
will be used.
As an example, if the following option is defined:
.. code-block:: yaml
...
label: 'formEditor.elements.Form.editor.finishers.label'
...
The translation key ``formEditor.elements.Form.editor.finishers.label``
is first searched for in the file
``20: 'EXT:my_site_package/Resources/Private/Language/Database.xlf'``
and then in the file ``10: 'EXT:form/Resources/Private/Language/Database.xlf'``
(loaded by default by EXT:form). If nothing is found, the option value will be
displayed unmodified.
.. _concepts-formeditor-customization-formeditor:
Customization of the form editor
--------------------------------
The form editor can be customized by YAML
configuration in the configuration. The configuration is not stored in one central configuration
file. Instead, configuration is defined for each form element (see
`EXT:form/form/Configuration/Yaml/FormElements/`). In addition,
the :yaml:`Form` element itself (see `EXT:form/Configuration/Yaml/FormElements/Form.yaml`)
has some basic configuration.
A common customization is to remove form elements from the form
editor. Unlike other TYPO3 modules, the form editor cannot be configured
using backend user groups and `Access Lists` - it can only be done by YAML configuration.
Quite often, integrators tend to unset form elements as shown below.
In this example, the :yaml:`AdvancedPassword` form element is completely removed from
the form framework. Integrators and developers will no longer be able to use
the :yaml:`AdvancedPassword` element in their YAML form definitions or via API.
.. code-block:: yaml
:linenos:
:emphasize-lines: 4
prototypes:
standard:
formElementsDefinition:
AdvancedPassword: null
The correct way is to unset the :ref:`group property <prototypes.prototypeIdentifier.formelementsdefinition.formelementtypeidentifier.formeditor.group>`.
This property defines which group in the ``form editor`` "new Element"
modal the form element should belong in. Unsetting this property will remove the
form element safely from the form editor:
.. code-block:: yaml
:linenos:
:emphasize-lines: 6
prototypes:
standard:
formElementsDefinition:
AdvancedPassword:
formEditor:
group: null
.. _concepts-formeditor-extending:
Extending the form editor
-------------------------
Learn :ref:`here <concepts-finishers-customfinisherimplementations-extend-gui>`
how to make finishers configurable in the backend form editor.
.. _concepts-formeditor-basicjavascriptconcepts:
Basic JavaScript concepts
-------------------------
The form framework was designed to be as extendable as possible. Sooner or
later, you will want to customize ``form editor`` components using
JavaScript. This is especially true if you want to create your own
``inspector editors``. In order to achieve this, you can implement your own
JavaScript modules. Those modules will include the required algorithms for
the ``inspector editors`` and the ``abstract view`` as well as your own
events.
.. _concepts-formeditor-basicjavascriptconcepts-registercustomjavascriptmodules:
Register custom JavaScript modules
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can use the following configuration YAML to register your JavaScript module.
.. code-block:: yaml
prototypes:
standard:
formEditor:
dynamicJavaScriptModules:
additionalViewModelModules:
10: '@my-vendor/my-site-package/backend/form-editor/view-model.js'
.. code-block:: php
# Configuration/JavaScriptModules.php
<?php
return [
'dependencies' => ['form'],
'imports' => [
'@myvendor/my-site-package/' => 'EXT:my_site_package/Resources/Public/JavaScript/',
],
];
In the configuration above, the JavaScript files have to be in the folder
``my_site_package/Resources/Public/JavaScript/backend/form-editor/view-model.js``.
The following example module is a template you can use containing the recommended setup.
.. code-block:: javascript
/**
* Module: @my-vendor/my-site-package/backend/form-editor/view-model.js
*/
import * as Helper from '@typo3/form/backend/form-editor/helper.js'
/**
* @private
*
* @var object
*/
let _formEditorApp = null;
/**
* @private
*
* @return object
*/
function getFormEditorApp() {
return _formEditorApp;
};
/**
* @private
*
* @return object
*/
function getPublisherSubscriber() {
return getFormEditorApp().getPublisherSubscriber();
};
/**
* @private
*
* @return object
*/
function getUtility() {
return getFormEditorApp().getUtility();
};
/**
* @private
*
* @param object
* @return object
*/
function getHelper() {
return Helper;
};
/**
* @private
*
* @return object
*/
function getCurrentlySelectedFormElement() {
return getFormEditorApp().getCurrentlySelectedFormElement();
};
/**
* @private
*
* @param mixed test
* @param string message
* @param int messageCode
* @return void
*/
function assert(test, message, messageCode) {
return getFormEditorApp().assert(test, message, messageCode);
};
/**
* @private
*
* @return void
* @throws 1491643380
*/
function _helperSetup() {
assert('function' === typeof Helper.bootstrap,
'The view model helper does not implement the method "bootstrap"',
1491643380
);
Helper.bootstrap(getFormEditorApp());
};
/**
* @private
*
* @return void
*/
function _subscribeEvents() {
getPublisherSubscriber().subscribe('some/eventName/you/want/to/handle', function(topic, args) {
myCustomCode();
});
};
/**
* @private
*
* @return void
*/
function myCustomCode() {
};
/**
* @public
*
* @param object formEditorApp
* @return void
*/
export function bootstrap(formEditorApp) {
_formEditorApp = formEditorApp;
_helperSetup();
_subscribeEvents();
};
.. _concepts-formeditor-basicjavascriptconcepts-events:
Events
^^^^^^
Event handling in :t3ext:`form` is based on the ``Publish/Subscribe Pattern``.
To learn more about this terrific pattern, see: https://addyosmani.com/resources/essentialjsdesignpatterns/book/.
Please note that the processing sequence of the subscribers cannot be
influenced. Furthermore, there is no information flow between the
subscribers. All events are asynchronous.
For more information, head to the API reference and read the section about
':ref:`Events<concepts-formeditor-basicjavascriptconcepts-events>`'.
.. _concepts-formeditor-basicjavascriptconcepts-formelementmodel:
FormElement model
^^^^^^^^^^^^^^^^^
In the JavaScript code, each form element is represented by a
``FormElement model``. This model can be seen as a copy of the ``form definition``
enriched with some additional data. The following example shows
you a ``form definition`` and, below it, the debug output of ``FormElement model``.
.. code-block:: yaml
identifier: javascript-form-element-model
label: 'JavaScript FormElement model'
type: Form
finishers:
-
identifier: EmailToReceiver
options:
subject: 'Your message: {subject}'
recipients:
your.company@example.com: 'Your Company name'
ceo@example.com: 'CEO'
senderAddress: '{email}'
senderName: '{name}'
replyToRecipients:
replyTo.company@example.com: 'Your Company name'
carbonCopyRecipients:
cc.company@example.com: 'Your Company name'
blindCarbonCopyRecipients:
bcc.company@example.com: 'Your Company name'
addHtmlPart: true
attachUploads: 'true'
translation:
language: ''
title: ''
renderables:
-
identifier: page-1
label: 'Contact Form'
type: Page
renderables:
-
identifier: name
label: Name
type: Text
properties:
fluidAdditionalAttributes:
placeholder: Name
defaultValue: ''
validators:
-
identifier: NotEmpty
.. code-block:: javascript
{
"identifier": "javascript-form-element-model",
"label": "JavaScript FormElement model",
"type": "Form",
"prototypeName": "standard",
"__parentRenderable": null,
"__identifierPath": "example-form",
"finishers": [
{
"identifier": "EmailToReceiver",
"options": {
"subject": "Your message: {subject}",
"recipients": {
"your.company@example.com": "Your Company name",
"ceo@example.com": "CEO"
},
"senderAddress": "{email}",
"senderName": "{name}",
"replyToRecipients": {
"replyTo.company@example.com": "Your Company name"
},
"carbonCopyRecipients": {
"cc.company@example.com": "Your Company name"
},
"blindCarbonCopyRecipients": {
"bcc.company@example.com": "Your Company name"
},
"addHtmlPart": true,
"attachUploads": true,
"translation": {
"language": ""
},
"title": ""
}
}
],
"renderables": [
{
"identifier": "page-1",
"label": "Contact Form",
"type": "Page",
"__parentRenderable": "example-form (filtered)",
"__identifierPath": "example-form/page-1",
"renderables": [
{
"identifier": "name",
"defaultValue": "",
"label": "Name",
"type": "Text",
"properties": {
"fluidAdditionalAttributes": {
"placeholder": "Name"
}
},
"__parentRenderable": "example-form/page-1 (filtered)",
"__identifierPath": "example-form/page-1/name",
"validators": [
{
"identifier": "NotEmpty"
}
]
}
]
}
]
}
For each form element which has child elements, there is a property
called ``renderables``. ``renderables`` are arrays of ``FormElement models``
of child elements.
The ``FormElement model`` is therefore a combination of the
of ``form definition`` data and some additional information:
- __parentRenderable
- __identifierPath
The following methods can be used to access ``FormElement model`` data:
- get()
- set()
- unset()
- on()
- off()
- getObjectData()
- toString()
- clone()
Head to the API reference to read more about
the :ref:`FormElement model<apireference-formeditor-basicjavascriptconcepts-formelementmodel>`.
@@ -0,0 +1,78 @@
.. include:: /Includes.rst.txt
.. _concepts-form-file-storages:
Form/ File storage
==================
Form definitions can also be stored in and shipped with your own
extensions and backend users can then
embed your forms. Furthermore, you can configure that your form
definitions:
- can be edited in the ``form editor``,
- can be deleted with the ``form manager``.
By default, all these options are turned off because dynamic content inside an
extension - possibly version-controlled - is not a good idea. There is also no
ACL system available.
**File uploads** are saved in file mounts. They are handled
as FAL objects. The file mounts for file uploads can be configured.
When adding/ editing a file upload element, backend users can select the
storage for the uploads.
Add your extension path as an additional file mount for form definitions as follows:
.. code-block:: yaml
persistenceManager:
allowedExtensionPaths:
10: EXT:my_site_package/Resources/Private/Forms/
Allow backend users to **edit** forms stored in your extension as follows:
.. code-block:: yaml
persistenceManager:
allowSaveToExtensionPaths: true
Allow backend users to **delete** forms stored in your extension as follows:
.. code-block:: yaml
persistenceManager:
allowDeleteFromExtensionPaths: true
The following YAML shows the default file mount setup for file (and image) uploads.
.. code-block:: yaml
prototypes:
standard:
formElementsDefinition:
FileUpload:
formEditor:
predefinedDefaults:
properties:
saveToFileMount: '1:/user_upload/'
editors:
400:
selectOptions:
10:
value: '1:/user_upload/'
label: '1:/user_upload/'
properties:
saveToFileMount: '1:/user_upload/'
ImageUpload:
formEditor:
predefinedDefaults:
properties:
saveToFileMount: '1:/user_upload/'
editors:
400:
selectOptions:
10:
value: '1:/user_upload/'
label: '1:/user_upload/'
@@ -0,0 +1,133 @@
.. include:: /Includes.rst.txt
.. _concepts-formmanager:
Form manager
============
.. _concepts-formmanager-general:
What does it do?
----------------
You will find the ``form manager`` in the backend :guilabel:`Web > Forms` backend
module. Editors can use the ``form manager`` to administer forms stored on file
mounts that they have access to. The ``form manager``:
- lists all forms
- allows users to create, edit, duplicate, and delete forms
- identifies the storage folder
- gives an overview of which pages the forms are on.
Creation and duplication of forms is made easier by a ``form wizard``.
The wizard guides the editor through form creation and offers a
variety of settings, such as the file
mount, the prototype, and start templates.
.. figure:: ../../Images/form_manager.png
:alt: The form manager
TYPO3 Backend with opened module 'Forms' displaying the form manager.
.. _concepts-formmanager-starttemplate:
Start templates
---------------
Editors can select a ``Start template`` when they are creating a new form. A
``Start template`` is a ``form definition`` which hasn't been assigned a
``prototypeName`` (the ``prototypeName`` property is normally used as the
foundation of a new form).
An integrator can create as many ``Start templates`` as they wish for a particular
``prototype``. After the ``Start templates`` have been defined the integrator can then:
- open :guilabel:`Web > Forms`
- create a new form by clicking on the appropriate button
- enter the 'Form name' and click the 'Advanced settings' checkbox
- select a ``Start template`` during the next steps
Integrators have to define ``Start templates`` so that they can be selected
by editors. Also, the same ``Start template``
can be used for several ``prototypes``. To do this, make sure the
``start template`` form elements are defined in the corresponding ``prototypes``.
For example, imagine an integrator has :ref:`configured<formmanager.selectablePrototypesConfiguration>`
a prototype called 'routing' which contains a form element of type
``<formElementTypeIdentifier>`` 'locationPicker'. The element is only
defined in this prototype. The integrator has created a ``Start template``
which contains the 'locationPicker' form element. A backend editor could now
select and use this ``Start template`` with the 'locationPicker' form element,
as long as the ``prototype`` is 'routing'. If the integrator
adds this form element to another ``prototype``, the process would
crash. The 'locationPicker' form element is only known to the 'routing'
``prototype``.
The following example shows a ``Start template``. A
``Start template`` requires at least the root form element
('Form') and a 'Page'.
.. code-block:: yaml
type: 'Form'
identifier: 'blankForm'
label: '[Blank Form]'
renderables:
-
type: 'Page'
identifier: 'page-1'
label: 'Page'
The ``form manager`` form wizard displays
a list of all :ref:`pre-configured<formmanager.selectableprototypesconfiguration.*.newformtemplates>`
``Start templates``.When a backend editor creates a form using a
``Start template``, a new ``form definition`` is generated based on that
``Start template``. The ``form definition`` ``propertyName`` will be that of the
chosen ``prototype``.The ``identifier`` of the root form element ('Form') is set
to the entered "Form name". This name is also used for the
property `` label`` of the 'Form' element. Finally, the ``form editor`` is
loaded and displays the newly created form.
.. _concepts-formmanager-translation-starttemplate:
Translation of the form manager
-------------------------------
All option values below the ``form editor`` key in the form configuration can be
translated:
.. code-block:: yaml
formManager:
The ``form manager`` translation files are loaded as follows:
.. code-block:: yaml
formManager:
translationFiles:
# custom translation file
20: 'EXT:my_site_package/Resources/Private/Language/Form/Database.xlf'
The process searches for each option value within all of the defined
translation files. If a translation is found, the translated option value
will be used in preference.
For the following option value:
.. code-block:: yaml
...
label: 'formManager.selectablePrototypesConfiguration.standard.label'
...
the process searches for the translation key ``formManager.selectablePrototypesConfiguration.standard.label``
in the file under key 20 ``20: 'EXT:my_site_package/Resources/Private/Language/Form/Database.xlf'``
and then the file in EXT:form ``10: 'EXT:form/Resources/Private/Language/Database.xlf'``
(loaded by default). If nothing is found, the option value will be
displayed unmodified.
@@ -0,0 +1,100 @@
.. include:: /Includes.rst.txt
.. _concepts-formplugin:
Form plugin
===========
.. _concepts-formplugin-general:
What does it do?
----------------
The ``form plugin`` allows you to assign a form to a page and view it in the
frontend. The form can have been created via the ``form editor`` or shipped with
your extension. Forms can be re-used throughout the TYPO3 installation and backend editors
can override form definitions. At the moment, only finisher options can be overridden but the
possibilities depend on the configuration of the underlying prototype.
Imagine that your form contains a redirect finisher. The redirect target is set
globally and valid for the whole ``form definition``. When they are adding the form
to a page, a backend editor can define a redirect target that is different to the
'global' form definition. This setting is only valid on the page containing the plugin.
Read more about changing :ref:`general<prototypes.prototypeIdentifier.formengine>`
and :ref:`specific form plugin configuration<prototypes.prototypeIdentifier.finishersdefinition.finisheridentifier.formengine>`.
.. _concepts-formplugin-exclude-override:
Exclude options from overrides
------------------------------
Sometimes it is useful to prevent options from being overridden by the
form plugin. You can do this by unsetting the options in your
general forms configuration YAML. To unset options use the YAML NULL (:yaml:`~`) value.
In this example, four ``EmailToReceiver`` finisher fields are unset. The
options will be removed from the form plugin but not the form editor.
.. code-block:: yaml
prototypes:
standard:
finishersDefinition:
EmailToReceiver:
FormEngine:
elements:
senderAddress: ~
senderName: ~
replyToRecipients: ~
translation: ~
.. _concepts-formplugin-translation-formengine:
Translation of form plugin
--------------------------
All option values under the following configuration keys can be
translated:
.. code-block:: yaml
prototypes:
standard:
finishersDefinition:
<finisherIdentifier>
formEngine:
``Form plugin`` translation files are loaded as follows:
.. code-block:: yaml
prototypes:
standard:
formEngine:
translationFiles:
# custom translation file
20: 'EXT:my_site_package/Resources/Private/Language/Database.xlf'
Each option value is searched for in the defined
translation files. If a translation is found, the translated option value
will be used.
Imagine that the following option value is defined:
.. code-block:: yaml
...
label: 'tt_content.finishersDefinition.EmailToReceiver.label'
...
The translation key
``tt_content.finishersDefinition.EmailToReceiver.label`` is first searched for in the file
``20: 'EXT:my_site_package/Resources/Private/Language/Database.xlf'`` and
then in the file 10: 'EXT:form/Resources/Private/Language/Database.xlf'
(loaded by EXT:form by default). If nothing is found, the option value will be
displayed unmodified.
@@ -0,0 +1,937 @@
.. include:: /Includes.rst.txt
.. _concepts-frontendrendering:
==================
Frontend rendering
==================
.. _concepts-frontendrendering-templates:
Templates
=========
Fluid templates in the form framework are based on `Bootstrap`_.
.. _Bootstrap: https://getbootstrap.com/
.. _concepts-frontendrendering-templates-customtemplates:
Custom templates
----------------
In order to use your own Fluid templates for frontend forms,
register your own template paths via YAML in the form configuration
(here under the default ``standard`` prototype).
.. code-block:: yaml
prototypes:
standard:
formElementsDefinition:
Form:
renderingOptions:
templateRootPaths:
100: 'EXT:my_site_package/Resources/Private/Frontend/Templates/'
partialRootPaths:
100: 'EXT:my_site_package/Resources/Private/Frontend/Partials/'
layoutRootPaths:
100: 'EXT:my_site_package/Resources/Private/Frontend/Layouts/'
If your `form definition` then references the `standard` prototype, the form
framework will look for Fluid templates in
:directory:`EXT:my_site_package/Resources/Private/Frontend/[*]`.
The `Form` element is the 'main' element. The framework will look for
:file:`Form.html` in :directory:`templateRootPaths`. For all other elements,
it will look in :directory:`partialRootPaths`. A partial has the same name
as the `formElementTypeIdentifier` property, for example,
a `Text` template will be in a partial named :file:`Text.html` in
:directory:`partialRootPaths`.
.. _concepts-frontendrendering-templates-singlevalues:
Form element values in finisher templates
-----------------------------------------
Use the :php:`RenderFormValueViewHelper` to access form element values in your
finisher templates. This ViewHelper accepts a single form
element and renders it. The following example shows the :php:`RenderFormValueViewHelper`
being called with two parameters (`renderable` and `as`) to output the value of a
`message` field. The value :fluid:`{formValue.processedValue}` can then
be manipulated with Fluid, styled, etc.
.. code-block:: html
<formvh:renderFormValue renderable="{form.formDefinition.elements.message}" as="formValue">
{formValue.processedValue}
</formvh:renderFormValue>
Names of your form elements can be found in your form definition (in your
individual YAML files or in the :guilabel:`System > Configuration` module if you
have the lowlevel extension installed). Or use the debug ViewHelper in Fluid to
list all the form elements.
.. code-block:: html
<f:debug>{page.rootForm.elements}</f:debug>
.. _concepts-frontendrendering-translation:
Translation
===========
.. _concepts-frontendrendering-translation-formdefinition:
Translate form definition
-------------------------
Translation of `form definitions` works differently to the usual translation
of the backend. Currently, there is no graphical user interface
for this translation process.
If `form definition` properties were translated in the same way as the rest of the backend,
a backend editor using the `form editor` to edit a form they would see long
unwieldy translation keys. In order to avoid this, form element *properties* are translated
instead of their values. The form framework does not look for translation keys
in a translation file. Instead, the system searches for translations
of the form element properties independent of their property values. The
property values are ignored if an entry is found in a
translation file. The form element property values are overridden by the
translated values.
This approach is a compromise between two scenarios: creating forms using the `form editor`
or creating `form definitions` (which could later be edited in the
`form editor`). An editor can create forms just using the `form editor` where
form element property values are displayed in the default language. An integrator
can provide additional language files which translate the form depending on the
prototype.
Add additional translation files to the form configuration as follows:
.. code-block:: yaml
prototypes:
standard:
formElementsDefinition:
Form:
renderingOptions:
translation:
translationFiles:
# custom translation file
20: 'EXT:my_site_package/Resources/Private/Language/Form/locallang.xlf'
The translationFiles array is processed from the highest key to the lowest, i.e. your
translation file with key `20` is processed before translation files with key
'10' in EXT:form. If no key is found in the translation files, a
property value will be displayed unmodified.
The following properties can be translated:
* label
* defaultValue (scalar values only; array values, e.g. for :yaml:`MultiCheckbox`, are not translated)
* properties.[*]
* properties.options.[*]
* properties.fluidAdditionalAttributes.[*]
* renderingOptions.[*]
The translation keys are put together based on a specific pattern and there is a
order (fallback chain) for the translations that depends on translation scenarios.
These are the translation scenarios:
* translation of a form element property for a specific form (`formDefinitionIdentifier) and form
element (`ElementIdentifier`)
* translation of a form element property for a specific form element (`formElementIdentifier`) and
various forms
* translation of a form element property for an element type (`elementType`) and various
forms, e.g. the `Page` element
The look-up process searches for translation keys in all given translation
files based on the following order (the same order as the translation scenarios above):
* `<formDefinitionIdentifier>.element.<elementIdentifier>.properties.<propertyName>`
* `element.<formElementIdentifier>.properties.<propertyName>`
* `element.<elementType>.properties.<propertyName>`
Translation of options (`properties.options`) in form elements, like the
`Select` element, have the following look-up order:
* `<formDefinitionIdentifier>.element.<elementIdentifier>.properties.options.<propertyValue>`
* `element.<elementIdentifier>.properties.options.<propertyValue>`
.. _concepts-frontendrendering-translation-formdefinition-example:
Example Form Definition
~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
identifier: ApplicationForm
type: Form
prototypeName: standard
label: 'Application form'
renderables:
-
identifier: GeneralInformation
type: Page
label: 'General information'
renderables:
-
identifier: LastName
type: Text
label: 'Last name'
properties:
placeholder: 'Please enter your last name.'
defaultValue: ''
-
identifier: Software
type: MultiSelect
label: 'Known software'
properties:
options:
value1: TYPO3
value2: Neos
In order to translate the form element `LastName`, the process will look for the following
translation keys in the translation files:
* `ApplicationForm.element.LastName.properties.label`
(*<formDefinitionIdentifier>.element.<elementIdentifier>.properties.<propertyName>*)
* `element.LastName.properties.label`
(*element.<formElementIdentifier>.properties.<propertyName>*)
* `element.Text.properties.label`
(*element.<elementType>.properties.<propertyName>*)
If none of these keys exist, 'Last name' will be displayed.
The :yaml:`defaultValue` of `LastName` can be translated with the same fallback chain,
using ``properties.defaultValue`` as the property name:
* `ApplicationForm.element.LastName.properties.defaultValue`
* `element.LastName.properties.defaultValue`
* `element.Text.properties.defaultValue`
In order to translate the form element `Software`, the process will look for the following
translation keys in the translation files:
* `ApplicationForm.element.Software.properties.label`
(*<formDefinitionIdentifier>.element.<elementIdentifier>.properties.<propertyName>*)
* `element.Software.properties.label`
(*element.<formElementIdentifier>.properties.<propertyName>*)
* `element.MultiSelect.properties.label`
(*element.<elementType>.properties.<propertyName>*)
If none of the these keys exist, 'Known software' will be
displayed. The option properties lookup process is as the following:
* `ApplicationForm.element.Software.properties.options.value1`
(*<formDefinitionIdentifier>.element.<elementIdentifier>.properties.options.<propertyValue>*)
* `element.Software.properties.options.value1`
(*element.<elementIdentifier>.properties.options.<propertyValue>*)
* `ApplicationForm.element.Software.properties.options.value2`
(*<formDefinitionIdentifier>.element.<elementIdentifier>.properties.options.<propertyValue>*)
* `element.Software.properties.options.value2`
(*element.<elementIdentifier>.properties.options.<propertyValue>*)
If none of the these keys exist, 'TYPO3' will be displayed as
label for the first option and 'Neos' for the second option.
.. _concepts-frontendrendering-translation-validationerrors:
Translation of validation messages
----------------------------------
The translation of validation messages is similar to the translation of
`form definitions` abpve. The same translation files can be used. If the look-up
process does not find a key within the files, an Extbase message will be displayed.
EXT:form translates validators by default.
The same as for `form definitions`, the translation keys are put together based on a
specific pattern. There is also a fallback chain.
The following translation scenarios are possible:
* translation of validation messages for a specific validator of a specific
form element (`elementIdentifier`) and specific form (`formDefinitionIdentifier`)
* translation of validation messages for a specific validator of various
form elements within a specific form (`formDefinitionIdentifier`)
* translation of validation messages for a specific validator of a specific
form element (`elementIdentifier`) in various forms
* translation of validation messages for a specific validator in various
forms
In Extbase, validation messages are identified by numerical codes (UNIX
timestamps). Different codes can be used for the same validator. Read more about
:ref:`concrete validator configurations <prototypes.prototypeIdentifier.validatorsdefinition.validatoridentifier-concreteconfigurations>`.
The look-up process searches for translation keys in the translation
files in the following order (the same order as the translation scenarios above):
* `<formDefinitionIdentifier>.validation.error.<elementIdentifier>.<validationErrorCode>`
* `<formDefinitionIdentifier>.validation.error.<validationErrorCode>`
* `validation.error.<elementIdentifier>.<validationErrorCode>`
* `validation.error.<validationErrorCode>`
.. _concepts-frontendrendering-translation-validation-example:
Example Form Definition with Validator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
identifier: ContactForm
type: Form
prototypeName: standard
label: 'Contact us'
renderables:
-
identifier: Page1
type: Page
label: 'Page 1'
renderables:
-
identifier: LastName
type: Text
label: 'Last name'
properties:
fluidAdditionalAttributes:
required: required
validators:
-
identifier: NotEmpty
If a user submits this form without providing a last name, the `NotEmpty`
validator (at the bottom of the example above) fails and
sends 1221560910 as a `<validationErrorCode>`. The system looks through the
translation keys in the following order searching for the `NotEmpty` validator for form element `LastName`:
* ContactForm.validation.error.LastName.1221560910 (*<formDefinitionIdentifier>.validation.error.<elementIdentifier>.<validationErrorCode>*)
* ContactForm.validation.error.1221560910 (*<formDefinitionIdentifier>.validation.error.<validationErrorCode>*)
* validation.error.LastName.1221560910 (*validation.error.<elementIdentifier>.<validationErrorCode>*)
* validation.error.1221560910 (validation*.error.<validationErrorCode>*)
As mentioned above, if no translation key is available,
a default Extbase framework message is displayed.
.. _concepts-finishers-translation:
.. _concepts-frontendrendering-translation-finishers:
Translation of finisher options
-------------------------------
The translation of finisher options is similar to the translation of
`form definitions` above. The same translation files can be used. If the look-up
process does not find a key in the provided translation files, the property value
will be displayed unmodified.
The same as for `form definitions`, the translation keys are put together based on a
specific pattern. There is also a fallback chain.
The following translation scenarios are possible:
* translation of finisher options for a specific finisher (`finisherIdentifier`) of a specific form (`formDefinitionIdentifier` below)
* translation of finisher options for a specific finisher (`finisherIdentifier`) of various forms
The look-up process searches for translation keys in all the translation
files based on the following order (the same order as the translation scenarios above):
* `<formDefinitionIdentifier>.finisher.<finisherIdentifier>.<optionName>`
* `finisher.<finisherIdentifier>.<optionName>`
The translation order is as follows:
1. Default value from form definition
2. Overridden value from a FlexForm (if any)
3. Localized value provided by translation files (if any)
The :yaml:`translation.propertiesExcludedFromTranslation` option skips the
third step so that the translation resolves to a FlexForm value if one exists.
For an example see
`Skip translation of overridden form finisher options <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-confirmationfinisher-yaml-propertiesexcludedfromtranslation>`_.
.. _concepts-frontendrendering-translation-finishers-example:
Example Form Definition with Finisher
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
identifier: ContactForm
type: Form
prototypeName: standard
label: 'Contact us'
finishers:
-
identifier: Confirmation
options:
message: 'Thank you for your inquiry.'
renderables:
...
The look-up process searches for the following translation keys for the
'Confirmation' finisher message option:
* `ContactForm.finisher.Confirmation.message` (*<formDefinitionIdentifier>.finisher.<finisherIdentifier>.<optionName>*)
* `finisher.Confirmation.message` (*finisher.<finisherIdentifier>.<optionName>*)
If no translation key exists, the message 'Thank you for your inquiry.' will
be displayed.
.. _concepts-frontendrendering-translation-arguments:
Form element translation arguments
==================================
Form element property translations and finisher option translations can have
placeholders to output translation arguments. Translations can be enriched with
variable values by passing arguments to form element properties. This
feature was introduced in :issue:`81363`.
.. _concepts-frontendrendering-translation-properties:
Form element properties
-----------------------
In the YAML form configuration you can add simple literal values:
.. code-block:: yaml
renderables:
- identifier: field-with-translation-arguments
type: Checkbox
label: This is a %s feature
renderingOptions:
translation:
translationFiles:
10: path/to/locallang.xlf
arguments:
label:
- useful
This will produce the label: `This is a useful feature`.
Alternatively, you can use :typoscript:`formDefinitionOverrides` in TypoScript to set
translation arguments. One use case is a checkbox for
user confirmation which links to further information. Here it makes sense to use
YAML hashes (key value pairs) instead of YAML lists so that sections have keys. This simplifies
references in TypoScript since named keys are easy to read and can easily be reordered. With lists and numeric
keys the TypoScript setup would also need to be updated in this case.
In the following form configuration example the list of :yaml:`renderables` has been replaced with
a hash of :yaml:`renderables`, and the field :yaml:`field-with-translation-arguments`
now has a named key :yaml:`fieldWithTranslationArguments`. This key can be anything
as long as it is unique at its level in the YAML - here just the :yaml:`identifier`
in another form:
.. code-block:: yaml
renderables:
fieldWithTranslationArguments:
identifier: field-with-translation-arguments
type: Checkbox
label: I agree to the <a href="%s">terms and conditions</a>
renderingOptions:
translation:
translationFiles:
10: path/to/locallang.xlf
If the label contains HTML markup - like in the above example - it must
be wrapped in `CDATA` tags in the :directory:`path/to/locallang.xlf` translation file,
to prevent analysis of character data by the parser. Also, the
label should be rendered using the :fluid:`<f:format.raw>`
ViewHelper in fluid templates, to prevent escaping of HTML tags:
.. code-block:: xml
<trans-unit id="<form-id>.element.field-with-translation-arguments.properties.label">
<source><![CDATA[I agree to the <a href="%s">terms and conditions</a>]]></source>
</trans-unit>
The TypoScript below can use the :typoscript:`fieldWithTranslationArguments` key to refer
to the field and adds a page URL as a translation argument for the link in the label:
.. code-block:: typoscript
plugin.tx_form {
settings {
formDefinitionOverrides {
<form-id> {
renderables {
0 {
# Page
renderables {
fieldWithTranslationArguments {
renderingOptions {
translation {
arguments {
label {
0 = TEXT
0.typolink {
# Terms and conditions page, could be
# set also via TypoScript constants
parameter = 42
returnLast = url
}
}
}
}
}
}
}
}
}
}
}
}
}
The :yaml:`Page` element of the form definition is not registered with a named key so a numeric
key :yaml:`0` is used which, as mentioned above, is prone to errors when more pages are added
or reordered.
.. important::
There must be at least one translation file with a translation for the
form element property. Arguments are not inserted into default
values in a form definition.
Finishers
---------
The same mechanism (YAML, YAML + TypoScript) works for finisher options:
.. code-block:: yaml
finishers:
finisherWithTranslationArguments:
identifier: EmailToReceiver
options:
subject: My %s subject
recipients:
your.company@example.com: 'Your Company name'
ceo@example.com: 'CEO'
senderAddress: bar@example.org
translation:
translationFiles:
10: path/to/locallang.xlf
arguments:
subject:
- awesome
This will produce `My awesome subject`.
.. _concepts-frontendrendering-basiccodecomponents:
Basic code components
=====================
.. figure:: ../../Images/basic_code_components.png
:alt: Basic code components
Basic code components
.. _concepts-frontendrendering-basiccodecomponents-formdefinition:
TYPO3\\CMS\\Form\\Domain\\Model\\FormDefinition
-----------------------------------------------
The class :php:`TYPO3\CMS\Form\Domain\Model\FormDefinition` encapsulates
a complete `form definition`, with all of its
* pages,
* form elements,
* validation rules, and
* finishers which are executed when the form is submitted.
The FormDefinition domain model is not modified when the form is executed.
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-anatomy:
The anatomy of a form
~~~~~~~~~~~~~~~~~~~~~
A `FormDefinition` domain model consists of multiple `Page` objects.
When a form is displayed, only one `Page` is visible at a time.
However, you can navigate back and forth between the pages. A
`Page` consists of multiple `FormElements` which represent input
fields, textareas, checkboxes, etc, on a page. The `FormDefinition`
domain model, `Page` and `FormElement` objects have `identifiers`
which must be unique for each `<formElementTypeIdentifier>`,
i.e. the `FormDefinition` domain model and a `FormElement` object may
have the same `identifier` but two `FormElement` objects cannot have the same
identifier.
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-anatomy-example:
Example
"""""""
You can create a :php:`FormDefinition` domain model by calling the API methods
on it, or you can use a :php:`FormFactory` to build the form from a different
representation format such as YAML. The example below calls API methods to
add a page to a :php:`FormDefinition` and then to add an element to the page:
.. code-block:: php
$formDefinition = GeneralUtility::makeInstance(FormDefinition::class, 'myForm');
$page1 = GeneralUtility::makeInstance(Page::class, 'page1');
$formDefinition->addPage($page);
// second argument is the <formElementTypeIdentifier> of the form element
$element1 = GeneralUtility::makeInstance(GenericFormElement::class, 'title', 'Text');
$page1->addElement($element1);
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-createformusingabstracttypes:
Creating a form using abstract form element types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can use the :php:`TYPO3\CMS\Form\Domain\Model\FormDefinition::addPage()`
and :php:`TYPO3\CMS\Form\Domain\Model\FormElements\Page::addElement()` methods as above
and create the `Page` and `FormElement` objects manually, but it is often
better to use the corresponding *create* methods (:php:`TYPO3\CMS\Form\Domain\Model\FormDefinition::createPage()`
and :php:`TYPO3\CMS\Form\Domain\Model\FormElements\Page::createElement()`).
You only need to pass them an abstract `<formElementTypeIdentifier>` such as `Text`
or `Page` and EXT:form will resolve the classname and set default values.
The :ref:`simple example <concepts-frontendrendering-basiccodecomponents-formdefinition-anatomy-example>`
shown above can then be rewritten as follows:
.. code-block:: php
// we will come back to this later on
$prototypeConfiguration = [];
$formDefinition = GeneralUtility::makeInstance(FormDefinition::class, 'myForm', $prototypeConfiguration);
$page1 = $formDefinition->createPage('page1');
$element1 = $page1->addElement('title', 'Text');
You might wonder how the system knows that the element `Text` is
implemented with a `GenericFormElement`. This is configured in the
:php:`$prototypeConfiguration`. To make the example from above actually work,
we need to add some meaningful values to :php:`$prototypeConfiguration`:
.. code-block:: php
$prototypeConfiguration = [
'formElementsDefinition' => [
'Page' => [
'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\Page'
],
'Text' => [
'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement'
],
],
];
For each abstract `<formElementTypeIdentifier>`, we have to add some
configuration. In the snippet above, we only define the `implementation
class name`. Apart from that, it is always possible to set default values
for all configuration options of such elements, as the following example
shows:
.. code-block:: php
$prototypeConfiguration = [
'formElementsDefinition' => [
'Page' => [
'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\Page',
'label' => 'This is the label of the page if nothing else is specified'
],
'Text' => [
'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement',
'label' = >'Default Label',
'defaultValue' => 'Default form element value',
'properties' => [
'placeholder' => 'Text that is shown if element is empty'
],
],
],
];
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-preconfiguredconfiguration:
Using pre-configured $prototypeConfiguration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Often, it does not make sense to manually create the $prototypeConfiguration
array. Bigger parts of this array are pre-configured in the extensions's
YAML settings. The :php:`TYPO3\CMS\Form\Domain\Configuration\ConfigurationService`
contains helper methods which return the ready-to-use :php`$prototypeConfiguration`.
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-rednering:
Rendering a FormDefinition
~~~~~~~~~~~~~~~~~~~~~~~~~~
To trigger the rendering of a :php:`FormDefinition` domain model, the current
:php:`TYPO3\CMS\Extbase\Mvc\Web\Request` needs to be bound to the
`FormDefinition`. This binding results in a :php:`TYPO3\CMS\Form\Domain\Runtime\FormRuntime`
object which contains the `Runtime State` of the form. Among other things,
this object includes the currently inserted values:
.. code-block:: php
// $currentRequest needs to be available.
// Inside a controller, you would use $this->request
$form = $formDefinition->bind($currentRequest);
// now, you can use the $form object to get information about the currently entered values, etc.
.. _concepts-frontendrendering-basiccodecomponents-formruntime:
TYPO3\\CMS\\Form\\Domain\\Runtime\\FormRuntime
----------------------------------------------
This class implements the runtime logic of a form, i.e. the class
* decides which page is currently shown,
* determines the current values of the form
* triggers validation and property mappings.
You generally receive an instance of this class by
calling :php:`TYPO3\CMS\Form\Domain\Model\FormDefinition::bind()`.
.. _concepts-frontendrendering-basiccodecomponents-formruntime-render:
Rendering a form
~~~~~~~~~~~~~~~~
Rendering a form is easy. Just call :php:`render()` on the :php:`FormRuntime`::
.. code-block:: php
$form = $formDefinition->bind($request);
$renderedForm = $form->render();
.. _concepts-frontendrendering-basiccodecomponents-formruntime-accessingformvalues:
Accessing form values
~~~~~~~~~~~~~~~~~~~~~
In order to get the values the user has entered into the form, you can
access the :php:`FormRuntime` object like an array. If a form element with the
identifier `firstName` exists, you can use :php:`$form['firstName']` to
retrieve its current value. You can set values the same way.
.. _concepts-frontendrendering-basiccodecomponents-formruntime-renderinginternals:
Rendering internals
~~~~~~~~~~~~~~~~~~~
The :php:`FormRuntime` inquires the :php:`FormDefinition` domain model regarding
the configured renderer (:php:`TYPO3\CMS\Form\Domain\Model\FormDefinition::getRendererClassName()`)
and then triggers :php:`render()` on this Renderer.
This allows you to declaratively define how a form should be rendered.
.. code-block:: yaml
prototypes:
standard:
formElementsDefinition:
Form:
rendererClassName: 'TYPO3\CMS\Form\Domain\Renderer\FluidFormRenderer'
.. _concepts-frontendrendering-basiccodecomponents-fluidformrenderer:
TYPO3\\CMS\\Form\\Domain\\Renderer\\FluidFormRenderer
-----------------------------------------------------
This class is a :php:`TYPO3\CMS\Form\Domain\Renderer\RendererInterface`
implementation which used to render a :php:`FormDefinition` domain model. It
is the default :t3ext:`form` renderer.
Learn more about
the :ref:`FluidFormRenderer Options<apireference-frontendrendering-fluidformrenderer-options>`.
.. _concepts-frontendrendering-codecomponents-customformelementimplementations:
Custom form element implementations
-----------------------------------
PSR-14 events are available at crucial points in the life cycle of a
`FormElement`. Most of the time, own class implementations are therefore
unnecessary. A custom form element can be defined by:
* writing some configuration, and
* utilizing the standard implementation of :php:`TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement`.
.. code-block:: yaml
prototypes:
standard:
formElementsDefinition:
CustomFormElementIdentifier:
implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement'
With the provided PSR-14 events, this `FormElement` can now be manipulated at runtime.
.. seealso::
* :ref:`PSR-14 events overview for EXT:form <apireference-events>`
tables of all events and registration instructions
* :ref:`Runtime manipulation events <apireference-frontendrendering-runtimemanipulation-events>`
If you insist on your own implementation, the abstract class :php:`TYPO3\CMS\Form\Domain\Model\FormElements\AbstractFormElement`
offers a perfect entry point. In addition, we recommend checking-out :php:`TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable`.
All of your own form element implementations must be programmed to the
interface :php:`TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface`.
It is a good idea to derive your implementation from :php:`TYPO3\CMS\Form\Domain\Model\FormElements\AbstractFormElement`.
.. _concepts-frontendrendering-renderviewHelper:
"render" viewHelper
===================
The `RenderViewHelper` is the actual starting point for form rendering and
not the typical Extbase Controller as you may know it.
For more technical insights read more about the viewHelper's :ref:`arguments<apireference-frontendrendering-renderviewHelper-arguments>`.
.. _concepts-frontendrendering-fluidtemplate:
Render through FLUIDTEMPLATE (without controller)
-------------------------------------------------
.. code-block:: typoscript
tt_content.custom_content_element = COA_INT
tt_content.custom_content_element {
20 = FLUIDTEMPLATE
20 {
file = EXT:my_site_package/Resources/Private/Templates/CustomContentElement.html
settings {
persistenceIdentifier = EXT:my_site_package/Resources/Private/Forms/MyForm.yaml
}
extbase.pluginName = Formframework
extbase.controllerExtensionName = Form
extbase.controllerName = FormFrontend
extbase.controllerActionName = perform
}
}
``my_site_package/Resources/Private/Templates/CustomContentElement.html``:
.. code-block:: html
<formvh:render persistenceIdentifier="{settings.persistenceIdentifier}" />
.. _concepts-frontendrendering-extbase:
Render within your own Extbase extension
----------------------------------------
It is straight forward. Use the `RenderViewHelper` like this and you are
done:
.. code-block:: html
<formvh:render persistenceIdentifier="EXT:my_site_package/Resources/Private/Forms/MyForm.yaml"/>
Point the property `controllerAction` to the desired action name and
provide values for the other parameters displayed below (you might need
those).
.. code-block:: yaml
type: Form
identifier: 'example-form'
label: 'TYPO3 is cool'
prototypeName: standard
renderingOptions:
controllerAction: perform
addQueryString: false
argumentsToBeExcludedFromQueryString: []
additionalParams: []
renderables:
...
.. note::
In general, you can override each and every `form definition` with the help
of TypoScript (see ':ref:`TypoScript overrides<concepts-frontendrendering-runtimemanipulation-typoscriptoverrides>`').
When using the `RenderViewHelper`, there is a second way:
The ':ref:`overrideConfiguration<apireference-frontendrendering-renderviewHelper-overrideconfiguration>`' parameter.
This way, you can override the form definition within your template.
Provide an according array as shown in the example below.
.. code-block:: html
<formvh:render persistenceIdentifier="EXT:my_site_package/Resources/Private/Forms/MyForm.yaml" overrideConfiguration="{renderables: {0: {renderables: {0: {label: 'My shiny new label'}}}}}"/>
.. _concepts-frontendrendering-programmatically:
Build forms programmatically
============================
To learn more about this topic, head to the chapter ':ref:`Build forms programmatically<apireference-frontendrendering-programmatically>`'
which is part of the API reference section.
.. _concepts-frontendrendering-runtimemanipulation:
Runtime manipulation
====================
.. _concepts-frontendrendering-runtimemanipulation-hooks:
:t3ext:`form` implements a decent amount of events that allow the manipulation of
your forms during runtime. In this way, it is possible to, for example,
* ... prefill form elements with values from your database,
* ... skip a whole page based on the value of a certain form element,
* ... mark a form element as mandatory depending of the chosen value of another
form element.
Please check out the :ref:`PSR-14 events overview <apireference-events>`
for more details.
.. _concepts-frontendrendering-runtimemanipulation-typoscriptoverrides:
TypoScript overrides
--------------------
Each and every `form definition` can be overridden via TypoScript if the
:php:`FormFrontendController` of :t3ext:`form` is used to render the form. Normally,
this is the case if the form has been added to the page using the form
plugin or when rendering the form via :ref:`FLUIDTEMPLATE <concepts-frontendrendering-fluidtemplate>`.
The overriding of settings with TypoScript's help takes place after the :ref:`custom finisher settings<concepts-formplugin>`
of the form plugin have been loaded. In this way, you are able to manipulate
the `form definition` for a single page. In doing so, the altered
`form definition` is passed to the :php:`RenderViewHelper` which then
generates the form programmatically. At this point, you can still change the
form elements using the above-mentioned concept of :ref:`hooks<concepts-frontendrendering-runtimemanipulation-hooks>`.
.. code-block:: typoscript
plugin.tx_form {
settings {
formDefinitionOverrides {
<formDefinitionIdentifier> {
renderables {
0 {
renderables {
0 {
label = TEXT
label.value = Overridden label
}
}
}
}
}
}
}
}
+26
View File
@@ -0,0 +1,26 @@
.. include:: /Includes.rst.txt
.. _concepts:
========
Concepts
========
Within this chapter, you will learn the basic concepts of the form framework.
It addresses your concerns as backend editor and integrator. Some of the
chapters also cover topics for developers.
.. toctree::
TargetGroupsAndMainPrinciples/Index
Configuration/Index
FormConfigurationFormDefinition/Index
FormFileStorages/Index
FrontendRendering/Index
Variants/Index
Validators/Index
Finishers/Index
FormManager/Index
FormEditor/Index
FormPlugin/Index
Autocomplete/Index
@@ -0,0 +1,37 @@
.. include:: /Includes.rst.txt
.. _concepts-introduction:
Target groups and main principles
=================================
As :ref:`we saw in the introduction<what-does-it-do>`, the ``form`` extension is a
framework where editors, integrators, and developers can
create and manage forms with different interfaces and functionality.
The most important part of EXT:form is the backend ``form editor``. Different types of users
can use the ``form editor`` for different things. Integrators can manage HTML
class attributes, developers can create
complex ``form definitions`` and editors can edit properties.
The form extension tries to find a compromise between these things. The
``form editor`` is mainly designed for editors, so simple, easy-to-edit properties are
displayed. However, the ``form editor`` can be easily extended by YAML configuration.
And should this is not enough for your specific project, you can
integrate your own JavaScript code using the JavaScript API.
You can create and define forms globally in the :guilabel:`Web->Forms` module or you can load forms
from inside extensions, for example, the ``Mail form`` content element.
Some parts of a form can be overridden in the form plugin. This means you can
reuse the same form on different pages with a different configuration.
The information in this chapter will show you that there are many ways to
customize the form framework, depending on your use case. Be creative and share
your solution with the TYPO3 community!
This chapter describes the basics of the form framework. Check
out the reference and the examples to get a deeper understanding of
the framework.
@@ -0,0 +1,294 @@
.. include:: /Includes.rst.txt
.. _concepts-validators:
Validators
==========
The form framework ships a set of server-side validators (derived from Extbase
validators) which you can use in form elements. Some validators can only
be used for certain elements, e.g. the "Date range validator" can only be used for
"Date" elements. Some form elements
(like "Email") come with validators.
You can define your own validation error messages using the ``validationErrorMessages``
property. These error messages can also be set in the form editor.
.. _concepts-validators-client-side-validation:
Client-side validation
----------------------
In the form framework, HTML 5-based frontend validation can be added to form
elements, but JavaScript validation is not included. The TYPO3 core have no plans to
add this functionality at the current time. However, you can
add it yourself if required. Examples of reliable and well-maintained projects are
`Parsley <https://github.com/guillaumepotier/Parsley.js>`_
and `jQuery Validation <https://github.com/jquery-validation/jquery-validation>`__.
.. _concepts-validators-localization-client-side-validations:
Localization of client side validation
""""""""""""""""""""""""""""""""""""""
Display of validation messages is browser-specific and not generated by TYPO3 so
these messages cannot easily be changed. However, you can use JavaScript to change
validation messages. See `Stack Overflow <http://stackoverflow.com/questions/5272433/html5-form-required-attribute-set-custom-validation-message>`__
for more information.
.. _concepts-validators-server-side-validation:
Server-side validation
----------------------
.. _concepts-validators-alphanumeric:
Alphanumeric validator (:yaml:`Alphanumeric`)
"""""""""""""""""""""""""""""""""""""""""""""
The :ref:`"Alphanumeric validator"<prototypes.prototypeIdentifier.validatorsdefinition.alphanumeric>`
checks for alphanumeric strings. Alphanumeric is defined as a combination of
alphabetic and numeric characters `[A-Z + 0-9]`.
.. _concepts-validators-count:
Number of submitted values validator (:yaml:`Count`)
""""""""""""""""""""""""""""""""""""""""""""""""""""
The :ref:`"Number of submitted values validator"<prototypes.prototypeIdentifier.validatorsdefinition.count>`
checks if a value contains a specific number of elements. The
validator has two options:
- Minimum [:yaml:`options.minimum`]: The minimum count to accept.
- Maximum [:yaml:`options.maximum`]: The maximum count to accept.
.. _concepts-validators-date_range:
Date range validator (:yaml:`DateRange`)
""""""""""""""""""""""""""""""""""""""""
The :ref:`"Date range validator"<prototypes.prototypeIdentifier.validatorsdefinition.daterange>`
checks if a value is a valid DateTime object and within a specified
date range. The range can be defined by providing a minimum and/or maximum date.
The validator has two options:
- Format [:yaml:`options.format`]: The format of the minimum and maximum option.
Default: [:yaml:`Y-m-d`].
- Minimum date [:yaml:`options.minimum`]: The minimum date formatted as `Y-m-d`.
- Maximum date [:yaml:`options.maximum`]: The maximum date formatted as `Y-m-d`.
The options :yaml:`minimum` and :yaml:`maximum` must have the format 'Y-m-d' which
represents the `RFC 3339 <https://www.w3.org/TR/2011/WD-html-markup-20110405/input.date.html>`__
'full-date' format.
The input must be a DateTime object. This input can be tested against a minimum
date and a maximum date. The minimum date and the maximum date are strings. The minimum
and maximum date can be configured through the validator options.
.. _concepts-validators-date_time:
Date/time validator (:yaml:`DateTime`)
"""""""""""""""""""""""""""""""""""""""
The :ref:`"Date/time validator"<prototypes.prototypeIdentifier.validatorsdefinition.datetime>`
checks if a value is a valid DateTime object. The date string is
expected to be formatted according to the `W3C standard <http://www.w3.org/TR/NOTE-datetime.html>`__
which is `YYYY-MM-DDT##:##:##+##:##`, for example `2005-08-15T15:52:01+00:00`.
.. _concepts-validators-email:
Email validator (:yaml:`EmailAddress`)
""""""""""""""""""""""""""""""""""""""
The :ref:`"Email validator"<prototypes.prototypeIdentifier.validatorsdefinition.emailaddress>`
checks if a value is a valid email address. The format of a valid email
address is defined in `RFC 3696 <https://tools.ietf.org/html/rfc3696>`__.
This standard allows international characters and multiple
`@` signs.
.. _concepts-validators-filesize:
File size validator (:yaml:`FileSize`)
""""""""""""""""""""""""""""""""""""""
The :ref:`"File size validator"<prototypes.prototypeIdentifier.validatorsdefinition.filesize>`
validates the size of a file resource. The validator has two options:
- Minimum [:yaml:`options.minimum`]: The minimum file size. Use the
format `<size>B|K|M|G`. For example: `10M` is 10 Megabytes.
- Maximum [:yaml:`options.maximum`]: The maximum file size. Use the
format `<size>B|K|M|G`. For example: `10M` is 10 Megabytes.
Use the format `<size>B|K|M|G` for file size, for example, `10M`
is 10 megabytes. Note: the maximum file size also depends on the :file:`php.ini`
settings of your environment.
.. _concepts-validators-floating_point:
Floating-point number validator (:yaml:`Float`)
"""""""""""""""""""""""""""""""""""""""""""""""
The :ref:`"Floating-point number validator"<prototypes.prototypeIdentifier.validatorsdefinition.float>`
checks if a value is of type float or a string matching the regular
expression `[0-9.e+-]`.
.. _concepts-validators-integer:
Integer number validator (:yaml:`Integer`)
""""""""""""""""""""""""""""""""""""""""""
The :ref:`"Integer number validator"<prototypes.prototypeIdentifier.validatorsdefinition.integer>`
checks if a value is a valid integer.
.. _concepts-validators-empty:
Empty validator (:yaml:`NotEmpty`)
""""""""""""""""""""""""""""""""""
The :ref:`"Empty validator"<prototypes.prototypeIdentifier.validatorsdefinition.notempty>`
checks if a value is not empty (i.e. equal to NULL, empty string, empty array or empty
object).
.. _concepts-validators-number:
Number validator (:yaml:`Number`)
"""""""""""""""""""""""""""""""""
The :ref:`"Number validator"<prototypes.prototypeIdentifier.validatorsdefinition.number>`
checks if a value is a number.
.. _concepts-validators-number_range:
Number range validator (:yaml:`NumberRange`)
""""""""""""""""""""""""""""""""""""""""""""
The :ref:`"Number range validator"<prototypes.prototypeIdentifier.validatorsdefinition.numberrange>`
checks if a value is a number in a specified range. The validator has
two options:
- Minimum [:yaml:`options.minimum`]: The minimum value.
- Maximum [:yaml:`options.maximum`]: The maximum value.
.. _concepts-validators-regular_expressions:
Regular expression validator (:yaml:`RegularExpression`)
""""""""""""""""""""""""""""""""""""""""""""""""""""""""
The :ref:`"Regular expression validator"<prototypes.prototypeIdentifier.validatorsdefinition.regularexpression>`
checks if a value matches a specified regular expression. Delimiters
or modifiers are not supported. The validator has one option:
- Regular expression [:yaml:`options.regularExpression`]: The regular expression
to use for validation, used as given.
As an example, a user submits a domain name and the submitted value should only
contain the second and the top level domain, i.e. "typo3.org" instead of
"https://typo3.org". The regular expression for this would be :code:`/^[-a-z0-9]+\.[a-z]{2,6}$/`.
.. _concepts-validators-string_length:
String length validator (:yaml:`StringLength`)
""""""""""""""""""""""""""""""""""""""""""""""
The :ref:`"String length validator"<prototypes.prototypeIdentifier.validatorsdefinition.stringlength>`
checks if a value is a valid string and its length is within a specified
range. The validator has two options:
- Minimum [:yaml:`options.minimum`]: The minimum length of a valid string.
- Maximum [:yaml:`options.maximum`]: The maximum length of a valid string.
.. _concepts-validators-text:
Non-XML text validator (:yaml:`Text`)
"""""""""""""""""""""""""""""""""""""
The :ref:`"Non-XML text validator"<prototypes.prototypeIdentifier.validatorsdefinition.text>`
checks if a value is a valid piece of text (containing no XML tags). This basically
means that tags are stripped out. In this special case quotes are not encoded
(see `filter_var() <https://php.net/filter_var>`__ for more information.
Be aware that the value of this check entirely depends on the output
context. The validated text is not expected to be secure.
If you want to be sure of that, use a customized regular expression or filter on
output.
.. _concepts-validators-validation-message-translation:
Translation of validation messages
----------------------------------
To learn more about this topic, see :ref:`here<concepts-frontendrendering-translation-validationerrors>`.
.. _concepts-validators-customvalidatorimplementations:
Custom validator implementations
--------------------------------
Validators belong to configuration ``prototypes`` in a ``validatorsDefinition``.
Set the ``implementationClassName`` property of the ``prototype`` to your
own validator classes.
.. code-block:: yaml
prototypes:
standard:
validatorsDefinition:
Custom:
implementationClassName: 'VENDOR\MySitePackage\Domain\Validation\CustomValidator'
Add ``options`` to your validator and provide a default value ``yourCustomOption``:
.. code-block:: yaml
prototypes:
standard:
validatorsDefinition:
Custom:
implementationClassName: 'VENDOR\MySitePackage\Domain\Validation\CustomValidator'
options:
yourCustomOption: 'Jurian'
You can override the default value in your ``form definition``:
.. code-block:: yaml
:emphasize-lines: 13
identifier: sample-form
label: 'Simple Contact Form'
prototype: standard
type: Form
renderables:
-
identifier: subject
label: 'Name'
type: Text
validators:
-
identifier: Custom
options:
yourCustomOption: 'Mathias'
As mentioned above, EXT:form uses Extbase validators. That said,
your own validators should extend :php:`\TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator`.
Read more in "TYPO3 Explained":
:ref:`t3coreapi:extbase_domain_validator`.
+686
View File
@@ -0,0 +1,686 @@
.. include:: /Includes.rst.txt
.. _concepts-variants:
Variants
========
.. _concepts-variants-basics:
Basics
------
A variant is an "alternative" form definition section that allows you to change
properties of form elements, validators, and finishers. Variants are activated
by conditions. This allows you to:
* translate form element values depending on the frontend language
* set and remove validators from one form element depending on the
value of another form element
* hide entire steps (form pages) depending on the value of a form
element
* set finisher options depending on the value of a form element
* hide a form element in particular finishers and on the summary step
Form element variants can be defined statically in
form definitions or created programmatically through an API. The
variants defined in a form definition are applied to
a form based on their conditions at runtime. Programmatically defined variants
can be applied at any time.
Variant conditions can be evaluated programmatically
at any time. However, some conditions are only available at runtime,
for example, checking a form element value.
Custom conditions and operators can be easily added.
Only the form element properties listed in a variant are applied to the
form element, all other properties are retained. An exception to this
rule are finishers and validators. If finishers or validators are
**not** defined in a variant, the original finishers and validators
will be used. If at least one finisher or validator is defined in a
variant, the original finishers and validators are overwritten
by the finishers and validators in the variant.
Variants defined in a form definition are **all** processed and
applied in the order of their matching conditions. This means if
variant 1 sets the label of a form element to "X" and variant 2 sets
the label to "Y", then variant 2 is applied, i.e. the label will be "Y".
.. note::
Currently it is **not** possible to define variants in
the backend form editor.
.. _concepts-variants-enabled-property:
Rendering option ``enabled``
----------------------------
The rendering option :yaml:`enabled` is available for all finishers and
form elements except the root form element and the first form
page. The option accepts a boolean value (:yaml:`true` or :yaml:`false`).
Setting a form element to :yaml:`enabled: true` renders it in the
frontend and enables processing of its values, including property mapping
and validation. Setting :yaml:`enabled: false` disables it in the frontend. All
form elements and finishers except the root form element and the first form page can be enabled
or disabled.
Setting a finisher to :yaml:`enabled: true` executes it when
the form is submitted. Setting :yaml:`enabled: false` skips the finisher.
By default, :yaml:`enabled` is set to :yaml:`true`.
See :ref:`examples<concepts-variants-examples-hide-form-elements>`
below to learn more.
.. _concepts-variants-definition:
Definition of variants
----------------------
Variants are defined at the form element level in YAML. Here is an example of a text
form element variant:
.. code-block:: yaml
type: Text
identifier: text-1
label: Foo
variants:
-
identifier: variant-1
condition: 'traverse(formValues, "checkbox-1") == 1'
# If the condition matches, the label property of the form
# element is set to the value 'Bar'
label: Bar
The :yaml:`identifier` must be unique at the form element level.
Each variant has a single :yaml:`condition` which applies the variant if the
condition is satisfied. The
properties in the variant are applied to the form element. In the
example above the label of :yaml:`text-1` is
changed to ``Bar`` if the checkbox :yaml:`checkbox-1` is checked.
The following properties can be overwritten by :yaml:`Form` (the topmost element)
variants:
* :yaml:`label`
* :yaml:`renderingOptions`
* :yaml:`finishers`
* :yaml:`rendererClassName`
The following properties can be overwritten by all other form element variants:
* :yaml:`enabled`
* :yaml:`label`
* :yaml:`defaultValue`
* :yaml:`properties`
* :yaml:`renderingOptions`
* :yaml:`validators`
.. note::
Unset individual list items in select option variants by marking the values with
:code:`__UNSET`. See :ref:`example <concepts-variants-examples-remove-options>` below.
.. _concepts-variants-conditions:
Conditions
----------
The form framework uses the Symfony component `expression language <https://symfony.com/doc/4.1/components/expression_language.html>`_
for conditions. An expression is a one-liner that returns a boolean value, for example,
:yaml:`applicationContext matches "#Production/Local#"`. For further information see
the `Symfony docs <https://symfony.com/doc/4.1/components/expression_language/syntax.html>`_.
The form framework extends the expression language with variables to access
form values and environment settings.
.. _concepts-variants-conditions-formruntime:
``formRuntime`` (object)
^^^^^^^^^^^^^^^^^^^^^^^^
You can access every public method of :php:`\TYPO3\CMS\Form\Domain\Runtime\FormRuntime`.
Learn more :ref:`here<apireference-frontendrendering-programmatically-apimethods-formruntime>`.
For example:
:yaml:`formRuntime.getIdentifier() == "test"`.
.. _concepts-variants-conditions-renderable:
``renderable`` (VariableRenderableInterface)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:yaml:`renderable` contains the instance of renderable that the condition
is applied to. This can be used e.g. to access the identifier of the
current renderable without having to duplicate it.
For example:
:yaml:`traverse(formValues, renderable.getIdentifier()) == "special value"`.
.. _concepts-variants-conditions-formvalues:
``formValues`` (array)
^^^^^^^^^^^^^^^^^^^^^^
:yaml:`formValues` holds all the submitted form element values. Each
key in the array represents a form element identifier.
For example:
:yaml:`traverse(formValues, "text-1") == "yes"`.
.. _concepts-variants-conditions-stepidentifier:
``stepIdentifier`` (string)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
:yaml:`stepIdentifier` is set to the :yaml:`identifier` of the current
step.
For example:
:yaml:`stepIdentifier == "page-1"`.
.. _concepts-variants-conditions-steptype:
``stepType`` (string)
^^^^^^^^^^^^^^^^^^^^^
:yaml:`stepType` is set to the :yaml:`type` of the current step.
For example:
:yaml:`stepType == "SummaryPage"`.
.. _concepts-variants-conditions-finisheridentifer:
``finisherIdentifier`` (string)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:yaml:`finisherIdentifier` is set to the :yaml:`identifier` of the
current finisher or an empty string (if no finishers are executed).
For example:
:yaml:`finisherIdentifier == "EmailToSender"`.
.. _concepts-variants-conditions-site:
``site`` (object)
^^^^^^^^^^^^^^^^^
You can access every public method in :php:`\TYPO3\CMS\Core\Site\Entity\Site`.
The following are the most important ones:
* getSettings() / The site settings array
* getDefaultLanguage() / The default language object for the current site
* getConfiguration() / The whole configuration of the current site
* getIdentifier() / The identifier of the current site
* getBase() / The base URL of the current site
* getRootPageId() / The ID of the root page of the current site
* getLanguages() / An array of available languages for the current site
* getSets() / Configured site sets of a site (new in TYPO3 v13+)
For example:
:yaml:`site("settings").get("myVariable") == "something"`.
:yaml:`site("rootPageId") == "42"`.
More details on the `Site` object can be found in
:ref:`Using site configuration in conditions <t3coreapi:sitehandling-inConditions>`.
.. _concepts-variants-conditions-sitelanguage:
``siteLanguage`` (object)
^^^^^^^^^^^^^^^^^^^^^^^^^
You can access every public method in :php:`\TYPO3\CMS\Core\Site\Entity\SiteLanguage`.
The most important ones are:
* getLanguageId() / The sys_language_uid.
* getLocale() / The language locale, for example 'en_US.UTF-8'.
* getTypo3Language() / The language key for XLF files, for example, 'de' or 'default'.
* getTwoLetterIsoCode() / Returns the ISO-639-1 language ISO code, for example, 'de'.
For example:
:yaml:`siteLanguage("locale").getName() == "de-DE"`.
.. _concepts-variants-conditions-applicationcontext:
``applicationContext`` (string)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:yaml:`applicationContext` is set to the application context
(@see GeneralUtility::getApplicationContext()).
For example:
:yaml:`applicationContext matches "#Production/Local#"`.
.. _concepts-variants-conditions-contentobject:
``contentObject`` (array)
^^^^^^^^^^^^^^^^^^^^^^^^^
:yaml:`contentObject` contains the data of the current content object
or an empty array if no content object is available.
For example:
:yaml:`contentObject["pid"] in [23, 42]`.
.. _concepts-variants-programmatically:
Working with variants programmatically
--------------------------------------
Create a variant with conditions through the PHP API::
/** @var TYPO3\CMS\Form\Domain\Model\Renderable\RenderableVariantInterface $variant */
$variant = $formElement->createVariant([
'identifier' => 'variant-1',
'condition' => 'traverse(formValues, "checkbox-1") == 1',
'label' => 'foo',
]);
Get all the variants of a form element::
/** @var TYPO3\CMS\Form\Domain\Model\Renderable\RenderableVariantInterface[] $variants */
$variants = $formElement->getVariants();
Apply a variant to a form element regardless of its conditions::
$formElement->applyVariant($variant);
.. _concepts-variants-examples:
Examples
--------
Here are some more complex examples to show you what is possible with the
form framework.
.. _concepts-variants-examples-translation:
Translation of form elements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In this example, form, page and text elements have variants so that they are translated differently depending on
the frontend language (whether it is German or English).
.. code-block:: yaml
:emphasize-lines: 9,10,24,25,40,41
type: Form
prototypeName: standard
identifier: contact-form
label: Kontaktformular
renderingOptions:
submitButtonLabel: Senden
variants:
-
identifier: language-variant-1
condition: 'siteLanguage("locale").getName() == "en-US"'
label: Contact form
renderingOptions:
submitButtonLabel: Submit
renderables:
-
type: Page
identifier: page-1
label: Kontaktdaten
renderingOptions:
previousButtonLabel: zurück
nextButtonLabel: weiter
variants:
-
identifier: language-variant-1
condition: 'siteLanguage("locale").getName() == "en-US"'
label: Contact data
renderingOptions:
previousButtonLabel: Previous step
nextButtonLabel: Next step
renderables:
-
type: Text
identifier: text-1
label: Vollständiger Name
properties:
fluidAdditionalAttributes:
placeholder: Ihre vollständiger Name
variants:
-
identifier: language-variant-1
condition: 'siteLanguage("locale").getName() == "en-US"'
label: Full name
properties:
fluidAdditionalAttributes:
placeholder: Your full name
.. _concepts-variants-examples-validation:
Adding validators dynamically
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In this example, the :yaml:`email-address` field has a variant that adds validators
if :yaml:`checkbox-1` is checked.
.. code-block:: yaml
:emphasize-lines: 18,19
type: Form
prototypeName: standard
identifier: newsletter-subscription
label: Newsletter Subscription
renderables:
-
type: Page
identifier: page-1
label: General data
renderables:
-
type: Text
identifier: email-address
label: Email address
defaultValue:
variants:
-
identifier: validation-1
condition: 'traverse(formValues, "checkbox-1") == 1'
properties:
fluidAdditionalAttributes:
required: required
validators:
-
identifier: NotEmpty
-
identifier: EmailAddress
-
type: Checkbox
identifier: checkbox-1
label: Check this and email will be mandatory
.. _concepts-variants-examples-hide-form-elements:
Hide form elements
^^^^^^^^^^^^^^^^^^
In this example, the form element :yaml:`email-address` has
been enabled explicitly but this can be left out as this is
the default state. The form element :yaml:`text-3` has been disabled
to (temporarily) remove it from the form. The
field :yaml:`text-1` has a variant that hides it in all finishers and on the summary step.
The :yaml:`EmailToSender` finisher contains form values (:yaml:`email-address`
and :yaml:`name`). The :yaml:`EmailToSender` finisher is only enabled if
:yaml:`checkbox-1` has been checked by the user, otherwise it is skipped.
.. code-block:: yaml
:emphasize-lines: 15,19,23,32,36,39,42,51
type: Form
prototypeName: standard
identifier: hidden-field-form
label: Hidden field form
finishers:
-
identifier: EmailToReceiver
options:
subject: Yes, I am ready
recipients:
your.company@example.com: 'Your Company name'
senderAddress: tritum@example.org
senderName: tritum@example.org
-
identifier: EmailToSender
options:
subject: This is a copy of the form data
recipients:
{email-address}: '{name}'
senderAddress: tritum@example.org
senderName: tritum@example.org
renderingOptions:
enabled: '{checkbox-1}'
renderables:
-
type: Page
identifier: page-1
label: General data
renderables:
-
type: Text
identifier: text-1
label: A field hidden on confirmation step and in all mails (finishers)
variants:
-
identifier: hide-1
renderingOptions:
enabled: false
condition: 'stepType == "SummaryPage" || finisherIdentifier in ["EmailToSender", "EmailToReceiver"]'
-
type: Text
identifier: email-address
label: Email address
properties:
fluidAdditionalAttributes:
required: required
renderingOptions:
enabled: true
-
type: Text
identifier: text-3
label: A temporarily disabled field
renderingOptions:
enabled: false
-
type: Checkbox
identifier: checkbox-1
label: Check this and the sender gets an email
-
type: SummaryPage
identifier: summarypage-1
label: Confirmation
.. _concepts-variants-examples-hide-steps:
Hide steps
^^^^^^^^^^
In this example, the second step (:yaml:`page-2`) has a variant that disables it
if :yaml:`checkbox-1` is checked. :yaml:`checkbox-1` has a variant which
disables it on the summary step.
.. code-block:: yaml
:emphasize-lines: 17, 21,22,24,27,31,32,34
type: Form
prototypeName: standard
identifier: multi-step-form
label: Muli step form
renderables:
-
type: Page
identifier: page-1
label: First step
renderables:
-
type: Text
identifier: text-1
label: A field
-
type: Checkbox
identifier: checkbox-1
label: Check this and the next step will be skipped
variants:
-
identifier: variant-1
condition: 'stepType == "SummaryPage"'
renderingOptions:
enabled: false
-
type: Page
identifier: page-2
label: Second step
variants:
-
identifier: variant-2
condition: 'traverse(formValues, "checkbox-1") == 1'
renderingOptions:
enabled: false
renderables:
-
type: Text
identifier: text-2
label: Another field
-
type: SummaryPage
identifier: summarypage-1
label: Confirmation
.. _concepts-variants-examples-finisher:
Set finisher values dynamically
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In this example, the form has a variant so that the finisher has different values
depending on the application context.
.. code-block:: yaml
:emphasize-lines: 9,12,13,18
type: Form
prototypeName: standard
identifier: finisher-condition-example
label: Finishers under condition
finishers:
-
identifier: Confirmation
options:
message: I am NOT a local environment.
variants:
-
identifier: variant-1
condition: 'applicationContext matches "#Production/Local#"'
finishers:
-
identifier: Confirmation
options:
message: I am a local environment.
renderables:
-
type: Page
identifier: page-1
label: General data
renderables:
-
type: Text
identifier: text-1
label: A field
.. _concepts-variants-examples-remove-options:
Remove select options
^^^^^^^^^^^^^^^^^^^^^
In this example, a select form element has a variant which removes an option for
a specific locale.
.. code-block:: yaml
:emphasize-lines: 13,24,25,28
type: Form
prototypeName: standard
identifier: option-remove-example
label: Options removed under condition
renderables:
-
type: Page
identifier: page-1
label: Step
renderables:
-
identifier: salutation
type: SingleSelect
label: Salutation
properties:
options:
'': '---'
mr: Mr.
mrs: Mrs.
miss: Miss
defaultValue: ''
variants:
-
identifier: salutation-variant
condition: 'siteLanguage("locale").getName() == "zh-CN"'
properties:
options:
miss: __UNSET
.. _concepts-variants-custom-language-providers:
Adding your own expression language providers
---------------------------------------------
You can extend the expression language with your own custom functions. For more
information see the official `docs <https://symfony.com/doc/5.4/components/expression_language/extending.html#using-expression-providers>`__
and the appropriate :ref:`TYPO3 implementation details<t3coreapi:symfony-expression-language>`.
Register your own expression language provider class in
:file:`Configuration/ExpressionLanguage.php` and create it, making sure it
implements :php:`Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface`.
.. code-block:: php
:caption: EXT:some_extension/Configuration/ExpressionLanguage.php
return [
'form' => [
Vendor\MyExtension\ExpressionLanguage\CustomExpressionLanguageProvider::class,
],
];
.. _concepts-variants-custom-language-variables:
Adding your own expression language variables
---------------------------------------------
You can extend the expression language with your own variables. These
variables can be used in conditions.
Register your own expression language provider class in
:file:`Configuration/ExpressionLanguage.php` as above and
and create it as follows:
.. code-block:: php
:caption: EXT:some_extension/Classes/ExpressionLanguage/CustomExpressionLanguageProvider.php
class CustomExpressionLanguageProvider extends AbstractProvider
{
public function __construct()
{
$this->expressionLanguageVariables = [
'variableA' => 'valueB',
];
}
}