Feature: #79440 - FormEngine Element Expansion

See forge#79440

Description

A new API in FormEngine has been introduced that allows fine grained additions to single elements and containers without substituting the whole element.

For elements within the TCA config section, three new options have been introduced:

  • fieldInformation An array of single field information. This could be additionally describing text that is rendered between the element label and the element itself. Field information are restricted, only a couple of HTML tags are allowed within the result HTML.

  • fieldControl An array of single field controls. These are icons with JavaScript or links to further functionality of the framework. They are usually displayed next to the element. Each control must return an icon identifier, a title, and an array of a-tag attributes.

  • fieldWizard Additional functionality enriching the element. These are typically shown below the element. Wizards may return any HTML.

For FormEngine containers, the same API has been introduced, but it is currently only implemented within the OuterWrapContainer which renders the record title and delegates the main record rendering to a different container. Adding fieldInformation or fieldWizard here allows embedding additional functionality between the record title an the main record body.

Single elements and containers may register default information, control and wizards. The configuration is merged with any possibly given configuration from TCA.

Example from GroupElement:

class GroupElement extends AbstractFormElement
{
    /**
     * Default field controls for this element.
     *
     * @var array
     */
    protected $defaultFieldControl = [
        'elementBrowser' => [
            'renderType' => 'elementBrowser',
        ],
        'insertClipboard' => [
            'renderType' => 'insertClipboard',
            'after' => [ 'elementBrowser' ],
        ],
        'editPopup' => [
            'renderType' => 'editPopup',
            'disabled' => true,
            'after' => [ 'insertClipboard' ],
        ],
        'addRecord' => [
            'renderType' => 'addRecord',
            'disabled' => true,
            'after' => [ 'editPopup' ],
        ],
        'listModule' => [
            'renderType' => 'listModule',
            'disabled' => true,
            'after' => [ 'addRecord' ],
        ],
    ];

    public function render()
    {
        ...
        $fieldControlResult = $this->renderFieldControl();
        $fieldControlHtml = $legacyFieldControlHtml . $fieldControlResult['html'];
        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false);
    }
}

This element registers five default field controls (icons next to the element), renders them and later adds the HTML at an appropriate place within the HTML of the main element. The defaultFieldControl can be overwritten on TCA level of single fields:

'columns' => [
    'aField' => [
        'label' => 'aField',
        'config' => [
            'fieldControl' => [
                'elementBrowser' => [
                    'disabled' => true,
                ],
                'editPopup' => [
                    'disabled' => false,
                ],
                'aNewControl' => [
                    'renderType' => 'myOwnTypeGroupControl',
                    'before' => [ 'elementBrowser' ],
                ],
            ],
        ],
    ],
],

The above configuration disables the element browser which is enabled by default, it enabled the edit popup control which exists in default configuration but is disabled by default, and it adds a further control called aNewControl with renderType=myOwnTypeGroupControl. The renderType instructs the FormEngine NodeFactory to instantiate the class that in configured for that renderType, identical to other usages of the NodeFactory, this lookup can be manipulated on configuration and code level. In the example above, a new renderType should be registered in ext_localconf.php:

$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1485351217] = [
    'nodeName' => 'myOwnTypeGroupControl',
    'priority' => 30,
    'class' => \My\ExtensionName\Form\FieldControl\MyFancyControl::class,
];

Note the above configuration also uses the DependencyResolver: It is possible to resort single elements by adding before and after to the configuration. In above example, the new control aNewControl would be shown as first control, before elementBrowser.

The first level below fieldControl contains speaking names of single controls, each control must have a renderType defined (either on TCA level or via defaultFieldControl), and they may have a before and after and a disabled setting. Each control also may have a options sub array with further settings given to the specific control.

In TCA, the configuration name for field controls is fieldControl, for wizards it is fieldWizard, and for information it is fieldInformation. All three follow the same structure. It is up to a single element if all three of these are actually called and rendered. For instance, it sometimes does not make sense to have field controls in all elements, so some elements skip that.

For containers, the configuration of fieldInformation, fieldControl and fieldWizard is within the ctrl section of TCA. This is currently only implemented within the OuterWrapContainer for fieldInformation and fieldWizard.

Example:

'ctrl' => [
    ...
    'container' => [
        'outerWrapContainer' => [
            'fieldInformation' => [
                'myHelloWorld' => [
                    'renderType' => 'helloWorld',
                ],
            ],
        ],
    ],
],

The above example would instruct the system to call the class registered for renderType helloWorld within the OuterWrapContainer.

Impact

The new API brings lots of new options to add functionality to single elements without substituting the full element.