## Form Builder
WoltLab Suite Core 5.2 introduces a new, simpler and quicker way of creating forms:
-[form builder](../../php/api/form_builder.md).
+[form builder](../../php/api/form_builder/overview.md).
You can find examples of how to migrate existing forms to form builder [here](form-builder.md).
In the near future, to ensure backwards compatibility within WoltLab packages, we will only use form builder for new forms or for major rewrites of existing forms that would break backwards compatibility anyway.
+++ /dev/null
-# Form Node Dependencies
-
-Form node dependencies allow to make parts of a form dynamically available or unavailable depending on the values of form fields.
-Dependencies are always added to the object whose visibility is determined by certain form fields.
-They are **not** added to the form field’s whose values determine the visibility!
-An example is a text form field that should only be available if a certain option from a single selection form field is selected.
-Form builder’s dependency system supports such scenarios and also automatically making form containers unavailable once all of its children are unavailable.
-
-If a form node has multiple dependencies and one of them is not met, the form node is unavailable.
-A form node not being available due to dependencies has to the following consequences:
-
-- The form field value is not validated. It is, however, read from the request data as all request data needs to be read first so that the dependencies can determine whether they are met or not.
-- No data is collected for the form field and returned by `IFormDocument::getData()`.
-- In the actual form, the form field will be hidden via JavaScript.
-
-
-## `IFormFieldDependency`
-
-The basis of the dependencies is the `IFormFieldDependency` interface that has to be implemented by every dependency class.
-The interface requires the following methods:
-
-- `checkDependency()` checks if the dependency is met, thus if the dependant form field should be considered available.
-- `dependentNode(IFormNode $node)` and `getDependentNode()` can be used to set and get the node whose availability depends on the referenced form field.
- `TFormNode::addDependency()` automatically calls `dependentNode(IFormNode $node)` with itself as the dependent node, thus the dependent node is automatically set by the API.
-- `field(IFormField $field)` and `getField()` can be used to set and get the form field that influences the availability of the dependent node.
-- `fieldId($fieldId)` and `getFieldId()` can be used to set and get the id of the form field that influences the availability of the dependent node.
-- `getHtml()` returns JavaScript code required to ensure the dependency in the form output.
-- `getId()` returns the id of the dependency used to identify multiple dependencies of the same form node.
-- `static create($id)` is the factory method that has to be used to create new dependencies with the given id.
-
-`AbstractFormFieldDependency` provides default implementations for all methods except for `checkDependency()`.
-
-Using `fieldId($fieldId)` instead of `field(IFormField $field)` makes sense when adding the dependency directly when setting up the form:
-
-```php
-$container->appendChildren([
- FooField::create('a'),
-
- BarField::create('b')
- ->addDependency(
- BazDependency::create('a')
- ->fieldId('a')
- )
-]);
-```
-
-Here, without an additional assignment, the first field with id `a` cannot be accessed thus `fieldId($fieldId)` should be used as the id of the relevant field is known.
-When the form is built, all dependencies that only know the id of the relevant field and do not have a reference for the actual object are populated with the actual form field objects.
-
-
-## Default Dependencies
-
-WoltLab Suite Core delivers the following two default dependency classes by default:
-
-- `NonEmptyFormFieldDependency` can be used to ensure that a node is only shown if the value of the referenced form field is not empty (being empty is determined using PHP’s `empty` function).
-- `ValueFormFieldDependency` can be used to ensure that a node is only shown if the value of the referenced form field is from a specified list of of values (see methods `values($values)` and `getValues()`).
- Additionally, via `negate($negate = true)` and `isNegated()`, the locic can also be inverted by requiring the value of the referenced form field not to be from a specified list of values.
-
-
-## JavaScript Implementation
-
-To ensure that dependent node are correctly shown and hidden when changing the value of referenced form fields, every PHP dependency class has a corresponding JavaScript module that checks the dependency in the browser.
-Every JavaScript dependency has to extend `WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract` and implement the `checkDependency()` function, the JavaScript version of `IFormFieldDependency::checkDependency()`.
-
-All of the JavaScript dependency objects automatically register themselves during initialization with the `WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager` which takes care of checking the dependencies at the correct points in time.
-
-Additionally, the dependency manager also ensures that form containers in which all children are hidden due to dependencies are also hidden and, once any child becomes available again, makes the container also available again.
-Every form container has to create a matching form container dependency object from a module based on `WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract`.
-
-
-## Examples
-
-If `$booleanFormField` is an instance of `BooleanFormField` and the text form field `$textFormField` should only be available if “Yes” has been selected, the following condition has to be set up:
-
-```php
-$textFormField->addDependency(
- NonEmptyFormFieldDependency::create('booleanFormField')
- ->field($booleanFormField)
-);
-```
-
-If `$singleSelectionFormField` is an instance of `SingleSelectionFormField` that offers the options `1`, `2`, and `3` and `$textFormField` should only be available if `1` or `3` is selected, the following condition has to be set up:
-
-```php
-$textFormField->addDependency(
- NonEmptyFormFieldDependency::create('singleSelectionFormField')
- ->field($singleSelectionFormField)
- ->values([1, 3])
-);
-```
-
-If, in contrast, `$singleSelectionFormField` has many available options and `7` is the only option for which `$textFormField` should **not** be available, `negate()` should be used:
-
-```php
-$textFormField->addDependency(
- NonEmptyFormFieldDependency::create('singleSelectionFormField')
- ->field($singleSelectionFormField)
- ->values([7])
- ->negate()
-);
-```
+++ /dev/null
-# Form Builder Fields
-
-## Abstract Form Fields
-
-The following form field classes cannot be instantiated directly because they are abstract, but they can/must be used when creating own form field classes.
-
-
-### `AbstractFormField`
-
-`AbstractFormField` is the abstract default implementation of the `IFormField` interface and it is expected that every implementation of `IFormField` implements the interface by extending this class.
-
-
-### `AbstractNumericFormField`
-
-`AbstractNumericFormField` is the abstract implementation of a form field handling a single numeric value.
-The class implements `IImmutableFormField`, `IMaximumFormField`, `IMinimumFormField`, `INullableFormField`, `IPlaceholderFormField` and `ISuffixedFormField`.
-If the property `$integerValues` is `true`, the form field works with integer values, otherwise it works with floating point numbers.
-The methods `step($step = null)` and `getStep()` can be used to set and get the step attribute of the `input` element.
-The default step for form fields with integer values is `1`.
-Otherwise, the default step is `any`.
-
-
-## General Form Fields
-
-The following form fields are general reusable fields without any underlying context.
-
-
-### `BooleanFormField`
-
-`BooleanFormField` is used for boolean (`0` or `1`, `yes` or `no`) values.
-Objects of this class require a label.
-The return value of `getSaveValue()` is the integer representation of the boolean value, i.e. `0` or `1`.
-
-
-### <span class="label label-info">5.3.2+</span> `CheckboxFormField`
-
-`CheckboxFormField` extends `BooleanFormField` and offers a simple HTML checkbox.
-
-
-### `ClassNameFormField`
-
-`ClassNameFormField` is a [text form field](#textformfield) that supports additional settings, specific to entering a PHP class name:
-
-- `classExists($classExists = true)` and `getClassExists()` can be used to ensure that the entered class currently exists in the installation.
- By default, the existance of the entered class is required.
-- `implementedInterface($interface)` and `getImplementedInterface()` can be used to ensure that the entered class implements the specified interface.
- By default, no interface is required.
-- `parentClass($parentClass)` and `getParentClass()` can be used to ensure that the entered class extends the specified class.
- By default, no parent class is required.
-- `instantiable($instantiable = true)` and `isInstantiable()` can be used to ensure that the entered class is instantiable.
- By default, entered classes have to instantiable.
-
-Additionally, the default id of a `ClassNameFormField` object is `className`, the default label is `wcf.form.field.className`, and if either an interface or a parent class is required, a default description is set if no description has already been set (`wcf.form.field.className.description.interface` and `wcf.form.field.className.description.parentClass`, respectively).
-
-
-### `DateFormField`
-
-`DateFormField` is a form field to enter a date (and optionally a time).
-The following methods are specific to this form field class:
-
-- `earliestDate($earliestDate)` and `getEarliestDate()` can be used to get and set the earliest selectable/valid date and `latestDate($latestDate)` and `getLatestDate()` can be used to get and set the latest selectable/valid date.
- The date passed to the setters must have the same format as set via `saveValueFormat()`.
- If a custom format is used, that format has to be set via `saveValueFormat()` before calling any of the setters.
-- `saveValueFormat($saveValueFormat)` and `getSaveValueFormat()` can be used to specify the date format of the value returned by `getSaveValue()`.
- By default, `U` is used as format.
- The [PHP manual](https://secure.php.net/manual/en/function.date.php) provides an overview of supported formats.
-- `supportTime($supportsTime = true)` and `supportsTime()` can be used to toggle whether, in addition to a date, a time can also be specified.
- By default, specifying a time is disabled.
-
-
-### `DescriptionFormField`
-
-`DescriptionFormField` is a [multi-line text form field](#multilinetextformfield) with `description` as the default id and `wcf.global.description` as the default label.
-
-
-### `FloatFormField`
-
-`FloatFormField` is an implementation of [AbstractNumericFormField](#abstractnumericformfield) for floating point numbers.
-
-
-### `IconFormField`
-
-`IconFormField` is a form field to select a FontAwesome icon.
-
-
-### `IntegerFormField`
-
-`IntegerFormField` is an implementation of [AbstractNumericFormField](#abstractnumericformfield) for integers.
-
-
-### `IsDisabledFormField`
-
-`IsDisabledFormField` is a [boolean form field](#booleanformfield) with `isDisabled` as the default id.
-
-
-### `ItemListFormField`
-
-`ItemListFormField` is a form field in which multiple values can be entered and returned in different formats as save value.
-The `saveValueType($saveValueType)` and `getSaveValueType()` methods are specific to this form field class and determine the format of the save value.
-The following save value types are supported:
-
-- `ItemListFormField::SAVE_VALUE_TYPE_ARRAY` adds a custom data processor that writes the form field data directly in the parameters array and not in the data sub-array of the parameters array.
-- `ItemListFormField::SAVE_VALUE_TYPE_CSV` lets the value be returned as a string in which the values are concatenated by commas.
-- `ItemListFormField::SAVE_VALUE_TYPE_NSV` lets the value be returned as a string in which the values are concatenated by `\n`.
-- `ItemListFormField::SAVE_VALUE_TYPE_SSV` lets the value be returned as a string in which the values are concatenated by spaces.
-
-By default, `ItemListFormField::SAVE_VALUE_TYPE_CSV` is used.
-
-If `ItemListFormField::SAVE_VALUE_TYPE_ARRAY` is used as save value type, `ItemListFormField` objects register a [custom form field data processor](form_builder-validation_data.md#customformfielddataprocessor) to add the relevant array into the `$parameters` array directly using the object property as the array key.
-
-
-### `MultilineTextFormField`
-
-`MultilineTextFormField` is a [text form field](#textformfield) that supports multiple rows of text.
-The methods `rows($rows)` and `getRows()` can be used to set and get the number of rows of the `textarea` elements.
-The default number of rows is `10`.
-These methods do **not**, however, restrict the number of text rows that canbe entered.
-
-
-### `MultipleSelectionFormField`
-
-`MultipleSelectionFormField` is a form fields that allows the selection of multiple options out of a predefined list of available options.
-The class implements `IFilterableSelectionFormField`, `IImmutableFormField`, and `INullableFormField`.
-If the field is nullable and no option is selected, `null` is returned as the save value.
-
-
-### `RadioButtonFormField`
-
-`RadioButtonFormField` is a form fields that allows the selection of a single option out of a predefined list of available options using radiobuttons.
-The class implements `IImmutableFormField` and `ISelectionFormField`.
-
-
-### `RatingFormField`
-
-`RatingFormField` is a form field to set a rating for an object.
-The class implements `IImmutableFormField`, `IMaximumFormField`, `IMinimumFormField`, and `INullableFormField`.
-Form fields of this class have `rating` as their default id, `wcf.form.field.rating` as their default label, `1` as their default minimum, and `5` as their default maximum.
-For this field, the minimum and maximum refer to the minimum and maximum rating an object can get.
-When the field is shown, there will be `maximum() - minimum() + 1` icons be shown with additional CSS classes that can be set and gotten via `defaultCssClasses(array $cssClasses)` and `getDefaultCssClasses()`.
-If a rating values is set, the first `getValue()` icons will instead use the classes that can be set and gotten via `activeCssClasses(array $cssClasses)` and `getActiveCssClasses()`.
-By default, the only default class is `fa-star-o` and the active classes are `fa-star` and `orange`.
-
-
-### `ShowOrderFormField`
-
-`ShowOrderFormField` is a [single selection form field](#singleselectionformfield) for which the selected value determines the position at which an object is shown.
-The show order field provides a list of all siblings and the object will be positioned **after** the selected sibling.
-To insert objects at the very beginning, the `options()` automatically method prepends an additional option for that case so that only the existing siblings need to be passed.
-The default id of instances of this class is `showOrder` and their default label is `wcf.form.field.showOrder`.
-
-!!! info "It is important that the relevant object property is always kept updated. Whenever a new object is added or an existing object is edited or delete, the values of the other objects have to be adjusted to ensure consecutive numbering."
-
-
-### `SingleSelectionFormField`
-
-`SingleSelectionFormField` is a form fields that allows the selection of a single option out of a predefined list of available options.
-The class implements `IFilterableSelectionFormField`, `IImmutableFormField`, and `INullableFormField`.
-If the field is nullable and the current form field value is considered `empty` by PHP, `null` is returned as the save value.
-
-
-### `SortOrderFormField`
-
-`SingleSelectionFormField` is a [single selection form field](#singleselectionformfield) with default id `sortOrder`, default label `wcf.global.showOrder` and default options `ASC: wcf.global.sortOrder.ascending` and `DESC: wcf.global.sortOrder.descending`.
-
-
-### `TextFormField`
-
-`TextFormField` is a form field that allows entering a single line of text.
-The class implements `IImmutableFormField`, `II18nFormField`, `IMaximumLengthFormField`, `IMinimumLengthFormField`, `IPatternFormField`, and `IPlaceholderFormField`.
-
-
-### `TitleFormField`
-
-`TitleFormField` is a [text form field](#textformfield) with `title` as the default id and `wcf.global.title` as the default label.
-
-
-### `UrlFormField`
-
-`UrlFormField` is a [text form field](#textformfield) whose values are checked via `Url::is()`.
-
-
-
-## Specific Fields
-
-The following form fields are reusable fields that generally are bound to a certain API or `DatabaseObject` implementation.
-
-
-### `AclFormField`
-
-`AclFormField` is used for setting up acl values for specific objects.
-The class implements `IObjectTypeFormField` and requires an object type of the object type definition `com.woltlab.wcf.acl`.
-Additionally, the class provides the methods `categoryName($categoryName)` and `getCategoryName()` that allow setting a specific name or filter for the acl option categories whose acl options are shown.
-A category name of `null` signals that no category filter is used.
-
-`AclFormField` objects register a [custom form field data processor](form_builder-validation_data.md#customformfielddataprocessor) to add the relevant ACL object type id into the `$parameters` array directly using `{$objectProperty}_aclObjectTypeID` as the array key.
-The relevant database object action method is expected, based on the given ACL object type id, to save the ACL option values appropriately.
-
-
-### `CaptchaFormField`
-
-`CaptchaFormField` is used to add captcha protection to the form.
-
-You must specify a captcha object type (`com.woltlab.wcf.captcha`) using the `objectType()` method.
-
-
-### `ContentLanguageFormField`
-
-`ContentLanguageFormField` is used to select the content language of an object.
-Fields of this class are only available if multilingualism is enabled and if there are content languages.
-The class implements `IImmutableFormField`.
-
-
-### `LabelFormField`
-
-`LabelFormField` is used to select a label from a specific label group.
-The class implements `IObjectTypeFormNode`.
-
-The `labelGroup(ViewableLabelGroup $labelGroup)` and `getLabelGroup()` methods are specific to this form field class and can be used to set and get the label group whose labels can be selected.
-Additionally, there is the static method `createFields($objectType, array $labelGroups, $objectProperty = 'labelIDs)` that can be used to create all relevant label form fields for a given list of label groups.
-In most cases, `LabelFormField::createFields()` should be used.
-
-
-### `OptionFormField`
-
-`OptionFormField` is an [item list form field](#itemlistformfield) to set a list of options.
-The class implements `IPackagesFormField` and only options of the set packages are considered available.
-The default label of instances of this class is `wcf.form.field.option` and their default id is `options`.
-
-
-### `SimpleAclFormField`
-
-`SimpleAclFormField` is used for setting up simple acl values (one `yes`/`no` option per user and user group) for specific objects.
-
-`SimpleAclFormField` objects register a [custom form field data processor](form_builder-validation_data.md#customformfielddataprocessor) to add the relevant simple ACL data array into the `$parameters` array directly using the object property as the array key.
-
-
-### `SingleMediaSelectionFormField`
-
-`SingleMediaSelectionFormField` is used to select a specific media file.
-The class implements `IImmutableFormField`.
-
-The following methods are specific to this form field class:
-
-- `imageOnly($imageOnly = true)` and `isImageOnly()` can be used to set and check if only images may be selected.
-- `getMedia()` returns the media file based on the current field value if a field is set.
-
-
-### `TagFormField`
-
-`TagFormField` is a form field to enter tags.
-Arrays passed to `TagFormField::values()` can contain tag names as strings and `Tag` objects.
-The default label of instances of this class is `wcf.tagging.tags` and their default description is `wcf.tagging.tags.description`.
-
-`TagFormField` objects register a [custom form field data processor](form_builder-validation_data.md#customformfielddataprocessor) to add the array with entered tag names into the `$parameters` array directly using the object property as the array key.
-
-
-### `UploadFormField`
-
-`UploadFormField` is a form field that allows uploading files by the user.
-
-`UploadFormField` objects register a [custom form field data processor](form_builder-validation_data.md#customformfielddataprocessor) to add the array of `wcf\system\file\upload\UploadFile\UploadFile` into the `$parameters` array directly using the object property as the array key. Also it registers the removed files as an array of `wcf\system\file\upload\UploadFile\UploadFile` into the `$parameters` array directly using the object property with the suffix `_removedFiles` as the array key.
-
-The field supports additional settings:
-- `imageOnly($imageOnly = true)` and `isImageOnly()` can be used to ensure that the uploaded files are only images.
-- `allowSvgImage($allowSvgImages = true)` and `svgImageAllowed()` can be used to allow SVG images, if the image only mode is enabled (otherwise, the method will throw an exception). By default, SVG images are not allowed.
-
-#### Provide value from database object
-
-To provide values from a database object, you should implement the method `get{$objectProperty}UploadFileLocations()` to your database object class. This method must return an array of strings with the locations of the files.
-
-#### Process files
-
-To process files in the database object action class, you must [`rename`](https://secure.php.net/manual/en/function.rename.php) the file to the final destination. You get the temporary location, by calling the method `getLocation()` on the given `UploadFile` objects. After that, you call `setProcessed($location)` with `$location` contains the new file location. This method sets the `isProcessed` flag to true and saves the new location. For updating files, it is relevant, whether a given file is already processed or not. For this case, the `UploadFile` object has an method `isProcessed()` which indicates, whether a file is already processed or new uploaded.
-
-
-### `UserFormField`
-
-`UserFormField` is a form field to enter existing users.
-The class implements `IAutoFocusFormField`, `IImmutableFormField`, `IMultipleFormField`, and `INullableFormField`.
-While the user is presented the names of the specified users in the user interface, the field returns the ids of the users as data.
-The relevant `UserProfile` objects can be accessed via the `getUsers()` method.
-
-
-### `UserGroupOptionFormField`
-
-`UserGroupOptionFormField` is an [item list form field](#itemlistformfield) to set a list of user group options/permissions.
-The class implements `IPackagesFormField` and only user group options of the set packages are considered available.
-The default label of instances of this class is `wcf.form.field.userGroupOption` and their default id is `permissions`.
-
-
-### `UsernameFormField`
-
-`UsernameFormField` is used for entering one non-existing username.
-The class implements `IImmutableFormField`, `IMaximumLengthFormField`, `IMinimumLengthFormField`, `INullableFormField`, and `IPlaceholderFormField`.
-As usernames have a system-wide restriction of a minimum length of 3 and a maximum length of 100 characters, these values are also used as the default value for the field’s minimum and maximum length.
-
-
-
-## Wysiwyg form container
-
-To integrate a wysiwyg editor into a form, you have to create a `WysiwygFormContainer` object.
-This container takes care of creating all necessary form nodes listed below for a wysiwyg editor.
-
-!!! warning "When creating the container object, its id has to be the id of the form field that will manage the actual text."
-
-The following methods are specific to this form container class:
-
-- `addSettingsNode(IFormChildNode $settingsNode)` and `addSettingsNodes(array $settingsNodes)` can be used to add nodes to the settings tab container.
-- `attachmentData($objectType, $parentObjectID)` can be used to set the data relevant for attachment support.
- By default, not attachment data is set, thus attachments are not supported.
-- `getAttachmentField()`, `getPollContainer()`, `getSettingsContainer()`, `getSmiliesContainer()`, and `getWysiwygField()` can be used to get the different components of the wysiwyg form container once the form has been built.
-- `enablePreviewButton($enablePreviewButton)` can be used to set whether the preview button for the message is shown or not.
- By default, the preview button is shown.
- This method is only relevant before the form is built.
- Afterwards, the preview button availability can not be changed.
- Only available since WoltLab Suite Core 5.3.
-- `getObjectId()` returns the id of the edited object or `0` if no object is edited.
-- `getPreselect()`, `preselect($preselect)` can be used to set the value of the wysiwyg tab menu's `data-preselect` attribute used to determine which tab is preselected.
- By default, the preselect is `'true'` which is used to pre-select the first tab.
-- `messageObjectType($messageObjectType)` can be used to set the message object type.
-- `pollObjectType($pollObjectType)` can be used to set the poll object type.
- By default, no poll object type is set, thus the poll form field container is not available.
-- `supportMentions($supportMentions)` can be used to set if mentions are supported.
- By default, mentions are not supported.
- This method is only relevant before the form is built.
- Afterwards, mention support can only be changed via the wysiwyg form field.
-- `supportSmilies($supportSmilies)` can be used to set if smilies are supported.
- By default, smilies are supported.
- This method is only relevant before the form is built.
- Afterwards, smiley availability can only be changed via changing the availability of the smilies form container.
-
-### `WysiwygAttachmentFormField`
-
-`WysiwygAttachmentFormField` provides attachment support for a wysiwyg editor via a tab in the menu below the editor.
-This class should not be used directly but only via `WysiwygFormContainer`.
-The methods `attachmentHandler(AttachmentHandler $attachmentHandler)` and `getAttachmentHandler()` can be used to set and get the `AttachmentHandler` object that is used for uploaded attachments.
-
-### `WysiwygPollFormContainer`
-
-`WysiwygPollFormContainer` provides poll support for a wysiwyg editor via a tab in the menu below the editor.
-This class should not be used directly but only via `WysiwygFormContainer`.
-`WysiwygPollFormContainer` contains all form fields that are required to create polls and requires edited objects to implement `IPollContainer`.
-
-The following methods are specific to this form container class:
-
-- `getEndTimeField()` returns the form field to set the end time of the poll once the form has been built.
-- `getIsChangeableField()` returns the form field to set if poll votes can be changed once the form has been built.
-- `getIsPublicField()` returns the form field to set if poll results are public once the form has been built.
-- `getMaxVotesField()` returns the form field to set the maximum number of votes once the form has been built.
-- `getOptionsField()` returns the form field to set the poll options once the form has been built.
-- `getQuestionField()` returns the form field to set the poll question once the form has been built.
-- `getResultsRequireVoteField()` returns the form field to set if viewing the poll results requires voting once the form has been built.
-- `getSortByVotesField()` returns the form field to set if the results are sorted by votes once the form has been built.
-
-### `WysiwygSmileyFormContainer`
-
-`WysiwygSmileyFormContainer` provides smiley support for a wysiwyg editor via a tab in the menu below the editor.
-This class should not be used directly but only via `WysiwygFormContainer`.
-`WysiwygSmileyFormContainer` creates a sub-tab for each smiley category.
-
-#### `WysiwygSmileyFormNode`
-
-`WysiwygSmileyFormNode` is contains the smilies of a specific category.
-This class should not be used directly but only via `WysiwygSmileyFormContainer`.
-
-### Example
-
-The following code creates a WYSIWYG editor component for a `message` object property.
-As smilies are supported by default and an attachment object type is given, the tab menu below the editor has two tabs: “Smilies” and “Attachments”.
-Additionally, mentions and quotes are supported.
-
-```php
-WysiwygFormContainer::create('message')
- ->label('foo.bar.message')
- ->messageObjectType('com.example.foo.bar')
- ->attachmentData('com.example.foo.bar')
- ->supportMentions()
- ->supportQuotes()
-```
-
-
-### `WysiwygFormField`
-
-`WysiwygFormField` is used for wysiwyg editor form fields.
-This class should, in general, not be used directly but only via `WysiwygFormContainer`.
-The class implements `IMaximumLengthFormField`, `IMinimumLengthFormField`, and `IObjectTypeFormNode` and requires an object type of the object type definition `com.woltlab.wcf.message`.
-The following methods are specific to this form field class:
-
-- `autosaveId($autosaveId)` and `getAutosaveId()` can be used enable automatically saving the current editor contents in the browser using the given id.
- An empty string signals that autosaving is disabled.
-- `lastEditTime($lastEditTime)` and `getLastEditTime()` can be used to set the last time the contents have been edited and saved so that the JavaScript can determine if the contents stored in the browser are older or newer.
- `0` signals that no last edit time has been set.
-- `supportAttachments($supportAttachments)` and `supportsAttachments()` can be used to set and check if the form field supports attachments.
-
- !!! warning "It is not sufficient to simply signal attachment support via these methods for attachments to work. These methods are relevant internally to signal the Javascript code that the editor supports attachments. Actual attachment support is provided by `WysiwygAttachmentFormField`."
-- `supportMentions($supportMentions)` and `supportsMentions()` can be used to set and check if the form field supports mentions of other users.
-
-`WysiwygFormField` objects register a [custom form field data processor](form_builder-validation_data.md#customformfielddataprocessor) to add the relevant simple ACL data array into the `$parameters` array directly using the object property as the array key.
-
-
-### `TWysiwygFormNode`
-
-All form nodes that need to know the id of the `WysiwygFormField` field should use `TWysiwygFormNode`.
-This trait provides `getWysiwygId()` and `wysiwygId($wysiwygId)` to get and set the relevant wysiwyg editor id.
-
-
-
-## Single-Use Form Fields
-
-The following form fields are specific for certain forms and hardly reusable in other contexts.
-
-
-### `BBCodeAttributesFormField`
-
-`DevtoolsProjectExcludedPackagesFormField` is a form field for setting the attributes of a BBCode.
-
-
-### `DevtoolsProjectExcludedPackagesFormField`
-
-`DevtoolsProjectExcludedPackagesFormField` is a form field for setting the excluded packages of a devtools project.
-
-
-### `DevtoolsProjectInstructionsFormField`
-
-`DevtoolsProjectExcludedPackagesFormField` is a form field for setting the installation and update instructions of a devtools project.
-
-
-### `DevtoolsProjectOptionalPackagesFormField`
-
-`DevtoolsProjectExcludedPackagesFormField` is a form field for setting the optional packages of a devtools project.
-
-
-### `DevtoolsProjectRequiredPackagesFormField`
-
-`DevtoolsProjectExcludedPackagesFormField` is a form field for setting the required packages of a devtools project.
+++ /dev/null
-# Structure of Form Builder
-
-Forms built with form builder consist of three major structural elements listed from top to bottom:
-
-1. form document,
-1. form container,
-1. form field.
-
-The basis for all three elements are form nodes.
-
-!!! info "The form builder API uses fluent interfaces heavily, meaning that unless a method is a getter, it generally returns the objects itself to support method chaining."
-
-
-## Form Nodes
-
-- `IFormNode` is the base interface that any node of a form has to implement.
-- `IFormChildNode` extends `IFormNode` for such elements of a form that can be a child node to a parent node.
-- `IFormParentNode` extends `IFormNode` for such elements of a form that can be a parent to child nodes.
-- `IFormElement` extends `IFormNode` for such elements of a form that can have a description and a label.
-
-
-### `IFormNode`
-
-`IFormNode` is the base interface that any node of a form has to implement and it requires the following methods:
-
-- `addClass($class)`, `addClasses(array $classes)`, `removeClass($class)`, `getClasses()`, and `hasClass($class)` add, remove, get, and check for CSS classes of the HTML element representing the form node.
- If the form node consists of multiple (nested) HTML elements, the classes are generally added to the top element.
- `static validateClass($class)` is used to check if a given CSS class is valid.
- By default, a form node has no CSS classes.
-- `addDependency(IFormFieldDependency $dependency)`, `removeDependency($dependencyId)`, `getDependencies()`, and `hasDependency($dependencyId)` add, remove, get, and check for dependencies of this form node on other form fields.
- `checkDependencies()` checks if **all** of the node’s dependencies are met and returns a boolean value reflecting the check’s result.
- The [form builder dependency documentation](form_builder-dependencies.md) provides more detailed information about dependencies and how they work.
- By default, a form node has no dependencies.
-- `attribute($name, $value = null)`, `removeAttribute($name)`, `getAttribute($name)`, `getAttributes()`, `hasAttribute($name)` add, remove, get, and check for attributes of the HTML element represting the form node.
- The attributes are added to the same element that the CSS classes are added to.
- `static validateAttribute($name)` is used to check if a given attribute is valid.
- By default, a form node has no attributes.
-- `available($available = true)` and `isAvailable()` can be used to set and check if the node is available.
- The availability functionality can be used to easily toggle form nodes based, for example, on options without having to create a condition to append the relevant.
- This way of checking availability makes it easier to set up forms.
- By default, every form node is available.
-
- The following aspects are important when working with availability:
-
- - Unavailable fields produce no output, their value is not read, they are not validated and they are not checked for save values.
- - Form fields are also able to mark themselves as unavailable, for example, a selection field without any options.
- - Form containers are automatically unavailable if they contain no available children.
-
- Availability sets the static availability for form nodes that does not change during the lifetime of a form.
- In contrast, dependencies represent a dynamic availability for form nodes that depends on the current value of certain form fields.
-- `cleanup()` is called after the whole form is not used anymore to reset other APIs if the form fields depends on them and they expect such a reset.
- This method is not intended to clean up the form field’s value as a new form document object is created to show a clean form.
-- `getDocument()` returns the `IFormDocument` object the node belongs to.
- (As `IFormDocument` extends `IFormNode`, form document objects simply return themselves.)
-- `getHtml()` returns the HTML representation of the node.
- `getHtmlVariables()` return template variables (in addition to the form node itself) to render the node’s HTML representation.
-- `id($id)` and `getId()` set and get the id of the form node.
- Every id has to be unique within a form.
- `getPrefixedId()` returns the prefixed version of the node’s id (see `IFormDocument::getPrefix()` and `IFormDocument::prefix()`).
- `static validateId($id)` is used to check if a given id is valid.
-- `populate()` is called by `IFormDocument::build()` after all form nodes have been added.
- This method should finilize the initialization of the form node after all parent-child relations of the form document have been established.
- This method is needed because during the construction of a form node, it neither knows the form document it will belong to nor does it know its parent.
-- `validate()` checks, after the form is submitted, if the form node is valid.
- A form node with children is valid if all of its child nodes are valid.
- A form field is valid if its value is valid.
-- `static create($id)` is the factory method that has to be used to create new form nodes with the given id.
-
-`TFormNode` provides a default implementation of most of these methods.
-
-
-### `IFormChildNode`
-
-`IFormChildNode` extends `IFormNode` for such elements of a form that can be a child node to a parent node and it requires the `parent(IFormParentNode $parentNode)` and `getParent()` methods used to set and get the node’s parent node.
-`TFormChildNode` provides a default implementation of these two methods and also of `IFormNode::getDocument()`.
-
-
-### `IFormParentNode`
-
-`IFormParentNode` extends `IFormNode` for such elements of a form that can be a parent to child nodes.
-Additionally, the interface also extends `\Countable` and `\RecursiveIterator`.
-The interface requires the following methods:
-
-- `appendChild(IFormChildNode $child)`, `appendChildren(array $children)`, `insertAfter(IFormChildNode $child, $referenceNodeId)`, and `insertBefore(IFormChildNode $child, $referenceNodeId)` are used to insert new children either at the end or at specific positions.
- `validateChild(IFormChildNode $child)` is used to check if a given child node can be added.
- A child node cannot be added if it would cause an id to be used twice.
-- `children()` returns the direct children of a form node.
-- `getIterator()` return a recursive iterator for a form node.
-- `getNodeById($nodeId)` returns the node with the given id by searching for it in the node’s children and recursively in all of their children.
- `contains($nodeId)` can be used to simply check if a node with the given id exists.
-- `hasValidationErrors()` checks if a form node or any of its children has a validation error (see `IFormField::getValidationErrors()`).
-- `readValues()` recursively calls `IFormParentNode::readValues()` and `IFormField::readValue()` on its children.
-
-
-### `IFormElement`
-
-`IFormElement` extends `IFormNode` for such elements of a form that can have a description and a label and it requires the following methods:
-
-- `label($languageItem = null, array $variables = [])` and `getLabel()` can be used to set and get the label of the form element.
- `requiresLabel()` can be checked if the form element requires a label.
- A label-less form element that requires a label will prevent the form from being rendered by throwing an exception.
-- `description($languageItem = null, array $variables = [])` and `getDescription()` can be used to set and get the description of the form element.
-
-
-### `IObjectTypeFormNode`
-
-`IObjectTypeFormField` has to be implemented by form nodes that rely on a object type of a specific object type definition in order to function.
-The implementing class has to implement the methods `objectType($objectType)`, `getObjectType()`, and `getObjectTypeDefinition()`.
-`TObjectTypeFormNode` provides a default implementation of these three methods.
-
-
-### `CustomFormNode`
-
-`CustomFormNode` is a form node whose contents can be set directly via `content($content)`.
-
-!!! warning "This class should generally not be relied on. Instead, `TemplateFormNode` should be used."
-
-
-### `TemplateFormNode`
-
-`TemplateFormNode` is a form node whose contents are read from a template.
-`TemplateFormNode` has the following additional methods:
-
-- `application($application)` and `getApplicaton()` can be used to set and get the abbreviation of the application the shown template belongs to.
- If no template has been set explicitly, `getApplicaton()` returns `wcf`.
-- `templateName($templateName)` and `getTemplateName()` can be used to set and get the name of the template containing the node contents.
- If no template has been set and the node is rendered, an exception will be thrown.
-- `variables(array $variables)` and `getVariables()` can be used to set and get additional variables passed to the template.
-
-
-## Form Document
-
-A form document object represents the form as a whole and has to implement the `IFormDocument` interface.
-WoltLab Suite provides a default implementation with the `FormDocument` class.
-`IFormDocument` should not be implemented directly but instead `FormDocument` should be extended to avoid issues if the `IFormDocument` interface changes in the future.
-
-`IFormDocument` extends `IFormParentNode` and requires the following additional methods:
-
-- `action($action)` and `getAction()` can be used set and get the `action` attribute of the `<form>` HTML element.
-- `addButton(IFormButton $button)` and `getButtons()` can be used add and get form buttons that are shown at the bottom of the form.
- `addDefaultButton($addDefaultButton)` and `hasDefaultButton()` can be used to set and check if the form has the default button which is added by default unless specified otherwise.
- Each implementing class may define its own default button.
- `FormDocument` has a button with id `submitButton`, label `wcf.global.button.submit`, access key `s`, and CSS class `buttonPrimary` as its default button.
-- `ajax($ajax)` and `isAjax()` can be used to set and check if the form document is requested via an AJAX request or processes data via an AJAX request.
- These methods are helpful for form fields that behave differently when providing data via AJAX.
-- `build()` has to be called once after all nodes have been added to this document to trigger `IFormNode::populate()`.
-- `formMode($formMode)` and `getFormMode()` sets the form mode.
- Possible form modes are:
-
- - `IFormDocument::FORM_MODE_CREATE` has to be used when the form is used to create a new object.
- - `IFormDocument::FORM_MODE_UPDATE` has to be used when the form is used to edit an existing object.
-- `getData()` returns the array containing the form data and which is passed as the `$parameters` argument of the constructor of a database object action object.
-- `getDataHandler()` returns the data handler for this document that is used to process the field data into a parameters array for the constructor of a database object action object.
-- `getEnctype()` returns the encoding type of the form.
- If the form contains a `IFileFormField`, `multipart/form-data` is returned, otherwise `null` is returned.
-- `loadValues(array $data, IStorableObject $object)` is used when editing an existing object to set the form field values by calling `IFormField::loadValue()` for all form fields.
- Additionally, the form mode is set to `IFormDocument::FORM_MODE_UPDATE`.
-- `method($method)` and `getMethod()` can be used to set and get the `method` attribute of the `<form>` HTML element.
- By default, the method is `post`.
-- `prefix($prefix)` and `getPrefix()` can be used to set and get a global form prefix that is prepended to form elements’ names and ids to avoid conflicts with other forms.
- By default, the prefix is an empty string.
- If a prefix of `foo` is set, `getPrefix()` returns `foo_` (additional trailing underscore).
-- `requestData(array $requestData)`, `getRequestData($index = null)`, and `hasRequestData($index = null)` can be used to set, get and check for specific request data.
- In most cases, the relevant request data is the `$_POST` array.
- In default AJAX requests handled by database object actions, however, the request data generally is in `AbstractDatabaseObjectAction::$parameters`.
- By default, `$_POST` is the request data.
-
-The last aspect is relevant for `DialogFormDocument` objects.
-`DialogFormDocument` is a specialized class for forms in dialogs that, in contrast to `FormDocument` do not require an `action` to be set.
-Additionally, `DialogFormDocument` provides the `cancelable($cancelable = true)` and `isCancelable()` methods used to determine if the dialog from can be canceled.
-By default, dialog forms are cancelable.
-
-
-## Form Button
-
-A form button object represents a button shown at the end of the form that, for example, submits the form.
-Every form button has to implement the `IFormButton` interface that extends `IFormChildNode` and `IFormElement`.
-`IFormButton` requires four methods to be implemented:
-
-- `accessKey($accessKey)` and `getAccessKey()` can be used to set and get the access key with which the form button can be activated.
- By default, form buttons have no access key set.
-- `submit($submitButton)` and `isSubmit()` can be used to set and check if the form button is a submit button.
- A submit button is an `input[type=submit]` element.
- Otherwise, the button is a `button` element.
-
-
-## Form Container
-
-A form container object represents a container for other form containers or form field directly.
-Every form container has to implement the `IFormContainer` interface which requires the following method:
-
-- `loadValues(array $data, IStorableObject $object)` is called by `IFormDocument::loadValuesFromObject()` to inform the container that object data is loaded.
- This method is *not* intended to generally call `IFormField::loadValues()` on its form field children as these methods are already called by `IFormDocument::loadValuesFromObject()`.
- This method is intended for specialized form containers with more complex logic.
-
-There are multiple default container implementations:
-
-1. `FormContainer` is the default implementation of `IFormContainer`.
-1. `TabMenuFormContainer` represents the container of tab menu, while
-1. `TabFormContainer` represents a tab of a tab menu and
-1. `TabTabMenuFormContainer` represents a tab of a tab menu that itself contains a tab menu.
-1. The children of `RowFormContainer` are shown in a row and should use `col-*` classes.
-1. The children of `RowFormFieldContainer` are also shown in a row but does not show the labels and descriptions of the individual form fields.
- Instead of the individual labels and descriptions, the container's label and description is shown and both span all of fields.
-1. `SuffixFormFieldContainer` can be used for one form field with a second selection form field used as a suffix.
-
-The methods of the interfaces that `FormContainer` is implementing are well documented, but here is a short overview of the most important methods when setting up a form or extending a form with an event listener:
-
-- `appendChild(IFormChildNode $child)`, `appendChildren(array $children)`, and `insertBefore(IFormChildNode $child, $referenceNodeId)` are used to insert new children into the form container.
-- `description($languageItem = null, array $variables = [])` and `label($languageItem = null, array $variables = [])` are used to set the description and the label or title of the form container.
-
-
-## Form Field
-
-A form field object represents a concrete form field that allows entering data.
-Every form field has to implement the `IFormField` interface which extends `IFormChildNode` and `IFormElement`.
-
-`IFormField` requires the following additional methods:
-
-- `addValidationError(IFormFieldValidationError $error)` and `getValidationErrors()` can be used to get and set validation errors of the form field (see [form validation](form_builder-validation_data.md#form-validation)).
-- `addValidator(IFormFieldValidator $validator)`, `getValidators()`, `removeValidator($validatorId)`, and `hasValidator($validatorId)` can be used to get, set, remove, and check for validators for the form field (see [form validation](form_builder-validation_data.md#form-validation)).
-- `getFieldHtml()` returns the field's HTML output without the surrounding `dl` structure.
-- `objectProperty($objectProperty)` and `getObjectProperty()` can be used to get and set the object property that the field represents.
- When setting the object property is set to an empty string, the previously set object property is unset.
- If no object property has been set, the field’s (non-prefixed) id is returned.
-
- The object property allows having different fields (requiring different ids) that represent the same object property which is handy when available options of the field’s value depend on another field.
- Having object property allows to define different fields for each value of the other field and to use form field dependencies to only show the appropriate field.
-- `readValue()` reads the form field value from the request data after the form is submitted.
-- `required($required = true)` and `isRequired()` can be used to determine if the form field has to be filled out.
- By default, form fields do not have to be filled out.
-- `value($value)` and `getSaveValue()` can be used to get and set the value of the form field to be used outside of the context of forms.
- `getValue()`, in contrast, returns the internal representation of the form field’s value.
- In general, the internal representation is only relevant when validating the value in additional validators.
- `loadValue(array $data, IStorableObject $object)` extracts the form field value from the given data array (and additional, non-editable data from the object if the field needs them).
-
-`AbstractFormField` provides default implementations of many of the listed methods above and should be extended instead of implementing `IFormField` directly.
-
-An overview of the form fields provided by default can be found [here](form_builder-form_fields.md).
-
-
-### Form Field Interfaces and Traits
-
-WoltLab Suite Core provides a variety of interfaces and matching traits with default implementations for several common features of form fields:
-
-
-#### `IAutoFocusFormField`
-
-`IAutoFocusFormField` has to be implemented by form fields that can be auto-focused.
-The implementing class has to implement the methods `autoFocus($autoFocus = true)` and `isAutoFocused()`.
-By default, form fields are not auto-focused.
-`TAutoFocusFormField` provides a default implementation of these two methods.
-
-
-#### `IFileFormField`
-
-`IFileFormField` has to be implemented by every form field that uploads files so that the `enctype` attribute of the form document is `multipart/form-data` (see `IFormDocument::getEnctype()`).
-
-
-#### `IFilterableSelectionFormField`
-
-`IFilterableSelectionFormField` extends `ISelectionFormField` by the possibilty for users when selecting the value(s) to filter the list of available options.
-The implementing class has to implement the methods `filterable($filterable = true)` and `isFilterable()`.
-`TFilterableSelectionFormField` provides a default implementation of these two methods.
-
-
-#### `II18nFormField`
-
-`II18nFormField` has to be implemented by form fields if the form field value can be entered separately for all available languages.
-The implementing class has to implement the following methods:
-
-- `i18n($i18n = true)` and `isI18n()` can be used to set whether a specific instance of the class actually supports multilingual input.
-- `i18nRequired($i18nRequired = true)` and `isI18nRequired()` can be used to set whether a specific instance of the class requires separate values for all languages.
-- `languageItemPattern($pattern)` and `getLanguageItemPattern()` can be used to set the pattern/regular expression for the language item used to save the multilingual values.
-- `hasI18nValues()` and `hasPlainValue()` check if the current value is a multilingual or monolingual value.
-
-`TI18nFormField` provides a default implementation of these eight methods and additional default implementations of some of the `IFormField` methods.
-If multilingual input is enabled for a specific form field, classes using `TI18nFormField` register a [custom form field data processor](form_builder-validation_data.md#customformfielddataprocessor) to add the array with multilingual input into the `$parameters` array directly using `{$objectProperty}_i18n` as the array key.
-If multilingual input is enabled but only a monolingual value is entered, the custom form field data processor does nothing and the form field’s value is added by the `DefaultFormDataProcessor` into the `data` sub-array of the `$parameters` array.
-
-!!! info "`TI18nFormField` already provides a default implementation of `IFormField::validate()`."
-
-
-#### `IImmutableFormField`
-
-`IImmutableFormField` has to be implemented by form fields that support being displayed but whose value cannot be changed.
-The implementing class has to implement the methods `immutable($immutable = true)` and `isImmutable()` that can be used to determine if the value of the form field is mutable or immutable.
-By default, form field are mutable.
-
-
-#### `IMaximumFormField`
-
-`IMaximumFormField` has to be implemented by form fields if the entered value must have a maximum value.
-The implementing class has to implement the methods `maximum($maximum = null)` and `getMaximum()`.
-A maximum of `null` signals that no maximum value has been set.
-`TMaximumFormField` provides a default implementation of these two methods.
-
-!!! warning "The implementing class has to validate the entered value against the maximum value manually."
-
-
-#### `IMaximumLengthFormField`
-
-`IMaximumLengthFormField` has to be implemented by form fields if the entered value must have a maximum length.
-The implementing class has to implement the methods `maximumLength($maximumLength = null)`, `getMaximumLength()`, and `validateMaximumLength($text, Language $language = null)`.
-A maximum length of `null` signals that no maximum length has been set.
-`TMaximumLengthFormField` provides a default implementation of these two methods.
-
-!!! warning "The implementing class has to validate the entered value against the maximum value manually by calling `validateMaximumLength()`."
-
-
-#### `IMinimumFormField`
-
-`IMinimumFormField` has to be implemented by form fields if the entered value must have a minimum value.
-The implementing class has to implement the methods `minimum($minimum = null)` and `getMinimum()`.
-A minimum of `null` signals that no minimum value has been set.
-`TMinimumFormField` provides a default implementation of these three methods.
-
-!!! warning "The implementing class has to validate the entered value against the minimum value manually."
-
-
-#### `IMinimumLengthFormField`
-
-`IMinimumLengthFormField` has to be implemented by form fields if the entered value must have a minimum length.
-The implementing class has to implement the methods `minimumLength($minimumLength = null)`, `getMinimumLength()`, and `validateMinimumLength($text, Language $language = null)`.
-A minimum length of `null` signals that no minimum length has been set.
-`TMinimumLengthFormField` provides a default implementation of these three methods.
-
-!!! warning "The implementing class has to validate the entered value against the minimum value manually by calling `validateMinimumLength()`."
-
-
-#### `IMultipleFormField`
-
-`IMinimumLengthFormField` has to be implemented by form fields that support selecting or setting multiple values.
-The implementing class has to implement the following methods:
-
-- `multiple($multiple = true)` and `allowsMultiple()` can be used to set whether a specific instance of the class actually should support multiple values.
- By default, multiple values are not supported.
-- `minimumMultiples($minimum)` and `getMinimumMultiples()` can be used to set the minimum number of values that have to be selected/entered.
- By default, there is no required minimum number of values.
-- `maximumMultiples($minimum)` and `getMaximumMultiples()` can be used to set the maximum number of values that have to be selected/entered.
- By default, there is no maximum number of values.
- `IMultipleFormField::NO_MAXIMUM_MULTIPLES` is returned if no maximum number of values has been set and it can also be used to unset a previously set maximum number of values.
-
-`TMultipleFormField` provides a default implementation of these six methods and classes using `TMultipleFormField` register a [custom form field data processor](form_builder-validation_data.md#customformfielddataprocessor) to add the `HtmlInputProcessor` object with the text into the `$parameters` array directly using `{$objectProperty}_htmlInputProcessor` as the array key.
-
-!!! warning "The implementing class has to validate the values against the minimum and maximum number of values manually."
-
-
-#### `INullableFormField`
-
-`INullableFormField` has to be implemented by form fields that support `null` as their (empty) value.
-The implementing class has to implement the methods `nullable($nullable = true)` and `isNullable()`.
-`TNullableFormField` provides a default implementation of these two methods.
-
-`null` should be returned by `IFormField::getSaveValue()` is the field is considered empty and the form field has been set as nullable.
-
-
-#### `IPackagesFormField`
-
-`IPackagesFormField` has to be implemented by form fields that, in some way, considers packages whose ids may be passed to the field object.
-The implementing class has to implement the methods `packageIDs(array $packageIDs)` and `getPackageIDs()`.
-`TPackagesFormField` provides a default implementation of these two methods.
-
-
-#### `IPatternFormField`
-
-`IPatternFormField` has to be implemented by form fields that support the [`pattern` attribute](https://html.spec.whatwg.org/multipage/input.html#the-pattern-attribute).
-The implementing class has to implement the methods `pattern(?string $pattern): self` and `getPattern(): ?string`, which are used to set and get the pattern, respectively.
-`TPatternFormField` provides a default implementation of these two methods.
-
-
-#### `IPlaceholderFormField`
-
-`IPlaceholderFormField` has to be implemented by form fields that support a placeholder value for empty fields.
-The implementing class has to implement the methods `placeholder($languageItem = null, array $variables = [])` and `getPlaceholder()`.
-`TPlaceholderFormField` provides a default implementation of these two methods.
-
-
-#### `ISelectionFormField`
-
-`ISelectionFormField` has to be implemented by form fields with a predefined set of possible values.
-The implementing class has to implement the getter and setter methods `options($options, $nestedOptions = false, $labelLanguageItems = true)` and `getOptions()` and additionally two methods related to nesting, i.e. whether the selectable options have a hierarchy:
-`supportsNestedOptions()` and `getNestedOptions()`.
-`TSelectionFormField` provides a default implementation of these four methods.
-
-
-#### `ISuffixedFormField`
-
-`ISuffixedFormField` has to be implemented by form fields that support supports displaying a suffix behind the actual input field.
-The implementing class has to implement the methods `suffix($languageItem = null, array $variables = [])` and `getSuffix()`.
-`TSuffixedFormField` provides a default implementation of these two methods.
-
-
-#### `TDefaultIdFormField`
-
-Form fields that have a default id have to use `TDefaultIdFormField` and have to implement the method `getDefaultId()`.
-
-
-## Displaying Forms
-
-The only thing to do in a template to display the **whole** form including all of the necessary JavaScript is to put
-
-```smarty
-{@$form->getHtml()}
-```
-
-into the template file at the relevant position.
+++ /dev/null
-# Form Validation and Form Data
-
-## Form Validation
-
-Every form field class has to implement `IFormField::validate()` according to their internal logic of what constitutes a valid value.
-If a certain constraint for the value is no met, a form field validation error object is added to the form field.
-Form field validation error classes have to implement the interface `IFormFieldValidationError`.
-
-In addition to intrinsic validations like checking the length of the value of a text form field, in many cases, there are additional constraints specific to the form like ensuring that the text is not already used by a different object of the same database object class.
-Such additional validations can be added to (and removed from) the form field via implementations of the `IFormFieldValidator` interface.
-
-
-### `IFormFieldValidationError` / `FormFieldValidationError`
-
-`IFormFieldValidationError` requires the following methods:
-
-- `__construct($type, $languageItem = null, array $information = [])` creates a new validation error object for an error with the given type and message stored in the given language items.
- The information array is used when generating the error message.
-- `getHtml()` returns the HTML element representing the error that is shown to the user.
-- `getMessage()` returns the error message based on the language item and information array given in the constructor.
-- `getInformation()` and `getType()` are getters for the first and third parameter of the constructor.
-
-`FormFieldValidationError` is a default implementation of the interface that shows the error in an `small.innerError` HTML element below the form field.
-
-Form field validation errors are added to form fields via the `IFormField::addValidationError(IFormFieldValidationError $error)` method.
-
-
-### `IFormFieldValidator` / `FormFieldValidator`
-
-`IFormFieldValidator` requires the following methods:
-
-- `__construct($id, callable $validator)` creates a new validator with the given id that passes the validated form field to the given callable that does the actual validation.
- `static validateId($id)` is used to check if the given id is valid.
-- `__invoke(IFormField $field)` is used when the form field is validated to execute the validator.
-- `getId()` returns the id of the validator.
-
-`FormFieldValidator` is a default implementation of the interface.
-
-Form field validators are added to form fields via the `addValidator(IFormFieldValidator $validator)` method.
-
-
-## Form Data
-
-After a form is successfully validated, the data of the form fields (returned by `IFormDocument::getData()`) have to be extracted which is the job of the `IFormDataHandler` object returned by `IFormDocument::getDataHandler()`.
-Form data handlers themselves, however, are only iterating through all `IFormDataProcessor` instances that have been registered with the data handler.
-
-
-### `IFormDataHandler` / `FormDataHandler`
-
-`IFormDataHandler` requires the following methods:
-
-- `addProcessor(IFormDataProcessor $processor)` adds a new data processor to the data handler.
-- `getFormData(IFormDocument $document)` returns the data of the given form by applying all registered data handlers on the form.
-- `getObjectData(IFormDocument $document, IStorableObject $object)` returns the data of the given object which will be used to populate the form field values of the given form.
-
-`FormDataHandler` is the default implementation of this interface and should also be extended instead of implementing the interface directly.
-
-
-### `IFormDataProcessor` / `DefaultFormDataProcessor`
-
-`IFormDataProcessor` requires the following methods:
-
-- `processFormData(IFormDocument $document, array $parameters)` is called by `IFormDataHandler::getFormData()`.
- The method processes the given parameters array and returns the processed version.
-- `processObjectData(IFormDocument $document, array $data, IStorableObject $object)` is called by `IFormDataHandler::getObjectData()`.
- The method processes the given object data array and returns the processed version.
-
-When `FormDocument` creates its `FormDataHandler` instance, it automatically registers an `DefaultFormDataProcessor` object as the first data processor.
-`DefaultFormDataProcessor` puts the save value of all form fields that are available and have a save value into `$parameters['data']` using the form field’s object property as the array key.
-
-!!! warning "`IFormDataProcessor` should not be implemented directly. Instead, `AbstractFormDataProcessor` should be extended."
-
-!!! info "All form data is put into the `data` sub-array so that the whole `$parameters` array can be passed to a database object action object that requires the actual database object data to be in the `data` sub-array."
-
-
-
-### Additional Data Processors
-
-#### `CustomFormDataProcessor`
-
-As mentioned above, the data in the `data` sub-array is intended to directly create or update the database object with.
-As these values are used in the database query directly, these values cannot contain arrays.
-Several form fields, however, store and return their data in form of arrays.
-Thus, this data cannot be returned by `IFormField::getSaveValue()` so that `IFormField::hasSaveValue()` returns `false` and the form field’s data is not collected by the standard `DefaultFormDataProcessor` object.
-
-Instead, such form fields register a `CustomFormDataProcessor` in their `IFormField::populate()` method that inserts the form field value into the `$parameters` array directly.
-This way, the relevant database object action method has access to the data to save it appropriately.
-
-The constructor of `CustomFormDataProcessor` requires an id (that is primarily used in error messages during the validation of the second parameter) and callables for `IFormDataProcessor::processFormData()` and `IFormDataProcessor::processObjectData()` which are passed the same parameters as the `IFormDataProcessor` methods.
-Only one of the callables has to be given, the other one then defaults to simply returning the relevant array unchanged.
-
-
-#### `VoidFormDataProcessor`
-
-Some form fields might only exist to toggle the visibility of other form fields (via dependencies) but the data of form field itself is irrelevant.
-As `DefaultFormDataProcessor` collects the data of all form fields, an additional data processor in the form of a `VoidFormDataProcessor` can be added whose constructor `__construct($property, $isDataProperty = true)` requires the name of the relevant object property/form id and whether the form field value is stored in the `data` sub-array or directory in the `$parameters` array.
-When the data processor is invoked, it checks whether the relevant entry in the `$parameters` array exists and voids it by removing it from the array.
+++ /dev/null
-# Form Builder
-
-!!! info "Form builder is only available since WoltLab Suite Core 5.2."
-
-!!! info "The [migration guide for WoltLab Suite Core 5.2](../../migration/wsc31/form-builder.md) provides some examples of how to migrate existing forms to form builder that can also help in understanding form builder if the old way of creating forms is familiar."
-
-
-## Advantages of Form Builder
-
-WoltLab Suite 5.2 introduces a new powerful way of creating forms: form builder.
-Before taking a closer look at form builder, let us recap how forms are created in previous versions:
-In general, for each form field, there is a corresponding property of the form's PHP class whose value has to be read from the request data, validated, and passed to the database object action to store the value in a database table.
-When editing an object, the property's value has to be set using the value of the corresponding property of the edited object.
-In the form's template, you have to write the `<form>` element with all of its children: the `<section>` elements, the `<dl>` elements, and, of course, the form fields themselves.
-In summary, this way of creating forms creates much duplicate or at least very similar code and makes it very time consuming if the structure of forms in general or a specific type of form field has to be changed.
-
-Form builder, in contrast, relies on PHP objects representing each component of the form, from the form itself down to each form field.
-This approach makes creating forms as easy as creating some PHP objects, populating them with all the relevant data, and one line of code in the template to print the form.
-
-
-## Form Builder Components
-
-Form builder consists of several components that are presented on the following pages:
-
-1. [Structure of form builder](form_builder-structure.md)
-1. [Form validation and form data](form_builder-validation_data.md)
-1. [Form node dependencies](form_builder-dependencies.md)
-
-!!! warning "In general, form builder provides default implementation of interfaces by providing either abstract classes or traits. It is expected that the interfaces are always implemented using these abstract classes and traits! This way, if new methods are added to the interfaces, default implementations can be provided by the abstract classes and traits without causing backwards compatibility problems."
-
-
-## `AbstractFormBuilderForm`
-
-To make using form builder easier, `AbstractFormBuilderForm` extends `AbstractForm` and provides most of the code needed to set up a form (of course without specific fields, those have to be added by the concrete form class), like reading and validating form values and using a database object action to use the form data to create or update a database object.
-
-In addition to the existing methods inherited by `AbstractForm`, `AbstractFormBuilderForm` provides the following methods:
-
-- `buildForm()` builds the form in the following steps:
-
- 1. Call `AbtractFormBuilderForm::createForm()` to create the `IFormDocument` object and add the form fields.
- 2. Call `IFormDocument::build()` to build the form.
- 3. Call `AbtractFormBuilderForm::finalizeForm()` to finalize the form like adding dependencies.
-
- Additionally, between steps 1 and 2 and after step 3, the method provides two events, `createForm` and `buildForm` to allow plugins to register event listeners to execute additional code at the right point in time.
-- `createForm()` creates the `FormDocument` object and sets the form mode.
- Classes extending `AbstractFormBuilderForm` have to override this method (and call `parent::createForm()` as the first line in the overridden method) to add concrete form containers and form fields to the bare form document.
-- `finalizeForm()` is called after the form has been built and the complete form hierarchy has been established.
- This method should be overridden to add dependencies, for example.
-- `setFormAction()` is called at the end of `readData()` and sets the form document’s action based on the controller class name and whether an object is currently edited.
-- If an object is edited, at the beginning of `readData()`, `setFormObjectData()` is called which calls `IFormDocument::loadValuesFromObject()`.
- If values need to be loaded from additional sources, this method should be used for that.
-
-`AbstractFormBuilderForm` also provides the following (public) properties:
-
-- `$form` contains the `IFormDocument` object created in `createForm()`.
-- `$formAction` is either `create` (default) or `edit` and handles which method of the database object is called by default (`create` is called for `$formAction = 'create'` and `update` is called for `$formAction = 'edit'`) and is used to set the value of the `action` template variable.
-- `$formObject` contains the `IStorableObject` if the form is used to edit an existing object.
- For forms used to create objects, `$formObject` is always `null`.
- Edit forms have to manually identify the edited object based on the request data and set the value of `$formObject`.
-- `$objectActionName` can be used to set an alternative action to be executed by the database object action that deviates from the default action determined by the value of `$formAction`.
-- `$objectActionClass` is the name of the database object action class that is used to create or update the database object.
-
-
-## `DialogFormDocument`
-
-Form builder forms can also be used in dialogs.
-For such forms, `DialogFormDocument` should be used which provides the additional methods `cancelable($cancelable = true)` and `isCancelable()` to set and check if the dialog can be canceled.
-If a dialog form can be canceled, a cancel button is added.
-
-If the dialog form is fetched via an AJAX request, `IFormDocument::ajax()` has to be called.
-AJAX forms are registered with `WoltLabSuite/Core/Form/Builder/Manager` which also supports getting all of the data of a form via the `getData(formId)` function.
-The `getData()` function relies on all form fields creating and registering a `WoltLabSuite/Core/Form/Builder/Field/Field` object that provides the data of a specific field.
-
-To make it as easy as possible to work with AJAX forms in dialogs, `WoltLabSuite/Core/Form/Builder/Dialog` (abbreviated as `FormBuilderDialog` from now on) should generally be used instead of `WoltLabSuite/Core/Form/Builder/Manager` directly.
-The constructor of `FormBuilderDialog` expects the following parameters:
-
-- `dialogId`: id of the dialog element
-- `className`: PHP class used to get the form dialog (and save the data if `options.submitActionName` is set)
-- `actionName`: name of the action/method of `className` that returns the dialog; the method is expected to return an array with `formId` containg the id of the returned form and `dialog` containing the rendered form dialog
-- `options`: additional options:
- - `actionParameters` (default: empty): additional parameters sent during AJAX requests
- - `destroyOnClose` (default: `false`): if `true`, whenever the dialog is closed, the form is destroyed so that a new form is fetched if the dialog is opened again
- - `dialog`: additional dialog options used as `options` during dialog setup
- - `onSubmit`: callback when the form is submitted (takes precedence over `submitActionName`)
- - `submitActionName` (default: not set): name of the action/method of `className` called when the form is submitted
-
-The three public functions of `FormBuilderDialog` are:
-
-- `destroy()` destroys the dialog, the form, and all of the form fields.
-- `getData()` returns a Promise that returns the form data.
-- `open()` opens the dialog.
-
-Example:
-
-```javascript
-require(['WoltLabSuite/Core/Form/Builder/Dialog'], function(FormBuilderDialog) {
- var dialog = new FormBuilderDialog(
- 'testDialog',
- 'wcf\\data\\test\\TestAction',
- 'getDialog',
- {
- destroyOnClose: true,
- dialog: {
- title: 'Test Dialog'
- },
- submitActionName: 'saveDialog'
- }
- );
-
- elById('testDialogButton').addEventListener('click', function() {
- dialog.open();
- });
-});
-```
--- /dev/null
+# Form Node Dependencies
+
+Form node dependencies allow to make parts of a form dynamically available or unavailable depending on the values of form fields.
+Dependencies are always added to the object whose visibility is determined by certain form fields.
+They are **not** added to the form field’s whose values determine the visibility!
+An example is a text form field that should only be available if a certain option from a single selection form field is selected.
+Form builder’s dependency system supports such scenarios and also automatically making form containers unavailable once all of its children are unavailable.
+
+If a form node has multiple dependencies and one of them is not met, the form node is unavailable.
+A form node not being available due to dependencies has to the following consequences:
+
+- The form field value is not validated. It is, however, read from the request data as all request data needs to be read first so that the dependencies can determine whether they are met or not.
+- No data is collected for the form field and returned by `IFormDocument::getData()`.
+- In the actual form, the form field will be hidden via JavaScript.
+
+
+## `IFormFieldDependency`
+
+The basis of the dependencies is the `IFormFieldDependency` interface that has to be implemented by every dependency class.
+The interface requires the following methods:
+
+- `checkDependency()` checks if the dependency is met, thus if the dependant form field should be considered available.
+- `dependentNode(IFormNode $node)` and `getDependentNode()` can be used to set and get the node whose availability depends on the referenced form field.
+ `TFormNode::addDependency()` automatically calls `dependentNode(IFormNode $node)` with itself as the dependent node, thus the dependent node is automatically set by the API.
+- `field(IFormField $field)` and `getField()` can be used to set and get the form field that influences the availability of the dependent node.
+- `fieldId($fieldId)` and `getFieldId()` can be used to set and get the id of the form field that influences the availability of the dependent node.
+- `getHtml()` returns JavaScript code required to ensure the dependency in the form output.
+- `getId()` returns the id of the dependency used to identify multiple dependencies of the same form node.
+- `static create($id)` is the factory method that has to be used to create new dependencies with the given id.
+
+`AbstractFormFieldDependency` provides default implementations for all methods except for `checkDependency()`.
+
+Using `fieldId($fieldId)` instead of `field(IFormField $field)` makes sense when adding the dependency directly when setting up the form:
+
+```php
+$container->appendChildren([
+ FooField::create('a'),
+
+ BarField::create('b')
+ ->addDependency(
+ BazDependency::create('a')
+ ->fieldId('a')
+ )
+]);
+```
+
+Here, without an additional assignment, the first field with id `a` cannot be accessed thus `fieldId($fieldId)` should be used as the id of the relevant field is known.
+When the form is built, all dependencies that only know the id of the relevant field and do not have a reference for the actual object are populated with the actual form field objects.
+
+
+## Default Dependencies
+
+WoltLab Suite Core delivers the following two default dependency classes by default:
+
+- `NonEmptyFormFieldDependency` can be used to ensure that a node is only shown if the value of the referenced form field is not empty (being empty is determined using PHP’s `empty` function).
+- `ValueFormFieldDependency` can be used to ensure that a node is only shown if the value of the referenced form field is from a specified list of of values (see methods `values($values)` and `getValues()`).
+ Additionally, via `negate($negate = true)` and `isNegated()`, the locic can also be inverted by requiring the value of the referenced form field not to be from a specified list of values.
+
+
+## JavaScript Implementation
+
+To ensure that dependent node are correctly shown and hidden when changing the value of referenced form fields, every PHP dependency class has a corresponding JavaScript module that checks the dependency in the browser.
+Every JavaScript dependency has to extend `WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract` and implement the `checkDependency()` function, the JavaScript version of `IFormFieldDependency::checkDependency()`.
+
+All of the JavaScript dependency objects automatically register themselves during initialization with the `WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager` which takes care of checking the dependencies at the correct points in time.
+
+Additionally, the dependency manager also ensures that form containers in which all children are hidden due to dependencies are also hidden and, once any child becomes available again, makes the container also available again.
+Every form container has to create a matching form container dependency object from a module based on `WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract`.
+
+
+## Examples
+
+If `$booleanFormField` is an instance of `BooleanFormField` and the text form field `$textFormField` should only be available if “Yes” has been selected, the following condition has to be set up:
+
+```php
+$textFormField->addDependency(
+ NonEmptyFormFieldDependency::create('booleanFormField')
+ ->field($booleanFormField)
+);
+```
+
+If `$singleSelectionFormField` is an instance of `SingleSelectionFormField` that offers the options `1`, `2`, and `3` and `$textFormField` should only be available if `1` or `3` is selected, the following condition has to be set up:
+
+```php
+$textFormField->addDependency(
+ NonEmptyFormFieldDependency::create('singleSelectionFormField')
+ ->field($singleSelectionFormField)
+ ->values([1, 3])
+);
+```
+
+If, in contrast, `$singleSelectionFormField` has many available options and `7` is the only option for which `$textFormField` should **not** be available, `negate()` should be used:
+
+```php
+$textFormField->addDependency(
+ NonEmptyFormFieldDependency::create('singleSelectionFormField')
+ ->field($singleSelectionFormField)
+ ->values([7])
+ ->negate()
+);
+```
--- /dev/null
+# Form Builder Fields
+
+## Abstract Form Fields
+
+The following form field classes cannot be instantiated directly because they are abstract, but they can/must be used when creating own form field classes.
+
+
+### `AbstractFormField`
+
+`AbstractFormField` is the abstract default implementation of the `IFormField` interface and it is expected that every implementation of `IFormField` implements the interface by extending this class.
+
+
+### `AbstractNumericFormField`
+
+`AbstractNumericFormField` is the abstract implementation of a form field handling a single numeric value.
+The class implements `IImmutableFormField`, `IMaximumFormField`, `IMinimumFormField`, `INullableFormField`, `IPlaceholderFormField` and `ISuffixedFormField`.
+If the property `$integerValues` is `true`, the form field works with integer values, otherwise it works with floating point numbers.
+The methods `step($step = null)` and `getStep()` can be used to set and get the step attribute of the `input` element.
+The default step for form fields with integer values is `1`.
+Otherwise, the default step is `any`.
+
+
+## General Form Fields
+
+The following form fields are general reusable fields without any underlying context.
+
+
+### `BooleanFormField`
+
+`BooleanFormField` is used for boolean (`0` or `1`, `yes` or `no`) values.
+Objects of this class require a label.
+The return value of `getSaveValue()` is the integer representation of the boolean value, i.e. `0` or `1`.
+
+
+### <span class="label label-info">5.3.2+</span> `CheckboxFormField`
+
+`CheckboxFormField` extends `BooleanFormField` and offers a simple HTML checkbox.
+
+
+### `ClassNameFormField`
+
+`ClassNameFormField` is a [text form field](#textformfield) that supports additional settings, specific to entering a PHP class name:
+
+- `classExists($classExists = true)` and `getClassExists()` can be used to ensure that the entered class currently exists in the installation.
+ By default, the existance of the entered class is required.
+- `implementedInterface($interface)` and `getImplementedInterface()` can be used to ensure that the entered class implements the specified interface.
+ By default, no interface is required.
+- `parentClass($parentClass)` and `getParentClass()` can be used to ensure that the entered class extends the specified class.
+ By default, no parent class is required.
+- `instantiable($instantiable = true)` and `isInstantiable()` can be used to ensure that the entered class is instantiable.
+ By default, entered classes have to instantiable.
+
+Additionally, the default id of a `ClassNameFormField` object is `className`, the default label is `wcf.form.field.className`, and if either an interface or a parent class is required, a default description is set if no description has already been set (`wcf.form.field.className.description.interface` and `wcf.form.field.className.description.parentClass`, respectively).
+
+
+### `DateFormField`
+
+`DateFormField` is a form field to enter a date (and optionally a time).
+The following methods are specific to this form field class:
+
+- `earliestDate($earliestDate)` and `getEarliestDate()` can be used to get and set the earliest selectable/valid date and `latestDate($latestDate)` and `getLatestDate()` can be used to get and set the latest selectable/valid date.
+ The date passed to the setters must have the same format as set via `saveValueFormat()`.
+ If a custom format is used, that format has to be set via `saveValueFormat()` before calling any of the setters.
+- `saveValueFormat($saveValueFormat)` and `getSaveValueFormat()` can be used to specify the date format of the value returned by `getSaveValue()`.
+ By default, `U` is used as format.
+ The [PHP manual](https://secure.php.net/manual/en/function.date.php) provides an overview of supported formats.
+- `supportTime($supportsTime = true)` and `supportsTime()` can be used to toggle whether, in addition to a date, a time can also be specified.
+ By default, specifying a time is disabled.
+
+
+### `DescriptionFormField`
+
+`DescriptionFormField` is a [multi-line text form field](#multilinetextformfield) with `description` as the default id and `wcf.global.description` as the default label.
+
+
+### `FloatFormField`
+
+`FloatFormField` is an implementation of [AbstractNumericFormField](#abstractnumericformfield) for floating point numbers.
+
+
+### `IconFormField`
+
+`IconFormField` is a form field to select a FontAwesome icon.
+
+
+### `IntegerFormField`
+
+`IntegerFormField` is an implementation of [AbstractNumericFormField](#abstractnumericformfield) for integers.
+
+
+### `IsDisabledFormField`
+
+`IsDisabledFormField` is a [boolean form field](#booleanformfield) with `isDisabled` as the default id.
+
+
+### `ItemListFormField`
+
+`ItemListFormField` is a form field in which multiple values can be entered and returned in different formats as save value.
+The `saveValueType($saveValueType)` and `getSaveValueType()` methods are specific to this form field class and determine the format of the save value.
+The following save value types are supported:
+
+- `ItemListFormField::SAVE_VALUE_TYPE_ARRAY` adds a custom data processor that writes the form field data directly in the parameters array and not in the data sub-array of the parameters array.
+- `ItemListFormField::SAVE_VALUE_TYPE_CSV` lets the value be returned as a string in which the values are concatenated by commas.
+- `ItemListFormField::SAVE_VALUE_TYPE_NSV` lets the value be returned as a string in which the values are concatenated by `\n`.
+- `ItemListFormField::SAVE_VALUE_TYPE_SSV` lets the value be returned as a string in which the values are concatenated by spaces.
+
+By default, `ItemListFormField::SAVE_VALUE_TYPE_CSV` is used.
+
+If `ItemListFormField::SAVE_VALUE_TYPE_ARRAY` is used as save value type, `ItemListFormField` objects register a [custom form field data processor](validation_data.md#customformfielddataprocessor) to add the relevant array into the `$parameters` array directly using the object property as the array key.
+
+
+### `MultilineTextFormField`
+
+`MultilineTextFormField` is a [text form field](#textformfield) that supports multiple rows of text.
+The methods `rows($rows)` and `getRows()` can be used to set and get the number of rows of the `textarea` elements.
+The default number of rows is `10`.
+These methods do **not**, however, restrict the number of text rows that canbe entered.
+
+
+### `MultipleSelectionFormField`
+
+`MultipleSelectionFormField` is a form fields that allows the selection of multiple options out of a predefined list of available options.
+The class implements `IFilterableSelectionFormField`, `IImmutableFormField`, and `INullableFormField`.
+If the field is nullable and no option is selected, `null` is returned as the save value.
+
+
+### `RadioButtonFormField`
+
+`RadioButtonFormField` is a form fields that allows the selection of a single option out of a predefined list of available options using radiobuttons.
+The class implements `IImmutableFormField` and `ISelectionFormField`.
+
+
+### `RatingFormField`
+
+`RatingFormField` is a form field to set a rating for an object.
+The class implements `IImmutableFormField`, `IMaximumFormField`, `IMinimumFormField`, and `INullableFormField`.
+Form fields of this class have `rating` as their default id, `wcf.form.field.rating` as their default label, `1` as their default minimum, and `5` as their default maximum.
+For this field, the minimum and maximum refer to the minimum and maximum rating an object can get.
+When the field is shown, there will be `maximum() - minimum() + 1` icons be shown with additional CSS classes that can be set and gotten via `defaultCssClasses(array $cssClasses)` and `getDefaultCssClasses()`.
+If a rating values is set, the first `getValue()` icons will instead use the classes that can be set and gotten via `activeCssClasses(array $cssClasses)` and `getActiveCssClasses()`.
+By default, the only default class is `fa-star-o` and the active classes are `fa-star` and `orange`.
+
+
+### `ShowOrderFormField`
+
+`ShowOrderFormField` is a [single selection form field](#singleselectionformfield) for which the selected value determines the position at which an object is shown.
+The show order field provides a list of all siblings and the object will be positioned **after** the selected sibling.
+To insert objects at the very beginning, the `options()` automatically method prepends an additional option for that case so that only the existing siblings need to be passed.
+The default id of instances of this class is `showOrder` and their default label is `wcf.form.field.showOrder`.
+
+!!! info "It is important that the relevant object property is always kept updated. Whenever a new object is added or an existing object is edited or delete, the values of the other objects have to be adjusted to ensure consecutive numbering."
+
+
+### `SingleSelectionFormField`
+
+`SingleSelectionFormField` is a form fields that allows the selection of a single option out of a predefined list of available options.
+The class implements `IFilterableSelectionFormField`, `IImmutableFormField`, and `INullableFormField`.
+If the field is nullable and the current form field value is considered `empty` by PHP, `null` is returned as the save value.
+
+
+### `SortOrderFormField`
+
+`SingleSelectionFormField` is a [single selection form field](#singleselectionformfield) with default id `sortOrder`, default label `wcf.global.showOrder` and default options `ASC: wcf.global.sortOrder.ascending` and `DESC: wcf.global.sortOrder.descending`.
+
+
+### `TextFormField`
+
+`TextFormField` is a form field that allows entering a single line of text.
+The class implements `IImmutableFormField`, `II18nFormField`, `IMaximumLengthFormField`, `IMinimumLengthFormField`, `IPatternFormField`, and `IPlaceholderFormField`.
+
+
+### `TitleFormField`
+
+`TitleFormField` is a [text form field](#textformfield) with `title` as the default id and `wcf.global.title` as the default label.
+
+
+### `UrlFormField`
+
+`UrlFormField` is a [text form field](#textformfield) whose values are checked via `Url::is()`.
+
+
+
+## Specific Fields
+
+The following form fields are reusable fields that generally are bound to a certain API or `DatabaseObject` implementation.
+
+
+### `AclFormField`
+
+`AclFormField` is used for setting up acl values for specific objects.
+The class implements `IObjectTypeFormField` and requires an object type of the object type definition `com.woltlab.wcf.acl`.
+Additionally, the class provides the methods `categoryName($categoryName)` and `getCategoryName()` that allow setting a specific name or filter for the acl option categories whose acl options are shown.
+A category name of `null` signals that no category filter is used.
+
+`AclFormField` objects register a [custom form field data processor](validation_data.md#customformfielddataprocessor) to add the relevant ACL object type id into the `$parameters` array directly using `{$objectProperty}_aclObjectTypeID` as the array key.
+The relevant database object action method is expected, based on the given ACL object type id, to save the ACL option values appropriately.
+
+
+### `CaptchaFormField`
+
+`CaptchaFormField` is used to add captcha protection to the form.
+
+You must specify a captcha object type (`com.woltlab.wcf.captcha`) using the `objectType()` method.
+
+
+### `ContentLanguageFormField`
+
+`ContentLanguageFormField` is used to select the content language of an object.
+Fields of this class are only available if multilingualism is enabled and if there are content languages.
+The class implements `IImmutableFormField`.
+
+
+### `LabelFormField`
+
+`LabelFormField` is used to select a label from a specific label group.
+The class implements `IObjectTypeFormNode`.
+
+The `labelGroup(ViewableLabelGroup $labelGroup)` and `getLabelGroup()` methods are specific to this form field class and can be used to set and get the label group whose labels can be selected.
+Additionally, there is the static method `createFields($objectType, array $labelGroups, $objectProperty = 'labelIDs)` that can be used to create all relevant label form fields for a given list of label groups.
+In most cases, `LabelFormField::createFields()` should be used.
+
+
+### `OptionFormField`
+
+`OptionFormField` is an [item list form field](#itemlistformfield) to set a list of options.
+The class implements `IPackagesFormField` and only options of the set packages are considered available.
+The default label of instances of this class is `wcf.form.field.option` and their default id is `options`.
+
+
+### `SimpleAclFormField`
+
+`SimpleAclFormField` is used for setting up simple acl values (one `yes`/`no` option per user and user group) for specific objects.
+
+`SimpleAclFormField` objects register a [custom form field data processor](validation_data.md#customformfielddataprocessor) to add the relevant simple ACL data array into the `$parameters` array directly using the object property as the array key.
+
+
+### `SingleMediaSelectionFormField`
+
+`SingleMediaSelectionFormField` is used to select a specific media file.
+The class implements `IImmutableFormField`.
+
+The following methods are specific to this form field class:
+
+- `imageOnly($imageOnly = true)` and `isImageOnly()` can be used to set and check if only images may be selected.
+- `getMedia()` returns the media file based on the current field value if a field is set.
+
+
+### `TagFormField`
+
+`TagFormField` is a form field to enter tags.
+Arrays passed to `TagFormField::values()` can contain tag names as strings and `Tag` objects.
+The default label of instances of this class is `wcf.tagging.tags` and their default description is `wcf.tagging.tags.description`.
+
+`TagFormField` objects register a [custom form field data processor](validation_data.md#customformfielddataprocessor) to add the array with entered tag names into the `$parameters` array directly using the object property as the array key.
+
+
+### `UploadFormField`
+
+`UploadFormField` is a form field that allows uploading files by the user.
+
+`UploadFormField` objects register a [custom form field data processor](validation_data.md#customformfielddataprocessor) to add the array of `wcf\system\file\upload\UploadFile\UploadFile` into the `$parameters` array directly using the object property as the array key. Also it registers the removed files as an array of `wcf\system\file\upload\UploadFile\UploadFile` into the `$parameters` array directly using the object property with the suffix `_removedFiles` as the array key.
+
+The field supports additional settings:
+- `imageOnly($imageOnly = true)` and `isImageOnly()` can be used to ensure that the uploaded files are only images.
+- `allowSvgImage($allowSvgImages = true)` and `svgImageAllowed()` can be used to allow SVG images, if the image only mode is enabled (otherwise, the method will throw an exception). By default, SVG images are not allowed.
+
+#### Provide value from database object
+
+To provide values from a database object, you should implement the method `get{$objectProperty}UploadFileLocations()` to your database object class. This method must return an array of strings with the locations of the files.
+
+#### Process files
+
+To process files in the database object action class, you must [`rename`](https://secure.php.net/manual/en/function.rename.php) the file to the final destination. You get the temporary location, by calling the method `getLocation()` on the given `UploadFile` objects. After that, you call `setProcessed($location)` with `$location` contains the new file location. This method sets the `isProcessed` flag to true and saves the new location. For updating files, it is relevant, whether a given file is already processed or not. For this case, the `UploadFile` object has an method `isProcessed()` which indicates, whether a file is already processed or new uploaded.
+
+
+### `UserFormField`
+
+`UserFormField` is a form field to enter existing users.
+The class implements `IAutoFocusFormField`, `IImmutableFormField`, `IMultipleFormField`, and `INullableFormField`.
+While the user is presented the names of the specified users in the user interface, the field returns the ids of the users as data.
+The relevant `UserProfile` objects can be accessed via the `getUsers()` method.
+
+
+### `UserGroupOptionFormField`
+
+`UserGroupOptionFormField` is an [item list form field](#itemlistformfield) to set a list of user group options/permissions.
+The class implements `IPackagesFormField` and only user group options of the set packages are considered available.
+The default label of instances of this class is `wcf.form.field.userGroupOption` and their default id is `permissions`.
+
+
+### `UsernameFormField`
+
+`UsernameFormField` is used for entering one non-existing username.
+The class implements `IImmutableFormField`, `IMaximumLengthFormField`, `IMinimumLengthFormField`, `INullableFormField`, and `IPlaceholderFormField`.
+As usernames have a system-wide restriction of a minimum length of 3 and a maximum length of 100 characters, these values are also used as the default value for the field’s minimum and maximum length.
+
+
+
+## Wysiwyg form container
+
+To integrate a wysiwyg editor into a form, you have to create a `WysiwygFormContainer` object.
+This container takes care of creating all necessary form nodes listed below for a wysiwyg editor.
+
+!!! warning "When creating the container object, its id has to be the id of the form field that will manage the actual text."
+
+The following methods are specific to this form container class:
+
+- `addSettingsNode(IFormChildNode $settingsNode)` and `addSettingsNodes(array $settingsNodes)` can be used to add nodes to the settings tab container.
+- `attachmentData($objectType, $parentObjectID)` can be used to set the data relevant for attachment support.
+ By default, not attachment data is set, thus attachments are not supported.
+- `getAttachmentField()`, `getPollContainer()`, `getSettingsContainer()`, `getSmiliesContainer()`, and `getWysiwygField()` can be used to get the different components of the wysiwyg form container once the form has been built.
+- `enablePreviewButton($enablePreviewButton)` can be used to set whether the preview button for the message is shown or not.
+ By default, the preview button is shown.
+ This method is only relevant before the form is built.
+ Afterwards, the preview button availability can not be changed.
+ Only available since WoltLab Suite Core 5.3.
+- `getObjectId()` returns the id of the edited object or `0` if no object is edited.
+- `getPreselect()`, `preselect($preselect)` can be used to set the value of the wysiwyg tab menu's `data-preselect` attribute used to determine which tab is preselected.
+ By default, the preselect is `'true'` which is used to pre-select the first tab.
+- `messageObjectType($messageObjectType)` can be used to set the message object type.
+- `pollObjectType($pollObjectType)` can be used to set the poll object type.
+ By default, no poll object type is set, thus the poll form field container is not available.
+- `supportMentions($supportMentions)` can be used to set if mentions are supported.
+ By default, mentions are not supported.
+ This method is only relevant before the form is built.
+ Afterwards, mention support can only be changed via the wysiwyg form field.
+- `supportSmilies($supportSmilies)` can be used to set if smilies are supported.
+ By default, smilies are supported.
+ This method is only relevant before the form is built.
+ Afterwards, smiley availability can only be changed via changing the availability of the smilies form container.
+
+### `WysiwygAttachmentFormField`
+
+`WysiwygAttachmentFormField` provides attachment support for a wysiwyg editor via a tab in the menu below the editor.
+This class should not be used directly but only via `WysiwygFormContainer`.
+The methods `attachmentHandler(AttachmentHandler $attachmentHandler)` and `getAttachmentHandler()` can be used to set and get the `AttachmentHandler` object that is used for uploaded attachments.
+
+### `WysiwygPollFormContainer`
+
+`WysiwygPollFormContainer` provides poll support for a wysiwyg editor via a tab in the menu below the editor.
+This class should not be used directly but only via `WysiwygFormContainer`.
+`WysiwygPollFormContainer` contains all form fields that are required to create polls and requires edited objects to implement `IPollContainer`.
+
+The following methods are specific to this form container class:
+
+- `getEndTimeField()` returns the form field to set the end time of the poll once the form has been built.
+- `getIsChangeableField()` returns the form field to set if poll votes can be changed once the form has been built.
+- `getIsPublicField()` returns the form field to set if poll results are public once the form has been built.
+- `getMaxVotesField()` returns the form field to set the maximum number of votes once the form has been built.
+- `getOptionsField()` returns the form field to set the poll options once the form has been built.
+- `getQuestionField()` returns the form field to set the poll question once the form has been built.
+- `getResultsRequireVoteField()` returns the form field to set if viewing the poll results requires voting once the form has been built.
+- `getSortByVotesField()` returns the form field to set if the results are sorted by votes once the form has been built.
+
+### `WysiwygSmileyFormContainer`
+
+`WysiwygSmileyFormContainer` provides smiley support for a wysiwyg editor via a tab in the menu below the editor.
+This class should not be used directly but only via `WysiwygFormContainer`.
+`WysiwygSmileyFormContainer` creates a sub-tab for each smiley category.
+
+#### `WysiwygSmileyFormNode`
+
+`WysiwygSmileyFormNode` is contains the smilies of a specific category.
+This class should not be used directly but only via `WysiwygSmileyFormContainer`.
+
+### Example
+
+The following code creates a WYSIWYG editor component for a `message` object property.
+As smilies are supported by default and an attachment object type is given, the tab menu below the editor has two tabs: “Smilies” and “Attachments”.
+Additionally, mentions and quotes are supported.
+
+```php
+WysiwygFormContainer::create('message')
+ ->label('foo.bar.message')
+ ->messageObjectType('com.example.foo.bar')
+ ->attachmentData('com.example.foo.bar')
+ ->supportMentions()
+ ->supportQuotes()
+```
+
+
+### `WysiwygFormField`
+
+`WysiwygFormField` is used for wysiwyg editor form fields.
+This class should, in general, not be used directly but only via `WysiwygFormContainer`.
+The class implements `IMaximumLengthFormField`, `IMinimumLengthFormField`, and `IObjectTypeFormNode` and requires an object type of the object type definition `com.woltlab.wcf.message`.
+The following methods are specific to this form field class:
+
+- `autosaveId($autosaveId)` and `getAutosaveId()` can be used enable automatically saving the current editor contents in the browser using the given id.
+ An empty string signals that autosaving is disabled.
+- `lastEditTime($lastEditTime)` and `getLastEditTime()` can be used to set the last time the contents have been edited and saved so that the JavaScript can determine if the contents stored in the browser are older or newer.
+ `0` signals that no last edit time has been set.
+- `supportAttachments($supportAttachments)` and `supportsAttachments()` can be used to set and check if the form field supports attachments.
+
+ !!! warning "It is not sufficient to simply signal attachment support via these methods for attachments to work. These methods are relevant internally to signal the Javascript code that the editor supports attachments. Actual attachment support is provided by `WysiwygAttachmentFormField`."
+- `supportMentions($supportMentions)` and `supportsMentions()` can be used to set and check if the form field supports mentions of other users.
+
+`WysiwygFormField` objects register a [custom form field data processor](validation_data.md#customformfielddataprocessor) to add the relevant simple ACL data array into the `$parameters` array directly using the object property as the array key.
+
+
+### `TWysiwygFormNode`
+
+All form nodes that need to know the id of the `WysiwygFormField` field should use `TWysiwygFormNode`.
+This trait provides `getWysiwygId()` and `wysiwygId($wysiwygId)` to get and set the relevant wysiwyg editor id.
+
+
+
+## Single-Use Form Fields
+
+The following form fields are specific for certain forms and hardly reusable in other contexts.
+
+
+### `BBCodeAttributesFormField`
+
+`DevtoolsProjectExcludedPackagesFormField` is a form field for setting the attributes of a BBCode.
+
+
+### `DevtoolsProjectExcludedPackagesFormField`
+
+`DevtoolsProjectExcludedPackagesFormField` is a form field for setting the excluded packages of a devtools project.
+
+
+### `DevtoolsProjectInstructionsFormField`
+
+`DevtoolsProjectExcludedPackagesFormField` is a form field for setting the installation and update instructions of a devtools project.
+
+
+### `DevtoolsProjectOptionalPackagesFormField`
+
+`DevtoolsProjectExcludedPackagesFormField` is a form field for setting the optional packages of a devtools project.
+
+
+### `DevtoolsProjectRequiredPackagesFormField`
+
+`DevtoolsProjectExcludedPackagesFormField` is a form field for setting the required packages of a devtools project.
--- /dev/null
+# Form Builder
+
+!!! info "Form builder is only available since WoltLab Suite Core 5.2."
+
+!!! info "The [migration guide for WoltLab Suite Core 5.2](../../../migration/wsc31/form-builder.md) provides some examples of how to migrate existing forms to form builder that can also help in understanding form builder if the old way of creating forms is familiar."
+
+
+## Advantages of Form Builder
+
+WoltLab Suite 5.2 introduces a new powerful way of creating forms: form builder.
+Before taking a closer look at form builder, let us recap how forms are created in previous versions:
+In general, for each form field, there is a corresponding property of the form's PHP class whose value has to be read from the request data, validated, and passed to the database object action to store the value in a database table.
+When editing an object, the property's value has to be set using the value of the corresponding property of the edited object.
+In the form's template, you have to write the `<form>` element with all of its children: the `<section>` elements, the `<dl>` elements, and, of course, the form fields themselves.
+In summary, this way of creating forms creates much duplicate or at least very similar code and makes it very time consuming if the structure of forms in general or a specific type of form field has to be changed.
+
+Form builder, in contrast, relies on PHP objects representing each component of the form, from the form itself down to each form field.
+This approach makes creating forms as easy as creating some PHP objects, populating them with all the relevant data, and one line of code in the template to print the form.
+
+
+## Form Builder Components
+
+Form builder consists of several components that are presented on the following pages:
+
+1. [Structure of form builder](structure.md)
+1. [Form validation and form data](validation_data.md)
+1. [Form node dependencies](dependencies.md)
+
+!!! warning "In general, form builder provides default implementation of interfaces by providing either abstract classes or traits. It is expected that the interfaces are always implemented using these abstract classes and traits! This way, if new methods are added to the interfaces, default implementations can be provided by the abstract classes and traits without causing backwards compatibility problems."
+
+
+## `AbstractFormBuilderForm`
+
+To make using form builder easier, `AbstractFormBuilderForm` extends `AbstractForm` and provides most of the code needed to set up a form (of course without specific fields, those have to be added by the concrete form class), like reading and validating form values and using a database object action to use the form data to create or update a database object.
+
+In addition to the existing methods inherited by `AbstractForm`, `AbstractFormBuilderForm` provides the following methods:
+
+- `buildForm()` builds the form in the following steps:
+
+ 1. Call `AbtractFormBuilderForm::createForm()` to create the `IFormDocument` object and add the form fields.
+ 2. Call `IFormDocument::build()` to build the form.
+ 3. Call `AbtractFormBuilderForm::finalizeForm()` to finalize the form like adding dependencies.
+
+ Additionally, between steps 1 and 2 and after step 3, the method provides two events, `createForm` and `buildForm` to allow plugins to register event listeners to execute additional code at the right point in time.
+- `createForm()` creates the `FormDocument` object and sets the form mode.
+ Classes extending `AbstractFormBuilderForm` have to override this method (and call `parent::createForm()` as the first line in the overridden method) to add concrete form containers and form fields to the bare form document.
+- `finalizeForm()` is called after the form has been built and the complete form hierarchy has been established.
+ This method should be overridden to add dependencies, for example.
+- `setFormAction()` is called at the end of `readData()` and sets the form document’s action based on the controller class name and whether an object is currently edited.
+- If an object is edited, at the beginning of `readData()`, `setFormObjectData()` is called which calls `IFormDocument::loadValuesFromObject()`.
+ If values need to be loaded from additional sources, this method should be used for that.
+
+`AbstractFormBuilderForm` also provides the following (public) properties:
+
+- `$form` contains the `IFormDocument` object created in `createForm()`.
+- `$formAction` is either `create` (default) or `edit` and handles which method of the database object is called by default (`create` is called for `$formAction = 'create'` and `update` is called for `$formAction = 'edit'`) and is used to set the value of the `action` template variable.
+- `$formObject` contains the `IStorableObject` if the form is used to edit an existing object.
+ For forms used to create objects, `$formObject` is always `null`.
+ Edit forms have to manually identify the edited object based on the request data and set the value of `$formObject`.
+- `$objectActionName` can be used to set an alternative action to be executed by the database object action that deviates from the default action determined by the value of `$formAction`.
+- `$objectActionClass` is the name of the database object action class that is used to create or update the database object.
+
+
+## `DialogFormDocument`
+
+Form builder forms can also be used in dialogs.
+For such forms, `DialogFormDocument` should be used which provides the additional methods `cancelable($cancelable = true)` and `isCancelable()` to set and check if the dialog can be canceled.
+If a dialog form can be canceled, a cancel button is added.
+
+If the dialog form is fetched via an AJAX request, `IFormDocument::ajax()` has to be called.
+AJAX forms are registered with `WoltLabSuite/Core/Form/Builder/Manager` which also supports getting all of the data of a form via the `getData(formId)` function.
+The `getData()` function relies on all form fields creating and registering a `WoltLabSuite/Core/Form/Builder/Field/Field` object that provides the data of a specific field.
+
+To make it as easy as possible to work with AJAX forms in dialogs, `WoltLabSuite/Core/Form/Builder/Dialog` (abbreviated as `FormBuilderDialog` from now on) should generally be used instead of `WoltLabSuite/Core/Form/Builder/Manager` directly.
+The constructor of `FormBuilderDialog` expects the following parameters:
+
+- `dialogId`: id of the dialog element
+- `className`: PHP class used to get the form dialog (and save the data if `options.submitActionName` is set)
+- `actionName`: name of the action/method of `className` that returns the dialog; the method is expected to return an array with `formId` containg the id of the returned form and `dialog` containing the rendered form dialog
+- `options`: additional options:
+ - `actionParameters` (default: empty): additional parameters sent during AJAX requests
+ - `destroyOnClose` (default: `false`): if `true`, whenever the dialog is closed, the form is destroyed so that a new form is fetched if the dialog is opened again
+ - `dialog`: additional dialog options used as `options` during dialog setup
+ - `onSubmit`: callback when the form is submitted (takes precedence over `submitActionName`)
+ - `submitActionName` (default: not set): name of the action/method of `className` called when the form is submitted
+
+The three public functions of `FormBuilderDialog` are:
+
+- `destroy()` destroys the dialog, the form, and all of the form fields.
+- `getData()` returns a Promise that returns the form data.
+- `open()` opens the dialog.
+
+Example:
+
+```javascript
+require(['WoltLabSuite/Core/Form/Builder/Dialog'], function(FormBuilderDialog) {
+ var dialog = new FormBuilderDialog(
+ 'testDialog',
+ 'wcf\\data\\test\\TestAction',
+ 'getDialog',
+ {
+ destroyOnClose: true,
+ dialog: {
+ title: 'Test Dialog'
+ },
+ submitActionName: 'saveDialog'
+ }
+ );
+
+ elById('testDialogButton').addEventListener('click', function() {
+ dialog.open();
+ });
+});
+```
--- /dev/null
+# Structure of Form Builder
+
+Forms built with form builder consist of three major structural elements listed from top to bottom:
+
+1. form document,
+1. form container,
+1. form field.
+
+The basis for all three elements are form nodes.
+
+!!! info "The form builder API uses fluent interfaces heavily, meaning that unless a method is a getter, it generally returns the objects itself to support method chaining."
+
+
+## Form Nodes
+
+- `IFormNode` is the base interface that any node of a form has to implement.
+- `IFormChildNode` extends `IFormNode` for such elements of a form that can be a child node to a parent node.
+- `IFormParentNode` extends `IFormNode` for such elements of a form that can be a parent to child nodes.
+- `IFormElement` extends `IFormNode` for such elements of a form that can have a description and a label.
+
+
+### `IFormNode`
+
+`IFormNode` is the base interface that any node of a form has to implement and it requires the following methods:
+
+- `addClass($class)`, `addClasses(array $classes)`, `removeClass($class)`, `getClasses()`, and `hasClass($class)` add, remove, get, and check for CSS classes of the HTML element representing the form node.
+ If the form node consists of multiple (nested) HTML elements, the classes are generally added to the top element.
+ `static validateClass($class)` is used to check if a given CSS class is valid.
+ By default, a form node has no CSS classes.
+- `addDependency(IFormFieldDependency $dependency)`, `removeDependency($dependencyId)`, `getDependencies()`, and `hasDependency($dependencyId)` add, remove, get, and check for dependencies of this form node on other form fields.
+ `checkDependencies()` checks if **all** of the node’s dependencies are met and returns a boolean value reflecting the check’s result.
+ The [form builder dependency documentation](dependencies.md) provides more detailed information about dependencies and how they work.
+ By default, a form node has no dependencies.
+- `attribute($name, $value = null)`, `removeAttribute($name)`, `getAttribute($name)`, `getAttributes()`, `hasAttribute($name)` add, remove, get, and check for attributes of the HTML element represting the form node.
+ The attributes are added to the same element that the CSS classes are added to.
+ `static validateAttribute($name)` is used to check if a given attribute is valid.
+ By default, a form node has no attributes.
+- `available($available = true)` and `isAvailable()` can be used to set and check if the node is available.
+ The availability functionality can be used to easily toggle form nodes based, for example, on options without having to create a condition to append the relevant.
+ This way of checking availability makes it easier to set up forms.
+ By default, every form node is available.
+
+ The following aspects are important when working with availability:
+
+ - Unavailable fields produce no output, their value is not read, they are not validated and they are not checked for save values.
+ - Form fields are also able to mark themselves as unavailable, for example, a selection field without any options.
+ - Form containers are automatically unavailable if they contain no available children.
+
+ Availability sets the static availability for form nodes that does not change during the lifetime of a form.
+ In contrast, dependencies represent a dynamic availability for form nodes that depends on the current value of certain form fields.
+- `cleanup()` is called after the whole form is not used anymore to reset other APIs if the form fields depends on them and they expect such a reset.
+ This method is not intended to clean up the form field’s value as a new form document object is created to show a clean form.
+- `getDocument()` returns the `IFormDocument` object the node belongs to.
+ (As `IFormDocument` extends `IFormNode`, form document objects simply return themselves.)
+- `getHtml()` returns the HTML representation of the node.
+ `getHtmlVariables()` return template variables (in addition to the form node itself) to render the node’s HTML representation.
+- `id($id)` and `getId()` set and get the id of the form node.
+ Every id has to be unique within a form.
+ `getPrefixedId()` returns the prefixed version of the node’s id (see `IFormDocument::getPrefix()` and `IFormDocument::prefix()`).
+ `static validateId($id)` is used to check if a given id is valid.
+- `populate()` is called by `IFormDocument::build()` after all form nodes have been added.
+ This method should finilize the initialization of the form node after all parent-child relations of the form document have been established.
+ This method is needed because during the construction of a form node, it neither knows the form document it will belong to nor does it know its parent.
+- `validate()` checks, after the form is submitted, if the form node is valid.
+ A form node with children is valid if all of its child nodes are valid.
+ A form field is valid if its value is valid.
+- `static create($id)` is the factory method that has to be used to create new form nodes with the given id.
+
+`TFormNode` provides a default implementation of most of these methods.
+
+
+### `IFormChildNode`
+
+`IFormChildNode` extends `IFormNode` for such elements of a form that can be a child node to a parent node and it requires the `parent(IFormParentNode $parentNode)` and `getParent()` methods used to set and get the node’s parent node.
+`TFormChildNode` provides a default implementation of these two methods and also of `IFormNode::getDocument()`.
+
+
+### `IFormParentNode`
+
+`IFormParentNode` extends `IFormNode` for such elements of a form that can be a parent to child nodes.
+Additionally, the interface also extends `\Countable` and `\RecursiveIterator`.
+The interface requires the following methods:
+
+- `appendChild(IFormChildNode $child)`, `appendChildren(array $children)`, `insertAfter(IFormChildNode $child, $referenceNodeId)`, and `insertBefore(IFormChildNode $child, $referenceNodeId)` are used to insert new children either at the end or at specific positions.
+ `validateChild(IFormChildNode $child)` is used to check if a given child node can be added.
+ A child node cannot be added if it would cause an id to be used twice.
+- `children()` returns the direct children of a form node.
+- `getIterator()` return a recursive iterator for a form node.
+- `getNodeById($nodeId)` returns the node with the given id by searching for it in the node’s children and recursively in all of their children.
+ `contains($nodeId)` can be used to simply check if a node with the given id exists.
+- `hasValidationErrors()` checks if a form node or any of its children has a validation error (see `IFormField::getValidationErrors()`).
+- `readValues()` recursively calls `IFormParentNode::readValues()` and `IFormField::readValue()` on its children.
+
+
+### `IFormElement`
+
+`IFormElement` extends `IFormNode` for such elements of a form that can have a description and a label and it requires the following methods:
+
+- `label($languageItem = null, array $variables = [])` and `getLabel()` can be used to set and get the label of the form element.
+ `requiresLabel()` can be checked if the form element requires a label.
+ A label-less form element that requires a label will prevent the form from being rendered by throwing an exception.
+- `description($languageItem = null, array $variables = [])` and `getDescription()` can be used to set and get the description of the form element.
+
+
+### `IObjectTypeFormNode`
+
+`IObjectTypeFormField` has to be implemented by form nodes that rely on a object type of a specific object type definition in order to function.
+The implementing class has to implement the methods `objectType($objectType)`, `getObjectType()`, and `getObjectTypeDefinition()`.
+`TObjectTypeFormNode` provides a default implementation of these three methods.
+
+
+### `CustomFormNode`
+
+`CustomFormNode` is a form node whose contents can be set directly via `content($content)`.
+
+!!! warning "This class should generally not be relied on. Instead, `TemplateFormNode` should be used."
+
+
+### `TemplateFormNode`
+
+`TemplateFormNode` is a form node whose contents are read from a template.
+`TemplateFormNode` has the following additional methods:
+
+- `application($application)` and `getApplicaton()` can be used to set and get the abbreviation of the application the shown template belongs to.
+ If no template has been set explicitly, `getApplicaton()` returns `wcf`.
+- `templateName($templateName)` and `getTemplateName()` can be used to set and get the name of the template containing the node contents.
+ If no template has been set and the node is rendered, an exception will be thrown.
+- `variables(array $variables)` and `getVariables()` can be used to set and get additional variables passed to the template.
+
+
+## Form Document
+
+A form document object represents the form as a whole and has to implement the `IFormDocument` interface.
+WoltLab Suite provides a default implementation with the `FormDocument` class.
+`IFormDocument` should not be implemented directly but instead `FormDocument` should be extended to avoid issues if the `IFormDocument` interface changes in the future.
+
+`IFormDocument` extends `IFormParentNode` and requires the following additional methods:
+
+- `action($action)` and `getAction()` can be used set and get the `action` attribute of the `<form>` HTML element.
+- `addButton(IFormButton $button)` and `getButtons()` can be used add and get form buttons that are shown at the bottom of the form.
+ `addDefaultButton($addDefaultButton)` and `hasDefaultButton()` can be used to set and check if the form has the default button which is added by default unless specified otherwise.
+ Each implementing class may define its own default button.
+ `FormDocument` has a button with id `submitButton`, label `wcf.global.button.submit`, access key `s`, and CSS class `buttonPrimary` as its default button.
+- `ajax($ajax)` and `isAjax()` can be used to set and check if the form document is requested via an AJAX request or processes data via an AJAX request.
+ These methods are helpful for form fields that behave differently when providing data via AJAX.
+- `build()` has to be called once after all nodes have been added to this document to trigger `IFormNode::populate()`.
+- `formMode($formMode)` and `getFormMode()` sets the form mode.
+ Possible form modes are:
+
+ - `IFormDocument::FORM_MODE_CREATE` has to be used when the form is used to create a new object.
+ - `IFormDocument::FORM_MODE_UPDATE` has to be used when the form is used to edit an existing object.
+- `getData()` returns the array containing the form data and which is passed as the `$parameters` argument of the constructor of a database object action object.
+- `getDataHandler()` returns the data handler for this document that is used to process the field data into a parameters array for the constructor of a database object action object.
+- `getEnctype()` returns the encoding type of the form.
+ If the form contains a `IFileFormField`, `multipart/form-data` is returned, otherwise `null` is returned.
+- `loadValues(array $data, IStorableObject $object)` is used when editing an existing object to set the form field values by calling `IFormField::loadValue()` for all form fields.
+ Additionally, the form mode is set to `IFormDocument::FORM_MODE_UPDATE`.
+- `method($method)` and `getMethod()` can be used to set and get the `method` attribute of the `<form>` HTML element.
+ By default, the method is `post`.
+- `prefix($prefix)` and `getPrefix()` can be used to set and get a global form prefix that is prepended to form elements’ names and ids to avoid conflicts with other forms.
+ By default, the prefix is an empty string.
+ If a prefix of `foo` is set, `getPrefix()` returns `foo_` (additional trailing underscore).
+- `requestData(array $requestData)`, `getRequestData($index = null)`, and `hasRequestData($index = null)` can be used to set, get and check for specific request data.
+ In most cases, the relevant request data is the `$_POST` array.
+ In default AJAX requests handled by database object actions, however, the request data generally is in `AbstractDatabaseObjectAction::$parameters`.
+ By default, `$_POST` is the request data.
+
+The last aspect is relevant for `DialogFormDocument` objects.
+`DialogFormDocument` is a specialized class for forms in dialogs that, in contrast to `FormDocument` do not require an `action` to be set.
+Additionally, `DialogFormDocument` provides the `cancelable($cancelable = true)` and `isCancelable()` methods used to determine if the dialog from can be canceled.
+By default, dialog forms are cancelable.
+
+
+## Form Button
+
+A form button object represents a button shown at the end of the form that, for example, submits the form.
+Every form button has to implement the `IFormButton` interface that extends `IFormChildNode` and `IFormElement`.
+`IFormButton` requires four methods to be implemented:
+
+- `accessKey($accessKey)` and `getAccessKey()` can be used to set and get the access key with which the form button can be activated.
+ By default, form buttons have no access key set.
+- `submit($submitButton)` and `isSubmit()` can be used to set and check if the form button is a submit button.
+ A submit button is an `input[type=submit]` element.
+ Otherwise, the button is a `button` element.
+
+
+## Form Container
+
+A form container object represents a container for other form containers or form field directly.
+Every form container has to implement the `IFormContainer` interface which requires the following method:
+
+- `loadValues(array $data, IStorableObject $object)` is called by `IFormDocument::loadValuesFromObject()` to inform the container that object data is loaded.
+ This method is *not* intended to generally call `IFormField::loadValues()` on its form field children as these methods are already called by `IFormDocument::loadValuesFromObject()`.
+ This method is intended for specialized form containers with more complex logic.
+
+There are multiple default container implementations:
+
+1. `FormContainer` is the default implementation of `IFormContainer`.
+1. `TabMenuFormContainer` represents the container of tab menu, while
+1. `TabFormContainer` represents a tab of a tab menu and
+1. `TabTabMenuFormContainer` represents a tab of a tab menu that itself contains a tab menu.
+1. The children of `RowFormContainer` are shown in a row and should use `col-*` classes.
+1. The children of `RowFormFieldContainer` are also shown in a row but does not show the labels and descriptions of the individual form fields.
+ Instead of the individual labels and descriptions, the container's label and description is shown and both span all of fields.
+1. `SuffixFormFieldContainer` can be used for one form field with a second selection form field used as a suffix.
+
+The methods of the interfaces that `FormContainer` is implementing are well documented, but here is a short overview of the most important methods when setting up a form or extending a form with an event listener:
+
+- `appendChild(IFormChildNode $child)`, `appendChildren(array $children)`, and `insertBefore(IFormChildNode $child, $referenceNodeId)` are used to insert new children into the form container.
+- `description($languageItem = null, array $variables = [])` and `label($languageItem = null, array $variables = [])` are used to set the description and the label or title of the form container.
+
+
+## Form Field
+
+A form field object represents a concrete form field that allows entering data.
+Every form field has to implement the `IFormField` interface which extends `IFormChildNode` and `IFormElement`.
+
+`IFormField` requires the following additional methods:
+
+- `addValidationError(IFormFieldValidationError $error)` and `getValidationErrors()` can be used to get and set validation errors of the form field (see [form validation](validation_data.md#form-validation)).
+- `addValidator(IFormFieldValidator $validator)`, `getValidators()`, `removeValidator($validatorId)`, and `hasValidator($validatorId)` can be used to get, set, remove, and check for validators for the form field (see [form validation](validation_data.md#form-validation)).
+- `getFieldHtml()` returns the field's HTML output without the surrounding `dl` structure.
+- `objectProperty($objectProperty)` and `getObjectProperty()` can be used to get and set the object property that the field represents.
+ When setting the object property is set to an empty string, the previously set object property is unset.
+ If no object property has been set, the field’s (non-prefixed) id is returned.
+
+ The object property allows having different fields (requiring different ids) that represent the same object property which is handy when available options of the field’s value depend on another field.
+ Having object property allows to define different fields for each value of the other field and to use form field dependencies to only show the appropriate field.
+- `readValue()` reads the form field value from the request data after the form is submitted.
+- `required($required = true)` and `isRequired()` can be used to determine if the form field has to be filled out.
+ By default, form fields do not have to be filled out.
+- `value($value)` and `getSaveValue()` can be used to get and set the value of the form field to be used outside of the context of forms.
+ `getValue()`, in contrast, returns the internal representation of the form field’s value.
+ In general, the internal representation is only relevant when validating the value in additional validators.
+ `loadValue(array $data, IStorableObject $object)` extracts the form field value from the given data array (and additional, non-editable data from the object if the field needs them).
+
+`AbstractFormField` provides default implementations of many of the listed methods above and should be extended instead of implementing `IFormField` directly.
+
+An overview of the form fields provided by default can be found [here](form_fields.md).
+
+
+### Form Field Interfaces and Traits
+
+WoltLab Suite Core provides a variety of interfaces and matching traits with default implementations for several common features of form fields:
+
+
+#### `IAutoFocusFormField`
+
+`IAutoFocusFormField` has to be implemented by form fields that can be auto-focused.
+The implementing class has to implement the methods `autoFocus($autoFocus = true)` and `isAutoFocused()`.
+By default, form fields are not auto-focused.
+`TAutoFocusFormField` provides a default implementation of these two methods.
+
+
+#### `IFileFormField`
+
+`IFileFormField` has to be implemented by every form field that uploads files so that the `enctype` attribute of the form document is `multipart/form-data` (see `IFormDocument::getEnctype()`).
+
+
+#### `IFilterableSelectionFormField`
+
+`IFilterableSelectionFormField` extends `ISelectionFormField` by the possibilty for users when selecting the value(s) to filter the list of available options.
+The implementing class has to implement the methods `filterable($filterable = true)` and `isFilterable()`.
+`TFilterableSelectionFormField` provides a default implementation of these two methods.
+
+
+#### `II18nFormField`
+
+`II18nFormField` has to be implemented by form fields if the form field value can be entered separately for all available languages.
+The implementing class has to implement the following methods:
+
+- `i18n($i18n = true)` and `isI18n()` can be used to set whether a specific instance of the class actually supports multilingual input.
+- `i18nRequired($i18nRequired = true)` and `isI18nRequired()` can be used to set whether a specific instance of the class requires separate values for all languages.
+- `languageItemPattern($pattern)` and `getLanguageItemPattern()` can be used to set the pattern/regular expression for the language item used to save the multilingual values.
+- `hasI18nValues()` and `hasPlainValue()` check if the current value is a multilingual or monolingual value.
+
+`TI18nFormField` provides a default implementation of these eight methods and additional default implementations of some of the `IFormField` methods.
+If multilingual input is enabled for a specific form field, classes using `TI18nFormField` register a [custom form field data processor](validation_data.md#customformfielddataprocessor) to add the array with multilingual input into the `$parameters` array directly using `{$objectProperty}_i18n` as the array key.
+If multilingual input is enabled but only a monolingual value is entered, the custom form field data processor does nothing and the form field’s value is added by the `DefaultFormDataProcessor` into the `data` sub-array of the `$parameters` array.
+
+!!! info "`TI18nFormField` already provides a default implementation of `IFormField::validate()`."
+
+
+#### `IImmutableFormField`
+
+`IImmutableFormField` has to be implemented by form fields that support being displayed but whose value cannot be changed.
+The implementing class has to implement the methods `immutable($immutable = true)` and `isImmutable()` that can be used to determine if the value of the form field is mutable or immutable.
+By default, form field are mutable.
+
+
+#### `IMaximumFormField`
+
+`IMaximumFormField` has to be implemented by form fields if the entered value must have a maximum value.
+The implementing class has to implement the methods `maximum($maximum = null)` and `getMaximum()`.
+A maximum of `null` signals that no maximum value has been set.
+`TMaximumFormField` provides a default implementation of these two methods.
+
+!!! warning "The implementing class has to validate the entered value against the maximum value manually."
+
+
+#### `IMaximumLengthFormField`
+
+`IMaximumLengthFormField` has to be implemented by form fields if the entered value must have a maximum length.
+The implementing class has to implement the methods `maximumLength($maximumLength = null)`, `getMaximumLength()`, and `validateMaximumLength($text, Language $language = null)`.
+A maximum length of `null` signals that no maximum length has been set.
+`TMaximumLengthFormField` provides a default implementation of these two methods.
+
+!!! warning "The implementing class has to validate the entered value against the maximum value manually by calling `validateMaximumLength()`."
+
+
+#### `IMinimumFormField`
+
+`IMinimumFormField` has to be implemented by form fields if the entered value must have a minimum value.
+The implementing class has to implement the methods `minimum($minimum = null)` and `getMinimum()`.
+A minimum of `null` signals that no minimum value has been set.
+`TMinimumFormField` provides a default implementation of these three methods.
+
+!!! warning "The implementing class has to validate the entered value against the minimum value manually."
+
+
+#### `IMinimumLengthFormField`
+
+`IMinimumLengthFormField` has to be implemented by form fields if the entered value must have a minimum length.
+The implementing class has to implement the methods `minimumLength($minimumLength = null)`, `getMinimumLength()`, and `validateMinimumLength($text, Language $language = null)`.
+A minimum length of `null` signals that no minimum length has been set.
+`TMinimumLengthFormField` provides a default implementation of these three methods.
+
+!!! warning "The implementing class has to validate the entered value against the minimum value manually by calling `validateMinimumLength()`."
+
+
+#### `IMultipleFormField`
+
+`IMinimumLengthFormField` has to be implemented by form fields that support selecting or setting multiple values.
+The implementing class has to implement the following methods:
+
+- `multiple($multiple = true)` and `allowsMultiple()` can be used to set whether a specific instance of the class actually should support multiple values.
+ By default, multiple values are not supported.
+- `minimumMultiples($minimum)` and `getMinimumMultiples()` can be used to set the minimum number of values that have to be selected/entered.
+ By default, there is no required minimum number of values.
+- `maximumMultiples($minimum)` and `getMaximumMultiples()` can be used to set the maximum number of values that have to be selected/entered.
+ By default, there is no maximum number of values.
+ `IMultipleFormField::NO_MAXIMUM_MULTIPLES` is returned if no maximum number of values has been set and it can also be used to unset a previously set maximum number of values.
+
+`TMultipleFormField` provides a default implementation of these six methods and classes using `TMultipleFormField` register a [custom form field data processor](validation_data.md#customformfielddataprocessor) to add the `HtmlInputProcessor` object with the text into the `$parameters` array directly using `{$objectProperty}_htmlInputProcessor` as the array key.
+
+!!! warning "The implementing class has to validate the values against the minimum and maximum number of values manually."
+
+
+#### `INullableFormField`
+
+`INullableFormField` has to be implemented by form fields that support `null` as their (empty) value.
+The implementing class has to implement the methods `nullable($nullable = true)` and `isNullable()`.
+`TNullableFormField` provides a default implementation of these two methods.
+
+`null` should be returned by `IFormField::getSaveValue()` is the field is considered empty and the form field has been set as nullable.
+
+
+#### `IPackagesFormField`
+
+`IPackagesFormField` has to be implemented by form fields that, in some way, considers packages whose ids may be passed to the field object.
+The implementing class has to implement the methods `packageIDs(array $packageIDs)` and `getPackageIDs()`.
+`TPackagesFormField` provides a default implementation of these two methods.
+
+
+#### `IPatternFormField`
+
+`IPatternFormField` has to be implemented by form fields that support the [`pattern` attribute](https://html.spec.whatwg.org/multipage/input.html#the-pattern-attribute).
+The implementing class has to implement the methods `pattern(?string $pattern): self` and `getPattern(): ?string`, which are used to set and get the pattern, respectively.
+`TPatternFormField` provides a default implementation of these two methods.
+
+
+#### `IPlaceholderFormField`
+
+`IPlaceholderFormField` has to be implemented by form fields that support a placeholder value for empty fields.
+The implementing class has to implement the methods `placeholder($languageItem = null, array $variables = [])` and `getPlaceholder()`.
+`TPlaceholderFormField` provides a default implementation of these two methods.
+
+
+#### `ISelectionFormField`
+
+`ISelectionFormField` has to be implemented by form fields with a predefined set of possible values.
+The implementing class has to implement the getter and setter methods `options($options, $nestedOptions = false, $labelLanguageItems = true)` and `getOptions()` and additionally two methods related to nesting, i.e. whether the selectable options have a hierarchy:
+`supportsNestedOptions()` and `getNestedOptions()`.
+`TSelectionFormField` provides a default implementation of these four methods.
+
+
+#### `ISuffixedFormField`
+
+`ISuffixedFormField` has to be implemented by form fields that support supports displaying a suffix behind the actual input field.
+The implementing class has to implement the methods `suffix($languageItem = null, array $variables = [])` and `getSuffix()`.
+`TSuffixedFormField` provides a default implementation of these two methods.
+
+
+#### `TDefaultIdFormField`
+
+Form fields that have a default id have to use `TDefaultIdFormField` and have to implement the method `getDefaultId()`.
+
+
+## Displaying Forms
+
+The only thing to do in a template to display the **whole** form including all of the necessary JavaScript is to put
+
+```smarty
+{@$form->getHtml()}
+```
+
+into the template file at the relevant position.
--- /dev/null
+# Form Validation and Form Data
+
+## Form Validation
+
+Every form field class has to implement `IFormField::validate()` according to their internal logic of what constitutes a valid value.
+If a certain constraint for the value is no met, a form field validation error object is added to the form field.
+Form field validation error classes have to implement the interface `IFormFieldValidationError`.
+
+In addition to intrinsic validations like checking the length of the value of a text form field, in many cases, there are additional constraints specific to the form like ensuring that the text is not already used by a different object of the same database object class.
+Such additional validations can be added to (and removed from) the form field via implementations of the `IFormFieldValidator` interface.
+
+
+### `IFormFieldValidationError` / `FormFieldValidationError`
+
+`IFormFieldValidationError` requires the following methods:
+
+- `__construct($type, $languageItem = null, array $information = [])` creates a new validation error object for an error with the given type and message stored in the given language items.
+ The information array is used when generating the error message.
+- `getHtml()` returns the HTML element representing the error that is shown to the user.
+- `getMessage()` returns the error message based on the language item and information array given in the constructor.
+- `getInformation()` and `getType()` are getters for the first and third parameter of the constructor.
+
+`FormFieldValidationError` is a default implementation of the interface that shows the error in an `small.innerError` HTML element below the form field.
+
+Form field validation errors are added to form fields via the `IFormField::addValidationError(IFormFieldValidationError $error)` method.
+
+
+### `IFormFieldValidator` / `FormFieldValidator`
+
+`IFormFieldValidator` requires the following methods:
+
+- `__construct($id, callable $validator)` creates a new validator with the given id that passes the validated form field to the given callable that does the actual validation.
+ `static validateId($id)` is used to check if the given id is valid.
+- `__invoke(IFormField $field)` is used when the form field is validated to execute the validator.
+- `getId()` returns the id of the validator.
+
+`FormFieldValidator` is a default implementation of the interface.
+
+Form field validators are added to form fields via the `addValidator(IFormFieldValidator $validator)` method.
+
+
+## Form Data
+
+After a form is successfully validated, the data of the form fields (returned by `IFormDocument::getData()`) have to be extracted which is the job of the `IFormDataHandler` object returned by `IFormDocument::getDataHandler()`.
+Form data handlers themselves, however, are only iterating through all `IFormDataProcessor` instances that have been registered with the data handler.
+
+
+### `IFormDataHandler` / `FormDataHandler`
+
+`IFormDataHandler` requires the following methods:
+
+- `addProcessor(IFormDataProcessor $processor)` adds a new data processor to the data handler.
+- `getFormData(IFormDocument $document)` returns the data of the given form by applying all registered data handlers on the form.
+- `getObjectData(IFormDocument $document, IStorableObject $object)` returns the data of the given object which will be used to populate the form field values of the given form.
+
+`FormDataHandler` is the default implementation of this interface and should also be extended instead of implementing the interface directly.
+
+
+### `IFormDataProcessor` / `DefaultFormDataProcessor`
+
+`IFormDataProcessor` requires the following methods:
+
+- `processFormData(IFormDocument $document, array $parameters)` is called by `IFormDataHandler::getFormData()`.
+ The method processes the given parameters array and returns the processed version.
+- `processObjectData(IFormDocument $document, array $data, IStorableObject $object)` is called by `IFormDataHandler::getObjectData()`.
+ The method processes the given object data array and returns the processed version.
+
+When `FormDocument` creates its `FormDataHandler` instance, it automatically registers an `DefaultFormDataProcessor` object as the first data processor.
+`DefaultFormDataProcessor` puts the save value of all form fields that are available and have a save value into `$parameters['data']` using the form field’s object property as the array key.
+
+!!! warning "`IFormDataProcessor` should not be implemented directly. Instead, `AbstractFormDataProcessor` should be extended."
+
+!!! info "All form data is put into the `data` sub-array so that the whole `$parameters` array can be passed to a database object action object that requires the actual database object data to be in the `data` sub-array."
+
+
+
+### Additional Data Processors
+
+#### `CustomFormDataProcessor`
+
+As mentioned above, the data in the `data` sub-array is intended to directly create or update the database object with.
+As these values are used in the database query directly, these values cannot contain arrays.
+Several form fields, however, store and return their data in form of arrays.
+Thus, this data cannot be returned by `IFormField::getSaveValue()` so that `IFormField::hasSaveValue()` returns `false` and the form field’s data is not collected by the standard `DefaultFormDataProcessor` object.
+
+Instead, such form fields register a `CustomFormDataProcessor` in their `IFormField::populate()` method that inserts the form field value into the `$parameters` array directly.
+This way, the relevant database object action method has access to the data to save it appropriately.
+
+The constructor of `CustomFormDataProcessor` requires an id (that is primarily used in error messages during the validation of the second parameter) and callables for `IFormDataProcessor::processFormData()` and `IFormDataProcessor::processObjectData()` which are passed the same parameters as the `IFormDataProcessor` methods.
+Only one of the callables has to be given, the other one then defaults to simply returning the relevant array unchanged.
+
+
+#### `VoidFormDataProcessor`
+
+Some form fields might only exist to toggle the visibility of other form fields (via dependencies) but the data of form field itself is irrelevant.
+As `DefaultFormDataProcessor` collects the data of all form fields, an additional data processor in the form of a `VoidFormDataProcessor` can be added whose constructor `__construct($property, $isDataProperty = true)` requires the name of the relevant object property/form id and whether the form field value is stored in the `data` sub-array or directory in the `$parameters` array.
+When the data processor is invoked, it checks whether the relevant entry in the `$parameters` array exists and voids it by removing it from the array.
### Forms
-!!! info "For new forms, use the new [form builder API](../php/api/form_builder.md) introduced with WoltLab Suite 5.2."
+!!! info "For new forms, use the new [form builder API](../php/api/form_builder/overview.md) introduced with WoltLab Suite 5.2."
```smarty
<form method="post" action="{link controller='FooBar'}{/link}">
- 'Comments': 'php/api/comments.md'
- 'Cronjobs': 'php/api/cronjobs.md'
- 'Events': 'php/api/events.md'
- - 'Form Builder': 'php/api/form_builder.md'
+ - 'Form Builder':
+ - 'Overview': 'php/api/form_builder/overview.md'
+ - 'Structure': 'php/api/form_builder/structure.md'
+ - 'Fields': 'php/api/form_builder/form_fields.md'
+ - 'Validation and Data': 'php/api/form_builder/validation_data.md'
+ - 'Dependencies': 'php/api/form_builder/dependencies.md'
- 'Package Installation Plugins': 'php/api/package_installation_plugins.md'
- 'User Activity Points': 'php/api/user_activity_points.md'
- 'User Notifications': 'php/api/user_notifications.md'