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