first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

@@ -0,0 +1,450 @@
.. include:: ../../Includes.txt
.. _concepts-configuration:
Configuration
=============
.. _concepts-configuration-whysomuchconfiguration:
A lot of configuration. Why?
----------------------------
The requirements for building forms in a declarative and programmatic way
are complex. What we have learned so far is that the program code must be
kept as generic as possible to handle the dynamics of forms, but a generic
program code means a lot of configurative overhead.
Initially, the configuration may overwhelm you, but it also has some great
advantages. Many aspects of EXT:form can be manipulated in a purely
configurative manner without involving a developer.
Furthermore, we wanted to avoid the configuration being done at places
whose context actually suggests something different. This pedantry,
however, leads to the situation in which certain settings have to be
defined multiple times at multiple places. This may seem nonsensical, but
it avoids unpredictable behaviour. Within the form framework, nothing
happens magically. It is all about configuration.
.. _concepts-configuration-whyyaml:
Why YAML?
---------
Former versions of EXT:form used a subset of TypoScript to describe the
definition of a specific form and the behaviour of the included form
elements. This led to a lot of confusion from integrators because the
implemented definition language looked like TypoScript but did not behave
like TypoScript.
Since the definition of forms and form elements must be declarative, the
EXT:form team decided to use YAML. Just through the visual appearance of
YAML, it should be clear to everyone that neither magic nor TypoScript
stdWrap functionality are possible.
.. _concepts-configuration-yamlregistration:
YAML registration
-----------------
At the moment, configuration via YAML is not natively integrated into the
core of TYPO3. You have to make a short detour by using TypoScript in order
to register your YAML configuration. Furthermore, there is a "speciality"
regarding the integration of your YAML configuration for the backend
module.
.. hint::
We recommend using a `site package <https://de.slideshare.net/benjaminkott/typo3-the-anatomy-of-sitepackages>`_.
This will make your life easier if you want to customize EXT:form
heavily in order to suit the customer's needs.
.. _concepts-configuration-yamlregistration-frontend:
YAML registration for the frontend
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
EXT:form registers two YAML configuration files which are required for the
frontend.
.. code-block:: typoscript
plugin.tx_form {
settings {
yamlConfigurations {
10 = EXT:form/Configuration/Yaml/BaseSetup.yaml
20 = EXT:form/Configuration/Yaml/FormEngineSetup.yaml
}
}
}
Since the keys 10 and 20 are already taken, we recommend registering your
own configuration beginning with the key ``100``.
.. code-block:: typoscript
plugin.tx_form {
settings {
yamlConfigurations {
100 = EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml
}
}
}
.. _concepts-configuration-yamlregistration-backend:
YAML registration for the backend
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
EXT:form registers three YAML configuration files which are required for
the backend.
.. code-block:: typoscript
module.tx_form {
settings {
yamlConfigurations {
10 = EXT:form/Configuration/Yaml/BaseSetup.yaml
20 = EXT:form/Configuration/Yaml/FormEditorSetup.yaml
30 = EXT:form/Configuration/Yaml/FormEngineSetup.yaml
}
}
}
Since the keys 10, 20, and 30 are already taken, we recommend registering
your own configuration beginning with the key ``100``.
.. code-block:: typoscript
module.tx_form {
settings {
yamlConfigurations {
100 = EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml
}
}
}
.. important::
Consider the following methods to register TypoScript for the backend.
The backend module of EXT:form is based on Extbase. Such backend modules
can, like frontend plugins, be configured via TypoScript. The frontend
plugins are configured below ``plugin.tx_[pluginkey]``. For the
configuration of the backend ``module.tx_[pluginkey]`` is used.
There are different ways to include the TypoScript configuration for the
backend:
- a) use the API function ``\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptSetup()``,
- b) add the configuration to your existing TypoScript template.
In both cases, the ``form editor`` will work as expected regardless the
chosen page from the page tree. If using the aforementioned method b, the
configuration would only be valid on a specific page tree, unless you add
your configuration to all trees within your installation. Nevertheless,
being on the root page (uid 0) would still be a problem.
To sum it up: choose either method a or b, and you will be fine.
.. _concepts-configuration-yamlregistration-backend-addtyposcriptsetup:
YAML registration for the backend via addTypoScriptSetup()
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Add the following PHP code to your :file:`ext_localconf.php` of your site
package::
defined('TYPO3_MODE') or die();
call_user_func(function () {
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptSetup(
trim('
module.tx_form {
settings {
yamlConfigurations {
100 = EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml
}
}
}
')
);
});
.. _concepts-configuration-configurationaspects:
Configuration aspects
---------------------
In EXT:form, four aspects can be configured:
- the behaviour of the frontend rendering,
- the behaviour of the ``form editor``,
- the behaviour of the ``form manager``, and
- the behaviour of the ``form plugin``.
Those aspects are defined in separate files which are only loaded in the
frontend/ backend when needed. This approach has two advantages:
- increased clarity,
- increased performance, e.g. the ``form editor`` configuration is not
needed in the frontend and therefore not loaded.
It is up to you if you want to follow this guideline or if you want to put
the whole configuration into one large file.
There are some configurational aspects which cannot explicitly be assigned
to either the frontend or the backend. Instead, the configuration is
valid for both areas. For example, within the backend, the whole frontend
configuration is required in order to allow the form preview to work
properly. In addition, as soon as the form is rendered via the ``form
plugin``, the ``FormEngine`` configuration is needed to interpret the
overridden finisher configuration correctly.
.. _concepts-configuration-inheritances:
Inheritances
------------
The final YAML configuration is not based on one huge file. Instead, it is
a compilation of a sequential process:
- First of all, all registered configuration files are parsed as YAML and
are overlaid according to their order.
- After that, the ``__inheritances`` operator is applied. It is a unique
operator introduced by the form framework.
- Finally, all configuration entries with a value of ``null`` are deleted.
Additionally, the frontend configuration can be extended/ overridden by
TypoScript:
.. code-block:: typoscript
plugin.tx_form {
settings {
yamlSettingsOverrides {
...
}
}
}
.. note::
Your TypoScript overrides are not interpreted by the ``form editor``,
i.e. those settings are ignored.
.. note::
The described process is quite handy for you. As soon as you are working
with your :ref:`own configuration files <concepts-configuration-yamlregistration>`,
you only have to define the differences compared to the previously
loaded configuration files.
For example, if you want to override the fluid templates and you therefore
register an additional configuration file via
.. code-block:: typoscript
plugin.tx_form {
settings {
yamlConfigurations {
# register your own additional configuration
# choose a number higher than 30 (below is reserved)
100 = EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml
}
}
}
... you only have to define the following YAML setup in ``EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml``:
.. code-block:: yaml
TYPO3:
CMS:
Form:
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 of your own configuration file will overrule the corresponding
values of the basic configuration file (:file:`EXT:form/Configuration/Yaml/BaseSetup.yaml`).
.. _concepts-configuration-inheritances-operator:
__inheritances operator
^^^^^^^^^^^^^^^^^^^^^^^
The ``__inheritances`` operator is an extremely useful instrument. Using it
helps to significantly reduce the configuration effort. It behaves similar
to the ``<`` operator in TypoScript. That is, the definition of the source
object is copied to the target object. The configuration can be inherited
from several parent objects and can be overridden afterwards. Two simple
examples will show you the usage and behaviour of the ``__inheritances``
operator.
.. code-block:: yaml
Form:
part01:
key01: value
key02:
key03: value
part02:
__inheritances:
10: Form.part01
The configuration above results in:
.. code-block:: yaml
Form:
part01:
key01: value
key02:
key03: value
part02:
key01: value
key02:
key03: value
As you can see, ``part02`` inherited all of ``part01``'s properties.
.. code-block:: yaml
Form:
part01:
key: value
part02:
__inheritances:
10: Form.part01
key: 'value override'
The configuration above results in:
.. code-block:: yaml
Form:
part01:
key: value
part02:
key: 'value override'
EXT:form heavily uses the ``__inheritances`` operator, in particular, for
the definition of form elements. The following example shows you how to use
the operator to define a new form element which behaves like the parent
element but also has its own properties.
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
formElementsDefinition:
GenderSelect:
__inheritances:
10: 'TYPO3.CMS.Form.prototypes.standard.formElementsDefinition.RadioButton'
renderingOptions:
templateName: 'RadioButton'
properties:
options:
f: 'Female'
m: 'Male'
u: 'Unicorn'
a: 'Alien'
The YAML configuration defines a new form element called ``GenderSelect``.
This element inherits its definition from the ``RadioButton`` element but
additionally ships four predefined options. Without any problems, the new
element can be used and overridden within the ``form definition``.
.. hint::
Currently, there is no built-in solution within the TYPO3 core to
preview the resulting/ final EXT:form YAML configuration. If you want
to check the configuration, there is a fishy way which you should never
implement on a production system.
Open the file ``typo3/sysext/form/Classes/Mvc/Configuration/ConfigurationManager.php::getConfigurationFromYamlFile()``
and add the following code before the ``return`` statement::
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($yamlSettings, 'form configuration', 9999);
Now open the ``Forms`` module in the backend or navigate to a page in
the frontend which contains a form. The DebuggerUtility will print the
final configuration directly to the screen.
It will probably take some time to fully understand the awesomeness of
this operator. If you are eager to learn more about this great instrument,
check out the unit tests defined in ``EXT:form/Tests/Unit/Mvc/Configuration/InheritancesResolverServiceTest.php``.
.. _concepts-configuration-prototypes:
Prototypes
----------
Most of the configurational aspects of the form framework are defined
in so-called ``prototypes``. By default, EXT:form defines a prototype
named ``standard``. The definition of form elements - including their
rendering in the frontend, ``form editor`` and ``form plugin`` - reside
within those prototypes. As soon as you create a new form, the specific
form definition references such a prototype.
This allows you to do a lot of nifty stuff. Let your imagination run free.
For example:
- based on the referenced prototype, the same form can load
- ...varying templates
- ...varying ``form editor`` configurations
- ...varying ``form plugin`` finisher overrides
- within the ``form manager``, depending on the selected prototype
- ...varying ``form editor`` configurations can be loaded
- ...varying pre-configured form templates (boilerplates) can be chosen
- different prototypes can define different/ extended form elements and
display them in the frontend/ ``form editor`` accordingly
Check out the following use case to fully understand the concept behind
prototypes. Imagine that there are two defined prototypes: "noob" and
"poweruser".
.. t3-field-list-table::
:header-rows: 1
- :a:
:b: Prototype "noob"
:c: Prototype "poweruser"
- :a: **Available form elements within the ``form editor``**
:b: Text, Textarea
:c: No changes. Default behaviour.
- :a: **Available finisher within the ``form editor``**
:b: Only the email finisher is available. It offers a field for setting
the subject of the mail. All remaining fields are hidden and filled
with default values.
:c: No changes. Default behaviour.
- :a: **Finisher overrides within the ``form plugin``**
:b: It is not possible to override the finisher configuration.
:c: No changes. Default behaviour.
@@ -0,0 +1,527 @@
.. include:: ../../Includes.txt
.. _concepts-finishers:
Finishers
=========
The form framework ships a bunch of finishers, which will be briefly
described here. For more details, please head to the API reference and check
out the section regarding :ref:`Finisher Options<apireference-finisheroptions>`.
.. _concepts-finishers-closurefinisher:
Closure finisher
----------------
The 'Closure finisher' can only be used within forms that are created
programmatically. It allows you to execute your own finisher code without
implementing/ declaring a finisher.
.. _concepts-finishers-confirmationfinisher:
Confirmation finisher
---------------------
The 'Confirmation finisher' is a simple finisher that outputs a given
text after the form has been submitted.
.. _concepts-finishers-deleteuploadsfinisher:
DeleteUploads finisher
----------------------
The 'DeleteUploads finisher' removes submitted files. Use this finisher,
for example, after the email finisher if you do not want to keep the files
within your TYPO3 installation.
.. note::
Finishers are only executed on successfully submitted forms. If a user uploads a file but
does not finish the form successfully, the uploaded files will not be deleted.
.. _concepts-finishers-emailfinisher:
Email finisher
--------------
The 'Email finisher' sends an email to one recipient. EXT:form uses two
``EmailFinisher`` declarations with the identifiers ``EmailToReceiver`` and
``EmailToSender``.
.. _concepts-finishers-flashmessagefinisher:
FlashMessage finisher
---------------------
The 'FlashMessage finisher' is a simple finisher that adds a message to the
FlashMessageContainer.
.. _concepts-finishers-redirectfinisher:
Redirect finisher
-----------------
The 'Redirect finisher' is a simple finisher that redirects to another page.
Additional link parameters can be added to the URL.
.. note::
This finisher stops the execution of all subsequent finishers in order to perform a redirect.
Therefore, this finisher should always be the last finisher to be executed.
.. _concepts-finishers-savetodatabasefinisher:
SaveToDatabase finisher
-----------------------
The 'SaveToDatabase finisher' saves the data of a submitted form into a
database table.
Here is an example for adding uploads to ext:news (fal_related_files and fal_media).
.. code-block:: yaml
-
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:
table_local:
value: sys_file
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:
table_local:
value: sys_file
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
.. _concepts-finishers-translation:
Translation of finisher options
-------------------------------
To learn more about this topic, please continue :ref:`here<concepts-frontendrendering-translation-finishers>`.
.. _concepts-finishers-customfinisherimplementations:
Write a custom finisher
-----------------------
If you want to make the finisher configurable in the backend UI read :ref:`here<concepts-finishers-customfinisherimplementations-extend-gui>`.
Finishers are defined as part of a ``prototype`` within a
``finishersDefinition``. The property ``implementationClassName`` is to be
utilized to load the finisher implementation.
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
finishersDefinition:
CustomFinisher:
implementationClassName: 'VENDOR\MySitePackage\Domain\Finishers\CustomFinisher'
If the finisher requires options, you can define those within the
``options`` property. The options will be used as default values and can
be overridden using the ``form definition``.
Define the default value:
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
finishersDefinition:
CustomFinisher:
implementationClassName: 'VENDOR\MySitePackage\Domain\Finishers\CustomFinisher'
options:
yourCustomOption: 'Ralf'
Override the option using the ``form definition``:
.. code-block:: yaml
identifier: sample-form
label: 'Simple Contact Form'
prototype: standard
type: Form
finishers:
-
identifier: CustomFinisher
options:
yourCustomOption: 'Björn'
renderables:
...
Each finisher has to be programmed to the interface ``TYPO3\CMS\Form\Domain\Finishers\FinisherInterface``
and should extend the class ``TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher``.
In doing so, the logic of the finisher should start with the method
``executeInternal()``.
.. _concepts-finishers-customfinisherimplementations-accessingoptions:
Accessing finisher options
""""""""""""""""""""""""""
If your finisher extends ``TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher``,
you can access your finisher options with the help of the ``parseOption()``
method::
$yourCustomOption = $this->parseOption('yourCustomOption');
``parseOption()`` is looking for 'yourCustomOption' in your
``form definition``. If it cannot be found, the method checks
1. the ``prototype`` configuration for a default value,
2. the finisher class itself by searching for a default value within the
``$defaultOptions`` property::
declare(strict_types = 1);
namespace VENDOR\MySitePackage\Domain\Finishers;
class CustomFinisher extends \TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher
{
protected $defaultOptions = [
'yourCustomOption' => 'Olli',
];
// ...
}
If the option cannot be found by processing this fallback chain, ``null`` is
returned.
If the option is found, 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 very end, it tries to :ref:`translate the finisher options<concepts-frontendrendering-translation-finishers>`.
.. _concepts-finishers-customfinisherimplementations-accessingoptions-formruntimeaccessor:
Accessing form runtime values
'''''''''''''''''''''''''''''
By utilizing a specific notation, finisher options can be populated with
submitted form values (assuming you are using the ``parseOption()`` method).
You can access values of the ``FormRuntime`` and thus values of each single
form element by encapsulating the option values with ``{}``. If there is a
form element with the ``identifier`` 'subject', you can access its value
within the finisher configuration. Check out the following example to
get the whole idea.
.. code-block:: yaml
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
::
// $yourCustomOption contains the value of the form element with the
// identifier 'subject'
$yourCustomOption = $this->parseOption('yourCustomOption');
In addition, you can use ``{__currentTimestamp}`` as a special option value.
It will return the current UNIX timestamp.
.. _concepts-finishers-customfinisherimplementations-finishercontext:
Finisher Context
""""""""""""""""
The class ``TYPO3\CMS\Form\Domain\Finishers\FinisherContext`` takes care of
transferring a finisher context to each finisher. Given the finisher is
derived from ``TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher`` the
finisher context will be available via::
$this->finisherContext
The method ``cancel`` prevents the execution of successive finishers::
$this->finisherContext->cancel();
The method ``getFormValues`` returns all of the submitted form values.
``getFormValues``::
$this->finisherContext->getFormValues();
The method ``getFormRuntime`` returns the ``FormRuntime``::
$this->finisherContext->getFormRuntime();
.. _concepts-finishers-customfinisherimplementations-finishercontext-sharedatabetweenfinishers:
Share data between finishers
''''''''''''''''''''''''''''
The method ``getFinisherVariableProvider`` returns an object (``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 within your configuration::
$this->finisherContext->getFinisherVariableProvider();
The data is stored within the ``FinisherVariableProvider`` and is addressed
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 potential 'Finisher' appendix. If your finisher is derived from
the class ``TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher``, the name of
this construct is stored in the following variable::
$this->shortFinisherIdentifier
For example, if the name of your finisher class is 'CustomFinisher', the
mentioned variable will contain the value 'Custom'.
There are a bunch of methods to access and manage the finisher data:
- Add data::
$this->finisherContext->getFinisherVariableProvider()->add(
$this->shortFinisherIdentifier,
'unique.value.identifier',
$value
);
- Get data::
$this->finisherContext->getFinisherVariableProvider()->get(
$this->shortFinisherIdentifier,
'unique.value.identifier',
'default value'
);
- Check the existence of data::
$this->finisherContext->getFinisherVariableProvider()->exists(
$this->shortFinisherIdentifier,
'unique.value.identifier'
);
- Delete data::
$this->finisherContext->getFinisherVariableProvider()->remove(
$this->shortFinisherIdentifier,
'unique.value.identifier'
);
In this way, each finisher can access data programmatically. Moreover, it is
possible to retrieve the data via configuration, provided that a finisher
stores the values within the ``FinisherVariableProvider``.
Assuming that a finisher called 'Custom' sets data as follows::
$this->finisherContext->getFinisherVariableProvider()->add(
$this->shortFinisherIdentifier,
'unique.value.identifier',
'Wouter'
);
... you are now able to access the value 'Wouter' via ``{Custom.unique.value.identifier}``
in any other finisher.
.. code-block:: yaml
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}'
.. _concepts-finishers-customfinisherimplementations-extend-gui:
Add finisher to backend UI
''''''''''''''''''''''''''
After adding a custom finisher you can also add the finisher to the
form editor GUI to let your backend users configure it visually. Add the
following to the backend yaml setup:
.. code-block:: yaml
TYPO3:
CMS:
Form:
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:
__inheritances:
10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
100:
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'
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'
Make sure the setup file is registered in the backend:
.. code-block:: typoscript
module.tx_form.settings.yamlConfigurations {
123456789 = EXT:yourExtension/Configuration/Form/Backend.yml
}
@@ -0,0 +1,110 @@
.. include:: ../../Includes.txt
.. _concepts-formdefinition-vs-formconfiguration:
Form configuration vs. form definition
======================================
So far, we have only described the configuration of the form framework.
Once again, based on prototypes, the form configuration allows you to
define:
- which form elements, finishers, and validators are available,
- how those objects are pre-configured,
- how those objects will be displayed within the frontend and backend.
In contrast, the **form definition** describes the specific form, including
- all form elements and their corresponding validators,
- the order of the form elements within the form, and
- the finishers which are fired as soon as the form has been submitted.
- Furthermore, it defines the concrete values of each property of the
mentioned aspects.
In other words, the **prototype configuration** defines the existence of a
form element of type ``Text`` globally. The **form definition** declares
that such a form element of type ``Text`` is located on page 1 at position
1 of a specific form. In addition, it carries the information that this form
element comes with the HTML attribute "placeholder" with value "Your name
here". The form definition is written by the ``form editor``.
Example form definition
-----------------------
.. 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'
recipientAddress: 'your.company@example.com'
recipientName: 'Your company name'
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: summarypage
label: 'Summary page'
type: SummaryPage
@@ -0,0 +1,549 @@
.. include:: ../../Includes.txt
.. _concepts-formeditor:
Form editor
===========
.. _concepts-formeditor-general:
What does it do?
----------------
The ``form editor`` is a powerful graphical user interface which allows the
backend editor to create ``form definitions`` without writing a single line
of code. Those ``form definitions`` will be used by the frontend process to
render beautiful forms.
The ``form editor`` is a modular interface which consists of several
components:
- Stage: central visual component of the ``form editor`` which displays the
form elements in an abstract view and a frontend preview
- Tree: displays the structure of the form as a tree
- Inspector: context specific toolbar which handles the visual display of
form element options and allows editing those
- Core: includes core functionalities of the ``form editor``
- ViewModel: defines and steers the visual display
- Mediator: delegates events of the components
- Modals: processes modals
- FormEditor: provides API functions
- Helper: helper functions which mainly allow the manipulation of DOM
elements
Generally speaking, the ``Modals``, ``Inspector``, and ``Stage`` components
can be adapted through configuration. Especially the ``Inspector`` component
is modular and extremely flexible. As an integrator, you can reuse so-called
``inspector editors``. Those elements are input fields of different types
which allow the backend editor to alter all of the available form element
options.
.. figure:: ../../Images/javascript_module_interaction.png
:alt: JavaScript module interaction
JavaScript module interaction
There is a general ``form editor`` configuration which can be found below
the following configuration path:
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
formEditor:
Furthermore, you are able to configure the ``form editor`` regarding its
different aspects. The configuration can be found below the following
configuration paths:
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
formElementsDefinition:
<formElementTypeIdentifier>:
formEditor:
finishersDefinition:
<finisherIdentifier>
formEditor:
validatorsDefinition:
<validatorIdentifier>
formEditor:
.. _concepts-formeditor-stage:
Stage
-----
The ``Stage`` is the central visual component of the form editor which
displays the form elements in two different modes:
- abstract view: all form elements of a ``Page`` are presented in an
abstract way,
- frontend preview: renders the form like it will (nearly) be displayed in
the frontend ('nearly' since you have to make sure that your frontend CSS
is also loaded in the backend in order to get the exact preview).
Per default, the frontend templates of EXT:form are based on Twitter
Bootstrap. Since the backend of TYPO3 CMS also depends on this CSS framework,
the corresponding CSS files are already loaded in the backend context.
Nevertheless, certain parts of the CSS were overridden and extended in order
to meet the specific needs of the TYPO3 backend. Thus, the frontend preview
in the backend could differ compared to the "real" frontend.
If your frontend preview requires loading 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 you own Fluid templated and adapt the abstract view
till it suits your needs.
For more information, read the following chapter: ':ref:`Common abstract view form element templates<apireference-formeditor-stage-commonabstractformelementtemplates>`'.
.. _concepts-formeditor-inspector:
Inspector
---------
The ``Inspector`` component is situated on the right side of the ``form
editor``. It is a modular, extremely flexible, and context specific toolbar
which depends on the chosen form element. The ``Inspector`` allows editing
the form element's options with the help of so-called ``inspector editors``.
For the most parts, the interface can be easily customized by writing
YAML configuration. For each form element you can define which properties
are available and in which way they can be edited.
In addition to the editable form element properties (like ``properties.placeholder``)
there are so-called ``property collections`` which can be written by the
``form editor`` as well. Their definition is stored on the hierarchical
level of a form element. Right now, there are the following ``property
collections``:
- validators
- finishers
``Property collections`` also make use of ``inspector editors`` in order to
configure them properly. Due to this, we can do a lot of cool stuff. Imagine
we have got a validator "Number range" with two validator options called
"Minimum" and "Maximum". Additionally, we have got two form elements "Age
spouse" and "Age infant". For both form elements the validator is available
but for the form element "Age child" the validator option "Minimum" is not
editable and the option "Maximum" is pre-filled with a certain value.
.. _concepts-formeditor-basicjavascriptconcepts:
Basic JavaScript concepts
-------------------------
The form framework was designed to be as extendible as possible. Sooner or
later, you want to customize the components of the ``form editor`` 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
event listing.
.. _concepts-formeditor-basicjavascriptconcepts-registercustomjavascriptmodules:
Register custom JavaScript modules
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following YAML configuration registers an additional JavaScript module.
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
formEditor:
dynamicRequireJsModules:
additionalViewModelModules:
10: 'TYPO3/CMS/MySitePackage/Backend/FormEditor/ViewModel'
According to the example shown above, the JavaScript files have to be stored
within the folder ``my_site_package/Resources/Public/JavaScript/Backend/FormEditor/ViewModel.js``.
In the TYPO3 backend JavaScript files are loaded via RequireJS which depends
on a certain convention. The name of the module starts with **TYPO3/CMS**/MySitePackage/Backend/FormEditor/ViewModel
followed by your extension key TYPO3/CMS/**MySitePackage**/Backend/FormEditor/ViewModel.
Thus, you have to ensure that the module is stored within a subfolder of ``my_site_package/Resources/Public/JavaScript/``.
The part TYPO3/CMS/MySitePackage/**Backend/FormEditor**/ViewModel tells you
that your files have to be stored in my_site_package/Resources/Public/JavaScript/**Backend/FormEditor**/.
The last section TYPO3/CMS/MySitePackage/Backend/FormEditor/**ViewModel**
states the name of the JavaScript file without the file extension (.js).
Check out the following base template which shows you the recommended way
for setting up your own module.
.. code-block:: javascript
/**
* Module: TYPO3/CMS/MySitePackage/Backend/FormEditor/ViewModel
*/
define(['jquery',
'TYPO3/CMS/Form/Backend/FormEditor/Helper'
], function($, Helper) {
'use strict';
return (function($, Helper) {
/**
* @private
*
* @var object
*/
var _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' === $.type(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
*/
function bootstrap(formEditorApp) {
_formEditorApp = formEditorApp;
_helperSetup();
_subscribeEvents();
};
/**
* Publish the public methods.
* Implements the "Revealing Module Pattern".
*/
return {
bootstrap: bootstrap
};
})($, Helper);
});
.. _concepts-formeditor-basicjavascriptconcepts-events:
Events
^^^^^^
The event handling of EXT:form is based on the ``Publish/Subscribe Pattern``.
To learn more about this terrific pattern, check out this website: https://addyosmani.com/resources/essentialjsdesignpatterns/book/.
Please not that the processing sequence of the subscribers cannot be
influenced. Furthermore, there is no information flow between the
subscribers. All events have to be arranged asynchronously.
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
^^^^^^^^^^^^^^^^^
Within 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 by some additional data. The following example shows
you a ``form definition`` and the debug output of the corresponding
``FormElement model``.
.. code-block:: yaml
identifier: javascript-form-element-model
label: 'JavaScript FormElement model'
type: Form
finishers:
-
identifier: EmailToReceiver
options:
subject: 'Your message: {subject}'
recipientAddress: your.company@example.com
recipientName: 'Your Company name'
senderAddress: '{email}'
senderName: '{name}'
replyToAddress: ''
carbonCopyAddress: ''
blindCarbonCopyAddress: ''
format: html
attachUploads: 'true'
translation:
language: ''
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}",
"recipientAddress": "your.company@example.com",
"recipientName": "Your Company name",
"senderAddress": "{email}",
"senderName": "{name}",
"replyToAddress": "",
"carbonCopyAddress": "",
"blindCarbonCopyAddress": "",
"format": "html",
"attachUploads": true,
"translation": {
"language": ""
}
}
}
],
"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, you will find a property
called ``renderables``. Those ``renderables`` are arrays whose elements
consists of ``FormElement models`` of the particular child elements.
As previously mentioned, the ``FormElement model`` is a conglomerate of the
data of the ``form definition`` and some additional information:
- __parentRenderable
- __identifierPath
The following methods can be utilized in order to access the data of a
``FormElement model``:
- get()
- set()
- unset()
- on()
- off()
- getObjectData()
- toString()
- clone()
For more information, head to the API reference and read the section about
the ':ref:`FormElement model<apireference-formeditor-basicjavascriptconcepts-formelementmodel>`'.
.. _concepts-formeditor-translation-formeditor:
Translation of form editor
--------------------------
All option values which reside below the following configuration keys can be
translated:
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
formEditor:
formElementsDefinition:
<formElementTypeIdentifier>:
formEditor:
finishersDefinition:
<finisherIdentifier>
formEditor:
validatorsDefinition:
<validatorIdentifier>
formEditor:
The translation files of the ``form editor`` are loaded as follows:
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
formEditor:
translationFile:
# translation files for the form editor
10: 'EXT:form/Resources/Private/Language/Database.xlf'
20: 'EXT:my_site_package/Resources/Private/Language/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.
Imagine, the following is defined for an option value:
.. code-block:: yaml
...
label: 'formEditor.elements.Form.editor.finishers.label'
...
First of all, the process searches for the translation key ``formEditor.elements.Form.editor.finishers.label``
within the file ``20: 'EXT:my_site_package/Resources/Private/Language/Database.xlf'``
and after it inside the file ``10: 'EXT:form/Resources/Private/Language/Database.xlf'``.
If nothing is found, the option value will be displayed unmodified.
Due to compatibility issues, the setting ``translationFile`` is not defined
as an array in the default configuration. To load your own translation files,
you should define an array containing 'EXT:form/Resources/Private/Language/Database.xlf'
as first entry (key ``10``) followed by your own file (key ``20``) as
displayed in the example above.
.. _concepts-formeditor-extending:
Examples for extending the editor
---------------------------------
Learn :ref:`here <concepts-finishers-customfinisherimplementations-extend-gui>`
how to make the finisher configurable in the backend UI.
@@ -0,0 +1,144 @@
.. include:: ../../Includes.txt
.. _concepts-form-file-storages:
Form/ File storages
===================
EXT:form stores the form definitions within the file system and thus needs
write access to this storage. By default, the filemount ``user_uploads`` is
used. It is possible to configure a different and/ or an additional
filemount, which is then utilized for storing and reading forms.
The backend user will only see form definitions that are stored in
filemounts where the user has at least read access. The ``form editor`` and
the ``form plugin`` respect those access rights. In this way, you are able
to implement ACLs. If you have configured more than one filemount and the
backend user is able to access those, the ``form manager`` will allow the
user to choose the preferred storage in which the form will be saved.
Even cooler, form definitions can be stored in and shipped with your custom
extensions. If configured accordingly, the backend user will be able to
embed those forms. Furthermore, you can configure that these form
definitions:
- can be edited within the ``form editor``,
- can be deleted with the help of the ``form manager``.
By default, the aforementioned options are turned off. We decided to do so
because having dynamic content within an extension - which is possibly
version-controlled - is usually not a good idea. Furthermore, there is no
ACL system available.
**File uploads** will be saved within filemounts as well. They are handled
as FAL objects. The available filemounts for such uploads can be configured.
When adding/ editing a file upload element, the backend user can select the
desired upload storage.
.. note::
In principle, files in filemounts are publicly accessible. If the
uploaded files could contain sensitive data, you should suppress any
HTTP access to the filemount. This may, for example, be achieved by
creating a :file:`.htaccess` file, assuming you are using an Apache web
server. The directive of the :file:`.htaccess` file is fairly easy:
.. code-block:: apache
# Apache ≥ 2.3
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
# Apache < 2.3
<IfModule !mod_authz_core.c>
Order allow,deny
Deny from all
Satisfy All
</IfModule>
The following code block shows you how to configure additional filemounts
for form definitions.
.. code-block:: yaml
TYPO3:
CMS:
Form:
persistenceManager:
allowedFileMounts:
# default filemount, no need to redeclare it again
# just to show you the structure
# 10: 1:/form_definitions/
# additional filemounts
100: 1:/custom/forms/
110: 2:/cloudstorage/forms/
The following code block shows you how to allow an extension path as an
additional filemount for form definitions.
.. code-block:: yaml
TYPO3:
CMS:
Form:
persistenceManager:
allowedExtensionPaths:
10: EXT:my_site_package/Resources/Private/Forms/
Add the following config if you want to allow backend users to **edit**
forms stored within your own extension.
.. code-block:: yaml
TYPO3:
CMS:
Form:
persistenceManager:
allowSaveToExtensionPaths: true
Add the following config if you want to allow backend users to **delete**
forms stored within your own extension.
.. code-block:: yaml
TYPO3:
CMS:
Form:
persistenceManager:
allowDeleteFromExtensionPaths: true
The following code blocks show you the default setup for filemounts that
are used for file (and image) uploads.
.. code-block:: yaml
TYPO3:
CMS:
Form:
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
properties:
saveToFileMount: '1:/user_upload/'
editors:
400:
selectOptions:
10:
value: '1:/user_upload/'
label: '1:/user_upload/'
@@ -0,0 +1,145 @@
.. include:: ../../Includes.txt
.. _concepts-formmanager:
Form manager
============
.. _concepts-formmanager-general:
What does it do?
----------------
The ``form manager`` can be accessed by opening the backend module 'Forms'.
It allows the editor to administer all of the existing forms stored on the
accessible filemounts. The central element of the ``form manager`` is a
table view which...
- lists all forms
- allows users to create, edit, duplicate, and delete forms
- names the storage folder
- gives a broad overview on which pages the listed forms are used in.
The creation and duplication of forms is supported by a so-called ``form
wizard``. The wizard guides the editor through the process and offers a
variety of settings depending on the form configuration. Those settings
include choosing filemounts, prototypes, 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
---------------
This is a very nifty feature. When creating a new form, the ``form manager``
allows the backend editor to select a so-called ``Start template``. Such a
template is a predefined ``form definition`` without the property
``prototypeName`` which is normally used as a foundation of a new form.
As an integrator, you can specify as many ``Start templates`` as you desire
for a given ``prototype``. After you have defined such a template, follow
these easy steps to use your defined ``Start templates`` as a foundation:
- open the ``Forms`` module
- create a new form by clicking on the appropriate button
- enter the 'Form name' and click the checkbox 'Advanced settings'
- during the next steps you can select a ``Start template``
For each ``prototype``, you have to define a ``Start template`` in order to
enable the editor to choose one. Additionally, the same ``Start template``
can be used for several ``prototypes``. To do so, make sure the included
form elements of the template are defined in the corresponding ``prototype``.
For example, imagine your integrator has :ref:`configured<typo3.cms.form.formmanager.selectablePrototypesConfiguration>`
a prototype called 'routing' which contains a custom form element with the
``<formElementTypeIdentifier>`` 'locationPicker'. The element is only
defined for this prototype. The integrator has created a ``Start template``
which carries the 'locationPicker' form element. A backend editor could now
select and use this ``Start template``, including the custom form element,
as long as the ``prototype`` is set to 'routing'. If the integrator also
adds this custom form element to another ``prototype``, the process would
crash. The custom form element is only known by the ``prototype`` 'routing'.
The following code block shows the minimal configuration of a ``Start
template``. You need 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'
As mentioned previously, the form wizard within the ``form manager`` offers
a list of all existing, :ref:`pre-configured<typo3.cms.form.formmanager.selectableprototypesconfiguration.*.newformtemplates>`
``Start templates``. As soon as the backend editor creates a form with the
help of such a template, a new ``form definition`` is generated based on the
one of the selected ``Start template``. The ``form definition`` will be
enriched by the property ``propertyName`` defining the chosen ``prototype``.
The ``identifier`` of the root form element ('Form') is automatically set
based on the entered "Form name". Additionally, this name is 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 which reside below the following configuration keys can be
translated:
.. code-block:: yaml
TYPO3:
CMS:
Form:
formManager:
The translation files of the ``form manager`` are loaded as follows:
.. code-block:: yaml
TYPO3:
CMS:
Form:
formManager:
translationFile:
# translation files for the form manager
10: 'EXT:form/Resources/Private/Language/Database.xlf'
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.
Imagine, the following is defined for an option value:
.. code-block:: yaml
...
label: 'formManager.selectablePrototypesConfiguration.standard.label'
...
First of all, the process searches for the translation key ``formManager.selectablePrototypesConfiguration.standard.label``
within the file ``20: 'EXT:my_site_package/Resources/Private/Language/Form/Database.xlf'``
and after it inside the file ``10: 'EXT:form/Resources/Private/Language/Database.xlf'``.
If nothing is found, the option value will be displayed unmodified.
Due to compatibility issues, the setting ``translationFile`` is not defined
as an array in the default configuration. To load your own translation files,
you should define an array containing 'EXT:form/Resources/Private/Language/Database.xlf'
as first entry (key ``10``) followed by your own file (key ``20``) as
displayed in the example above.
@@ -0,0 +1,87 @@
.. include:: ../../Includes.txt
.. _concepts-formplugin:
Form plugin
===========
.. _concepts-formelugin-general:
What does it do?
----------------
The form plugin allows you to assign a form - created with the ``form
editor`` or shipped with your extension - to a specific page. This enables
you to re-use forms throughout the whole TYPO3 installation. Furthermore, it
offers the backend editor the possibility to override certain aspects of the
form definition. At the moment, only finisher options can be overridden. The
possibilities depend on the configuration of the underlying prototype.
Imagine, your form contains a redirect finisher. The redirect target is set
globally and valid for the whole ``form definition`` . While adding the form
to a specific page, the backend editor can define a different redirect targeting. This
setting is only valid for the page containing the plugin.
Read more about changing the :ref:`general<typo3.cms.form.prototypes.\<prototypeidentifier>.formengine>`
and :ref:`aspect-specific form plugin configuration<typo3.cms.form.prototypes.\<prototypeIdentifier>.finishersdefinition.\<finisheridentifier>.formengine>`.
.. _concepts-formplugin-translation-formengine:
Translation of form plugin
--------------------------
All option values which reside below the following configuration keys can be
translated:
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
finishersDefinition:
<finisherIdentifier>
formEngine:
The translation files of the ``form plugin`` are loaded as follows:
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
formEngine:
translationFile:
# translation files for the form plugin (finisher overrides)
10: 'EXT:form/Resources/Private/Language/Database.xlf'
20: 'EXT:my_site_package/Resources/Private/Language/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.
Imagine, the following is defined for an option value:
.. code-block:: yaml
...
label: 'tt_content.finishersDefinition.EmailToReceiver.label'
...
First of all, the process searches for the translation key
``tt_content.finishersDefinition.EmailToReceiver.label`` within the file
20: 'EXT:my_site_package/Resources/Private/Language/Database.xlf' and after
it inside the file 10: 'EXT:form/Resources/Private/Language/Database.xlf'.
If nothing is found, the option value will be displayed unmodified.
Due to compatibility issues, the setting ``translationFile`` is not defined
as an array in the default configuration. To load your own translation files,
you should define an array containing 'EXT:form/Resources/Private/Language/Database.xlf'
as first entry (key ``10``) followed by your own file (key ``20``) as
displayed in the example above.
@@ -0,0 +1,878 @@
.. include:: ../../Includes.txt
.. _concepts-frontendrendering:
==================
Frontend rendering
==================
.. _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 ``TYPO3\CMS\Form\Domain\Model\FormDefinition`` encapsulates
a complete ``form definition``, with all of its
- pages,
- form elements,
- applicable validation rules, and
- finishers, which should be 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 any given time.
Moreover, there is a navigation to go back and forth between those pages. A
``Page`` consists of multiple ``FormElements`` which represent the input
fields, textareas, checkboxes, etc. shown on a page. The ``FormDefinition``
domain model, ``Page`` and ``FormElement`` objects have ``identifier``
properties which must be unique for each given ``<formElementTypeIdentifier>``,
i.e. the ``FormDefinition`` domain model and a ``FormElement`` object may
have the same ``identifier`` but having the same identifier for two
``FormElement`` objects is disallowed.
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-anatomy-example:
Example
'''''''
Basically, you can manually create a ``FormDefinition`` domain model just
by calling the API methods on it, or you can use a ``FormFactory`` to build
the form from a different representation format such as YAML::
$formDefinition = $this->objectManager->get(FormDefinition::class, 'myForm');
$page1 = $this->objectManager->get(Page::class, 'page1');
$formDefinition->addPage($page);
// second argument is the <formElementTypeIdentifier> of the form element
$element1 = $this->objectManager->get(GenericFormElement::class, 'title', 'Text');
$page1->addElement($element1);
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-createformusingabstracttypes:
Creating a form using abstract form element types
"""""""""""""""""""""""""""""""""""""""""""""""""
While you can use the ``TYPO3\CMS\Form\Domain\Model\FormDefinition::addPage()``
or ``TYPO3\CMS\Form\Domain\Model\FormElements\Page::addElement()`` methods
and create the ``Page`` and ``FormElement`` objects manually, it is often
better to use the corresponding create* methods (``TYPO3\CMS\Form\Domain\Model\FormDefinition::createPage()``
and ``TYPO3\CMS\Form\Domain\Model\FormElements\Page::createElement()``), as
you pass them an abstract ``<formElementTypeIdentifier>`` such as ``Text``
or ``Page``. EXT:form will automatically resolve the implementation class
name and set default values.
The :ref:`simple example <concepts-frontendrendering-basiccodecomponents-formdefinition-anatomy-example>`
shown above should be rewritten as follows::
// we will come back to this later on
$prototypeConfiguration = [];
$formDefinition = $this->objectManager->get(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 by using a ``GenericFormElement``. This is configured in the
``$prototypeConfiguration``. To make the example from above actually work,
we need to add some meaningful values to ``$prototypeConfiguration``::
$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 form that, it is always possible to set default values
for all configuration options of such elements, as the following example
shows::
$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 ``TYPO3\CMS\Form\Domain\Configuration\ConfigurationService``
contains helper methods which return the ready-to-use ``$prototypeConfiguration``.
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-rednering:
Rendering a FormDefinition
""""""""""""""""""""""""""
To trigger the rendering of a ``FormDefinition`` domain model, the current
``TYPO3\CMS\Extbase\Mvc\Web\Request`` needs to be bound to the
``FormDefinition``. This binding results in a ``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::
// $currentRequest and $currentResponse need to be available
// inside a controller, you would use $this->request and $this->response;
$form = $formDefinition->bind($currentRequest, $currentResponse);
// 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 ``TYPO3\CMS\Form\Domain\Model\FormDefinition::bind()``.
.. _concepts-frontendrendering-basiccodecomponents-formruntime-render:
Rendering a form
""""""""""""""""
Rendering a form is easy. Just call ``render()`` on the ``FormRuntime``::
$form = $formDefinition->bind($request, $response);
$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 ``FormRuntime`` object like an array. If a form element with the
identifier ``firstName`` exists, you can use ``$form['firstName']`` to
retrieve its current value. You can set values the same way.
.. _concepts-frontendrendering-basiccodecomponents-formruntime-renderinginternals:
Rendering internals
"""""""""""""""""""
The ``FormRuntime`` inquires the ``FormDefinition`` domain model regarding
the configured renderer (``TYPO3\CMS\Form\Domain\Model\FormDefinition::getRendererClassName()``)
and then triggers render() on this Renderer.
This allows you to declaratively define how a form should be rendered.
.. code-block:: yaml
TYPO3:
CMS:
Form:
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 ``TYPO3\CMS\Form\Domain\Renderer\RendererInterface``
implementation which used to render a ``FormDefinition`` domain model. It
is the default EXT:form renderer.
Learn more about the :ref:`FluidFormRenderer Options<apireference-frontendrendering-fluidformrenderer-options>`.
.. _concepts-frontendrendering-codecomponents-customformelementimplementations:
Custom form element implementations
-----------------------------------
EXT:form ships a decent amount of hooks which are available at crucial
points of the life cycle of a ``FormElement``. Most of the time, own
implementations are therefore unnecessary. An own form element can be
defined by:
- writing some configuration, and
- utilizing the standard implementation of ``TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement``.
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
formElementsDefinition:
CustomFormElementIdentifier:
implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement'
With the provided hooks, this ``FormElement`` can now be manipulated.
If you insist on your own implementation, the abstract class ``TYPO3\CMS\Form\Domain\Model\FormElements\AbstractFormElement``
offers a perfect entry point. In addition, we recommend checking-out ``TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable``.
All of your own form element implementations must be programmed to the
interface ``TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface``.
It is a good idea to derive your implementation from ``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 {
10 = < lib.stdheader
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:
...
.. _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:
Hooks
-----
EXT:form implements a decent amount of hooks 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:`API reference section<apireference-frontendrendering-runtimemanipulation-hooks>`'
for more details.
.. _concepts-frontendrendering-runtimemanipulation-typoscriptoverrides:
TypoScript overrides
--------------------
Each and every ``form definition`` can be overridden via TypoScript if the
``FormFrontendController`` of EXT: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 ``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
}
}
}
}
}
}
}
}
.. _concepts-frontendrendering-templates:
Templates
=========
The Fluid templates of the form framework are based on Twitter Bootstrap.
.. _concepts-frontendrendering-templates-customtemplates:
Custom templates
----------------
If you want to use custom Fluid templates for the frontend output of the
form elements, you cannot register an additional template path using
TypoScript. Instead, the registration of new template paths has to be done
via YAML. The settings are part of the ``prototypes`` configuration.
.. code-block:: yaml
TYPO3:
CMS:
Form:
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/'
For each ``form definition`` - which references the prototype ``standard`` -
the form framework will additionally look for Fluid templates within the
path 'EXT:my_site_package/Resources/Private/Frontend/[*]' as set above.
Apart from the 'Form' element, the process will search for templates within
the ``partialRootPaths`` folder. The name of the partial is derived from the
property ``formElementTypeIdentifier``. For example, the template of the
form element ``Text`` must be stored within the ``partialRootPaths`` folder
named ``Text.html``. In contrast, the template of the ``Form`` element must
reside within the ``templateRootPaths`` folder. According to the introduced
logic, the template name must be ``Form.html``.
.. _concepts-frontendrendering-translation:
Translation
===========
.. _concepts-frontendrendering-translation-formdefinition:
Translate form definition
-------------------------
The translation of ``form definitions`` works differently to the translation
of the backend aspects. Currently, there is no graphical user interface
supporting the translation process.
If the backend editor needed to translate the ``form definition`` properties
in the same way the backend aspects are translated, he/ she would see long
and unwieldy translation keys while editing a form within the ``form editor``.
In order to avoid this, rather the element properties are translated than
their values. Thus, the form framework does not look for translation keys
within the translation file. Instead, the system searches for translations
of the form element properties independent of their property values. The
property values are ignored if the process finds a proper entry within the
translation file. As a result, the property values are overridden by the
translated value.
This approach is a compromise between two scenarios: the exclusive usage of
the ``form editor`` and/ or the manual creation of ``form definitions``
which can afterwards (theoretically) be edited with the ``form editor``. In
addition, the described compromise allows the editor to create forms in the
default language whose form element property values are displayed as
specified in the ``form editor``. Based on this, an integrator could provide
additional language files which automatically translate the specific form.
Additional translation files can be defined as follows:
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
formElementsDefinition:
Form:
renderingOptions:
translation:
translationFile:
# translation files for the frontend
10: 'EXT:form/Resources/Private/Language/locallang.xlf'
20: 'EXT:my_site_package/Resources/Private/Language/Form/locallang.xlf'
Due to compatibility issues, the setting ``translationFile`` is not defined
as an array in the default configuration. To load your own translation files,
you should define an array containing 'EXT:form/Resources/Private/Language/Form/locallang.xlf'
as first entry (key ``10``) followed by your own file (key ``20``) as
displayed in the example above. The array is processed from the highest key
to the lowest, i.e. your translation file with the key ``20`` is processed
first. If the look-up process does not find a key within all of the provided
files, the property value will be displayed unmodified.
The following properties can be translated:
- label
- properties.[*]
- properties.options.[*]
- properties.fluidAdditionalAttributes.[*]
- renderingOptions.[*]
The translation keys are put together based on a specific pattern. In
addition, a fallback chain that depends on the form element identifiers
exists. As a result, the following translation scenarios are possible:
- translation of a form element property for a specific form and form
element
- translation of a form element property for a specific form element and
various forms
- translation of a form element property for an element type 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:
- ``<formDefinitionIdentifier>.element.<elementIdentifier>.properties.<propertyName>``
- ``element.<formElementIdentifier>.properties.<propertyName>``
- ``element.<elementType>.properties.<propertyName>``
Form elements with option properties (``properties.options``), like the
``Select`` element, feature the following look-up process:
- ``<formDefinitionIdentifier>.element.<elementIdentifier>.properties.options.<propertyValue>``
- ``element.<elementIdentifier>.properties.options.<propertyValue>``
Example
"""""""
.. 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
For the form element ``LastName``, the process will look for the following
translation keys within the translation files:
- ``ApplicationForm.element.LastName.properties.label``
- ``element.LastName.properties.label``
- ``element.Text.properties.label``
If none of the above-mentioned keys exist, 'Last name' will be displayed.
For the form element ``Software``, the process will look for the following
translation keys within the translation files:
- ``ApplicationForm.element.Software.properties.label``
- ``element.Software.properties.label``
- ``element.MultiSelect.properties.label``
If none of the above-mentioned keys exist, 'Known software' will be
displayed. The option properties are addressed as follows:
- ``ApplicationForm.element.Software.properties.options.value1``
- ``element.Software.properties.options.value1``
- ``ApplicationForm.element.Software.properties.options.value2``
- ``element.Software.properties.options.value2``
If none of the above-mentioned keys exist, 'TYPO3' will be displayed as
label for the first option and 'Neos' as label 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``. The same translation files can be used. If the look-up
process does not find a key within the provided files, the appropriate
message of the Extbase framework will be displayed. EXT:form already
translates all of those validators by default.
As mentioned above, the translation keys are put together based on a
specific pattern. Furthermore, the fallback chain exists here as well. Thus,
the following translation scenarios are possible:
- translation of validation messages for a specific validator of a concrete
form element and concrete form
- translation of validation messages for a specific validator of various
form elements within a concrete form
- translation of validation messages for a specific validator of a concrete
form element in various forms
- translation of validation messages for a specific validator within various
forms
In Extbase, the validation messages are identified with the help of
numerical codes (UNIX timestamps). For the same validator, different codes
are valid. Read more about :ref:`concrete validator configurations <typo3.cms.form.prototypes.\<prototypeidentifier>.validatorsdefinition.\<validatoridentifier>-concreteconfigurations>`.
The look-up process searches for translation keys in all given translation
files based on the following order:
- ``<formDefinitionIdentifier>.validation.error.<elementIdentifier>.<validationErrorCode>``
- ``<formDefinitionIdentifier>.validation.error.<validationErrorCode>``
- ``validation.error.<elementIdentifier>.<validationErrorCode>``
- ``validation.error.<validationErrorCode>``
Example
"""""""
.. 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
Amongst others, the ``NotEmpty`` validator sends 1221560910 as ``<validationErrorCode>``.
If a user submits this form without providing a value for the field "Last
name", the ``NotEmpty`` validator fails. Now, the look-up process searches
for the following translation keys for the ``NotEmpty`` validator combined
with the form element ``LastName``:
- ContactForm.validation.error.LastName.1221560910
- ContactForm.validation.error.1221560910
- validation.error.LastName.1221560910
- validation.error.1221560910
As mentioned above, if there is no corresponding translation key available,
the default message of the Extbase framework will be shown.
.. _concepts-frontendrendering-translation-finishers:
Translation of finisher options
-------------------------------
The translation of finisher options is similar to the translation of
``form definitions``. The same translation files can be used. If the look-up
process does not find a key within all provided files, the property value
will be displayed unmodified.
As mentioned above, the translation keys are put together based on a
specific pattern. Furthermore, the fallback chain exists here as well. Thus,
the following translation scenarios are possible:
- translation of finisher options for a specific finisher of a concrete form
- translation of finisher options for a specific finisher of various forms
The look-up process searches for translation keys in all given translation
files based on the following order:
- ``<formDefinitionIdentifier>.finisher.<finisherIdentifier>.<optionName>``
- ``finisher.<finisherIdentifier>.<optionName>``
Example
"""""""
.. 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
``<finisherIdentifier>`` 'Confirmation' and the option 'message':
- ``ContactForm.finisher.Confirmation.message``
- ``finisher.Confirmation.message``
If no translation key exists, the message 'Thank you for your inquiry.' will
be shown.
.. _concepts-frontendrendering-translation-arguments:
Form element translation arguments are supported
================================================
Form element property translations and finisher option translations can use
placeholders to output translation arguments. Translations can be enriched
with variable values by passing arguments to form element properties. The
feature was introduced with :issue:`81363`.
Form element properties
-----------------------
Pure YAML is sufficient to add simple, static values:
.. code-block:: yaml
renderables:
- identifier: field-with-translation-arguments
type: Checkbox
label: This is a %s feature
renderingOptions:
translation:
translationFile: path/to/locallang.xlf
arguments:
label:
- useful
This will produce the label: `This is a useful feature`.
Alternatively, translation arguments can be set via
:ts:`formDefinitionOverrides` in TypoScript. A common usecase is a checkbox for
user confirmation linking to details of the topic. Here it makes sense to use
YAML hashes instead of YAML lists to give sections named keys. This simplifies
references in TypoScript a lot since named keys are way more readable and also
keep the setup working in case elements are reordered. With lists and numeric
keys the TypoScript setup would also need to be updated in this case.
In the following example the list of :yaml:`renderables` has been replaced with
a hash of :yaml:`renderables` and the field :yaml:`field-with-translation-arguments`
has received a named key :yaml:`fieldWithTranslationArguments`. This key can be anything
as long as it is unique on the same level, usually simply copying the :yaml:`identifier`
should be enough:
.. 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:
translationFile: path/to/locallang.xlf
The following TypoScript setup uses the named key :yaml:`fieldWithTranslationArguments` to refer
to the field and adds a page URL as translation argument:
.. 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 was not registered with a named key so a numeric
key :yaml:`0` must be used which, as mentioned above, is prone to errors when more pages are added
or pages are reordered.
.. important::
There must be at least one translation file with a translation for the
configured form element property. Arguments are not inserted into default
values defined 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
recipientAddress: foo@example.org
senderAddress: bar@example.org
translation:
translationFile: path/to/locallang.xlf
arguments:
subject:
- awesome
This will produce `My awesome subject`.
@@ -0,0 +1,26 @@
.. include:: ../Includes.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
@@ -0,0 +1,48 @@
.. include:: ../../Includes.txt
.. _concepts-introduction:
Target groups and main principles
=================================
As :ref:`mentioned earlier<what-does-it-do>`, the ``form`` extension can be
seen as a framework which allows editors, integrators, and developers to
create and manage all kind of forms. For this task, different interfaces
and techniques are available.
Conceptually, EXT:form always tries to consider the ``form editor`` first.
The requirements for the ``form editor`` differ between the defined target
groups. On the one hand, as an integrator, you may want to manage HTML
class attributes. On the other hand, as a developer you may want to use the
``form editor`` as a kick starter for complex ``form definitions``, and you
may want to edit all possible (technical) properties you can think of.
The form extension tries to find a compromise for such cases. Since the
``form editor`` is mainly used by backend editors, only simple,
nontechnical properties are displayed and editable. However, EXT:form
allows you to easily extend the ``form editor`` by writing some YAML
configurations.
If this is not enough for your specific project, EXT:form provides a way to
integrate your own JavaScript code by utilizing the JavaScript API. Thus,
it should be possible to meet all your requirements.
Your forms can be created and defined globally in the ``form module`` and/
or loaded from extensions. Within the ``Mail form`` content element, one of
those forms can be referenced.
Furthermore, certain aspects of a form can be overridden in the plugin. This
concept allows you to reuse the same form on different pages with the same,
or a different, configuration.
The following explanations will show you that there are many ways to
manipulate the form framework in different contexts.
Those explanations are partly contradictory, depending on your use case. It
is up to you how you want to use the form framework. Be creative and share
your solution with the TYPO3 community!
This chapter attempts to describe the basics of the form framework. Check
out the reference and the example sections to get a deeper understanding of
the framework.
@@ -0,0 +1,296 @@
.. include:: ../../Includes.txt
.. _concepts-validators:
Validators
==========
The form framework ships a set of server-side validators, which implement
Extbase validators. Be aware that not all of the existing validators are
available for each form element, e.g. the "Date range validator" is
only available for the "Date" element. Furthermore, some form elements
(like "Email") already contain reasonable validators.
With the help of the form element property ``validationErrorMessages``
you can define custom validation error messages. The message can also
be set within the form editor.
.. _concepts-validators-client-side-validation:
Client-side validation
----------------------
If a form element is configured accordingly, the form framework adds
HTML 5 based frontend validation. Nevertheless, there is no JavaScript
validation included by default. We as the TYPO3 core have no plans to
opt for a specific solution. This has to be integrated manually.
Reliable and 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 validations
"""""""""""""""""""""""""""""""""""""""
The displayed validation message is a browser specific text. The output
is not generated by TYPO3 and therefore you cannot change it easily.
Nevertheless, there is a JavaScript solution for changing the validation
message. 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
""""""""""""""""""""""
The 'Alphanumeric validator' checks for alphanumeric strings.
Alphanumeric is defined as a combination of alphabetic and numeric
characters [A-Z + 0-9].
.. _concepts-validators-date_time:
Date/ time validator
""""""""""""""""""""
The 'Date/ time validator' checks if the given 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-date_range:
Date range validator
""""""""""""""""""""
The 'Date range validator' checks if the given value is a valid DateTime
object and in-between a specified date range. The range can be defined by
providing a minimum and/ or maximum date. The validator has 2 options:
- Minimum date: The minimum date.
- Maximum date: The maximum date.
The properties ``minimum`` and ``maximum`` must have the format 'Y-m-d' which
represents the RFC 3339 'full-date' format.
Read more: https://www.w3.org/TR/2011/WD-html-markup-20110405/input.date.html
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
date and the maximum date can be configured through the validator options.
.. _concepts-validators-email:
Email validator
"""""""""""""""
The 'Email validator' checks if the given value is a valid email address.
The format of a valid email address is defined in `RFC 3696 <http://tools.ietf.org/html/rfc3696>`_.
The standard allows international characters and the multiple appearance
of the @ sign.
.. _concepts-validators-empty:
Empty validator
"""""""""""""""
The 'Empty validator' checks if the given value is not empty (NULL,
empty string, empty array or empty object).
.. _concepts-validators-filesize:
File size validator
"""""""""""""""""""
The 'File size validator' validates a file resource regarding its file
size. The validator has 2 options:
- Minimum: The minimum file size to accept.
- Maximum: The maximum file size to accept.
Use the format <size>B|K|M|G when entering file sizes. For example: 10M
means 10 megabytes. Please keep in mind that the maximum file size also
depends on the php.ini settings of your environment.
.. _concepts-validators-floating_point:
Floating-point number validator
"""""""""""""""""""""""""""""""
The 'Floating-point number validator' checks if the given value is of
type float or a string matching the regular expression [0-9.e+-].
.. _concepts-validators-integer:
Integer number validator
""""""""""""""""""""""""
The 'Integer number validator' checks if the given value is a valid
integer.
.. _concepts-validators-text:
Non-XML text validator
""""""""""""""""""""""
The 'Non-XML text validator' checks if the given value is a valid text
(contains no XML tags). This basically means, that tags are stripped.
In this special case quotes are not encoded (see `filter_var()
<http://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 in every
circumstance, if you want to be sure of that, use a customized regular
expression or filter on output.
.. _concepts-validators-count:
Number of submitted values validator
""""""""""""""""""""""""""""""""""""
The 'Number of submitted values validator' checks if the given value
contains the specified amount of elements. The validator has 2 options:
- Minimum: The minimum number of submitted values.
- Maximum: The maximum number of submitted values.
.. _concepts-validators-number_range:
Number range validator
""""""""""""""""""""""
The 'Number range validator' checks if the given value is a number in
the specified range. The validator has 2 options:
- Minimum: The minimum value to accept.
- Maximum: The maximum vlaue to accept.
.. _concepts-validators-mimetype:
MimeType validator
""""""""""""""""""
The 'MimeType validator' checks if the given value is a FileReference
of the configured type (one of the */* IANA media types).
.. _concepts-validators-string_length:
String length validator
"""""""""""""""""""""""
The 'String length validator' checks if the given value is a valid string
and its length is in the specified range. The validator has 2 options:
- Minimum: The minimum length of the string.
- Maximum: The maximum length of the string.
.. _concepts-validators-regular_expressions:
Regular expression validator
""""""""""""""""""""""""""""
The 'Regular expression validator' checks if the given value matches
the specified regular expression. Delimiters or modifiers are not
supported. The validator has 1 option:
- Regular expression: The regular expression to use for validation.
Imagine the following example. You want the user to provide a domain
name. The submitted value shall only contain the second and the top
level domain, e.g. "typo3.org" instead of "https://typo3.org". The
regular expression for this use case would be
:code:`/^[-a-z0-9]+\.[a-z]{2,6}$/`.
.. _concepts-validators-validation-message-translation:
Translation of validation messages
----------------------------------
To learn more about this topic, please continue :ref:`here<concepts-frontendrendering-translation-validationerrors>`.
.. _concepts-validators-customvalidatorimplementations:
Custom validator implementations
--------------------------------
Validators belong to a certain ``prototype`` and are defined within the
``validatorsDefinition``. The property ``implementationClassName`` is used
for the validator implementation.
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
validatorsDefinition:
Custom:
implementationClassName: 'VENDOR\MySitePackage\Domain\Validation\CustomValidator'
You can provide options for your validator using the property ``options``.
Those will be used as default values which can be overridden within a
specific ``form definition``.
Define the default value of the option ``yourCustomOption``:
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
validatorsDefinition:
Custom:
implementationClassName: 'VENDOR\MySitePackage\Domain\Validation\CustomValidator'
options:
yourCustomOption: 'Jurian'
Override the default value within your ``form definition``:
.. code-block:: yaml
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 implements Extbase validators. That said,
your own validators should extend ``TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator``.
Read more about this topic in the `Extbase docs <https://docs.typo3.org/typo3cms/ExtbaseFluidBook/9-CrosscuttingConcerns/2-validating-domain-objects.html>`_.
@@ -0,0 +1,639 @@
.. include:: ../../Includes.txt
.. _concepts-variants:
Variants
========
.. _concepts-variants-basics:
Basics
------
Variants allow you to change properties of form elements, validators,
and finishers and are activated by conditions. This allows you among
other things:
* translating form element values depending on the frontend language
* setting and removing validators of one form element depending on the
value of another form element
* hiding entire steps (form pages) depending on the value of a form
element
* setting finisher options depending on the value of a form element
* hiding a form element in certain finishers and on the summary step
Variants are defined on the form element level either statically in
form definitions or created programmatically through an API. The
variants defined within a form definition are automatically applied to
the form based on their conditions at runtime. Programmatically,
variants can be applied at any time.
Furthermore, conditions of a variant can be evaluated programmatically
at any time. However, some conditions are only available at runtime,
for example a check for a form element value.
Custom conditions and operators can be added easily.
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 within a variant, the original finishers and validators
will be used. If at least one finisher or validator is defined in a
variant, the originally defined finishers or validators are overwritten
by the list of finishers and validators of the variant.
Variants defined within 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::
At the current state it is **not** possible to define variants in
the UI of the form editor.
.. _concepts-variants-enabled-property:
Rendering option ``enabled``
----------------------------
The rendering option :yaml:`enabled` is available for all finishers and
all form elements - except the root form element and the first form
page. The option accepts a boolean value (:yaml:`true` or :yaml:`false`).
Setting :yaml:`enabled: true` for a form element renders it in the
frontend and enables processing of its value including property mapping
and validation. Setting :yaml:`enabled: false` disables the form
element in the frontend. All form elements and finishers except the root form element and the first form page can be enabled
or disabled.
Setting :yaml:`enabled: true` for a finisher executes it when
submitting forms. 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 about using this rendering option.
.. _concepts-variants-definition:
Definition of variants
----------------------
Variants are defined on the form element level. Check the following -
incomplete - example:
.. code-block:: yaml
type: Text
identifier: text-1
label: Foo
variants:
-
identifier: variant-1
condition: 'formValues["checkbox-1"] == 1'
# If the condition matches, the label property of the form
# element is set to the value 'Bar'
label: Bar
As usual, :yaml:`identifier` must be a unique name of the variant on
the form element level.
Each variant has a single :yaml:`condition` which applies the variants'
changes as soon as the condition matches. In addition, the remaining
properties are applied to the form element as well. In the
aforementioned example the label of the form element :yaml:`text-1` is
changed to ``Bar`` if the checkbox :yaml:`checkbox-1` is checked.
The following properties can be overwritten by variants within the
topmost element (:yaml:`Form`):
* :yaml:`label`
* :yaml:`renderingOptions`
* :yaml:`finishers`
* :yaml:`rendererClassName`
The following properties can be overwritten by variants within all of
the other form elements:
* :yaml:`enabled`
* :yaml:`label`
* :yaml:`defaultValue`
* :yaml:`properties`
* :yaml:`renderingOptions`
* :yaml:`validators`
.. note::
To selectively unset list items in variants like select options the special value :code:`__UNSET` can be used as value for the item to remove.
.. _concepts-variants-conditions:
Conditions
----------
The form framework uses the Symfony component `expression language <https://symfony.com/doc/4.1/components/expression_language.html>`_.
Here, an expression is a one-liner that returns a boolean value like
:yaml:`applicationContext matches "#Production/Local#"`. Please check
the `Symfony docs <https://symfony.com/doc/4.1/components/expression_language/syntax.html>`_
to learn more about this topic. The form framework extends the
expression language with some variables which can be used to access
form values and environment settings.
``formRuntime`` (object)
^^^^^^^^^^^^^^^^^^^^^^^^
You can access every public method from the :php:`\TYPO3\CMS\Form\Domain\Runtime\FormRuntime`,
learn more :ref:`here<apireference-frontendrendering-programmatically-apimethods-formruntime>`.
For example:
:yaml:`formRuntime.getIdentifier() == "test"`.
``formValues`` (array)
^^^^^^^^^^^^^^^^^^^^^^
:yaml:`formValues` holds all the submitted form element values. Each
key within this array represents a form element identifier.
For example:
:yaml:`formValues["text-1"] == "yes"`.
``stepIdentifier`` (string)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
:yaml:`stepIdentifier` is set to the :yaml:`identifier` of the current
step.
For example:
:yaml:`stepIdentifier == "page-1"`.
``stepType`` (string)
^^^^^^^^^^^^^^^^^^^^^
:yaml:`stepType` is set to the :yaml:`type` of the current step.
For example:
:yaml:`stepType == "SummaryPage"`.
``finisherIdentifier`` (string)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:yaml:`finisherIdentifier` is set to the :yaml:`identifier` of the
current finisher or an empty string (while no finishers are executed).
For example:
:yaml:`finisherIdentifier == "EmailToSender"`.
``siteLanguage`` (object)
^^^^^^^^^^^^^^^^^^^^^^^^^
You can access every public method from :php:`\TYPO3\CMS\Core\Site\Entity\SiteLanguage`.
The most needed ones are for sure:
* getLanguageId() / Aka sys_language_uid.
* getLocale() / The language locale. Something like 'en_US.UTF-8'.
* getTypo3Language() / The language key for XLF files. Something like
'de' or 'default'.
* getTwoLetterIsoCode() / Returns the ISO-639-1 language ISO code.
Something like 'de'.
For example:
:yaml:`siteLanguage("locale") == "de_DE"`.
``applicationContext`` (string)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:yaml:`applicationContext` is set to the application context
(@see GeneralUtility::getApplicationContext()).
For example:
:yaml:`applicationContext matches "#Production/Local#"`.
``contentObject`` (array)
^^^^^^^^^^^^^^^^^^^^^^^^^
:yaml:`contentObject` is set to the data of the current content object
or to 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' => 'formValues["checkbox-1"] == 1',
'label' => 'foo',
]);
Get all 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 defined conditions::
$formElement->applyVariant($variant);
.. _concepts-variants-examples:
Examples
--------
Here are some complex examples to show you the possibilities of the
form framework.
.. _concepts-variants-examples-translation:
Translation of form elements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In this example form elements are translated differently depending on
the frontend language.
.. code-block:: yaml
type: Form
prototypeName: standard
identifier: contact-form
label: Kontaktformular
renderingOptions:
submitButtonLabel: Senden
variants:
-
identifier: language-variant-1
condition: 'siteLanguage("locale") == "en_US.UTF-8"'
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") == "en_US.UTF-8"'
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") == "en_US.UTF-8"'
label: Full name
properties:
fluidAdditionalAttributes:
placeholder: Your full name
.. _concepts-variants-examples-validation:
Adding validators dynamically
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In this example a bunch of validators are added to the field
:yaml:`email-address` depending on the value of the form element
:yaml:`checkbox-1`.
.. code-block:: yaml
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: '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 extensive example the form element :yaml:`email-address` has
been enabled explicitly but it is fine to leave this out since this is
the default state. The form element :yaml:`text-3` has been disabled
completely, for example to temporarily remove it from the form. The
field :yaml:`text-1` is hidden in all finishers and on the summary step.
The :yaml:`EmailToSender` finisher takes the fact into account that
finishers can refer to form values. It is only enabled if the form
element :yaml:`checkbox-1` has been activated by the user. Otherwise,
the finisher is skipped.
.. code-block:: yaml
type: Form
prototypeName: standard
identifier: hidden-field-form
label: Hidden field form
finishers:
-
identifier: EmailToReceiver
options:
subject: Yes, I am ready
recipientAddress: tritum@example.org
recipientName: TRITUM
senderAddress: tritum@example.org
senderName: tritum@example.org
-
identifier: EmailToSender
options:
subject: This is a copy of the form data
recipientAddress: '{email-address}'
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` is disabled if the field
:yaml:`checkbox-1` is checked. Furthermore, the form element
:yaml:`checkbox-1` is disabled on the summary step.
.. code-block:: yaml
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: '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 finisher values are set differently depending on the
application context.
.. code-block:: yaml
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 option is removed for a specific locale.
.. code-block:: yaml
type: Form
prototypeName: standard
identifier: option-remove-example
label: Options removed under condition
renderables:
-
type: Page
identifier: page-1
label: Step
renderables:
-
identifier: salulation
type: SingleSelect
label: Salutation
properties:
options:
'': '---'
mr: Mr.
mrs: Mrs.
miss: Miss
defaultValue: ''
variants:
-
identifier: salulation-variant
condition: 'siteLanguage("locale") == "zh_CN.utf-8"'
properties:
options:
miss: __UNSET
.. _concepts-variants-custom-language-providers:
Adding own expression language providers
----------------------------------------
If you need to extend the expression language with custom functions you
can extend it. For more information check the official `docs <https://symfony.com/doc/4.1/components/expression_language/extending.html#using-expression-providers>`_.
Register the expression language provider within the form setup and
provide the functionality you need. Make sure your expression language
provider implements :php:`Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface`.
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
conditionContextDefinition:
expressionLanguageProvider:
MyCustomExpressionLanguageProvider:
implementationClassName: '\Vendor\MyExtension\CustomExpressionLanguageProvider'
.. _concepts-variants-custom-language-variables:
Adding own expression language variables
----------------------------------------
If you need to add custom variables to the expression language you can
extend it. Then the variables are ready to be checked in conditions.
Register the variable provider within the form setup. Make sure your
expression language variable provider implements :php:`TYPO3\CMS\Form\Domain\Condition\ExpressionLanguageVariableProviderInterface`.
.. code-block:: yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
conditionContextDefinition:
expressionLanguageVariableProvider:
MyCustomExpressionLanguageVariableProvider:
implementationClassName: '\Vendor\MyExtension\CustomExpressionLanguageVariableProvider'