## Developer Tools
-{% include callout.html content="This feature is available with WoltLab Suite 3.1 or newer only." type="warning" %}
+!!! warning "This feature is available with WoltLab Suite 3.1 or newer only."
The developer tools provide an interface to synchronize the data of an installed package with a bare repository on the local disk. You can re-import most PIPs at any time and have the changes applied without crafting a manual update. This process simulates a regular package update with a single PIP only, and resets the cache after the import has been completed.
### The Accelerated Guest View ("Tiny Builds")
-{% include callout.html content="You can learn more on the [Accelerated Guest View](migration_wsc-30_javascript.md) in the migration docs." type="info" %}
+!!! info "You can learn more on the [Accelerated Guest View](migration_wsc-30_javascript.md) in the migration docs."
The "Accelerated Guest View" was introduced in WoltLab Suite 3.1 and aims to
decrease page size and to improve responsiveness by enabling a read-only mode
## Selecting Elements
-{% include callout.html content="Unlike libraries like jQuery, these functions will return `null` if an element is not found. You are responsible to validate if the element exist and to branch accordingly, invoking methods on the return value without checking for `null` will yield an error." type="warning" %}
+!!! warning "Unlike libraries like jQuery, these functions will return `null` if an element is not found. You are responsible to validate if the element exist and to branch accordingly, invoking methods on the return value without checking for `null` will yield an error."
### `elById(id: string): Element | null`
### `elBySel(selector: string, context?: Element): Element | null`
-{% include callout.html content="The underlying `querySelector()`-method works on the entire DOM hierarchy and can yield results outside of your context element! Please read and understand the MDN article on [`Element.querySelector()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector#The_entire_hierarchy_counts) to learn more about this." type="danger" %}
+!!! danger "The underlying `querySelector()`-method works on the entire DOM hierarchy and can yield results outside of your context element! Please read and understand the MDN article on [`Element.querySelector()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector#The_entire_hierarchy_counts) to learn more about this."
Select a single element based on a CSS selector, optionally limiting the results
to be a direct or indirect children of the context element.
### `elBySelAll(selector: string, context?: Element, callback: (element: Element) => void): NodeList`
-{% include callout.html content="The underlying `querySelector()`-method works on the entire DOM hierarchy and can yield results outside of your context element! Please read and understand the MDN article on [`Element.querySelector()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector#The_entire_hierarchy_counts) to learn more about this." type="danger" %}
+!!! danger "The underlying `querySelector()`-method works on the entire DOM hierarchy and can yield results outside of your context element! Please read and understand the MDN article on [`Element.querySelector()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector#The_entire_hierarchy_counts) to learn more about this."
Finds and returns a `NodeList` containing all elements that match the provided
CSS selector. Although `NodeList` is an array-like structure, it is not possible
#### `withCredentials`
-{% include callout.html content="Enabling this parameter for any domain other than the current will trigger a CORS preflight request." type="warning" %}
+!!! warning "Enabling this parameter for any domain other than the current will trigger a CORS preflight request."
_Defaults to `false`._
#### `includeRequestedWith`
-{% include callout.html content="Enabling this parameter for any domain other than the current will trigger a CORS preflight request." type="warning" %}
+!!! warning "Enabling this parameter for any domain other than the current will trigger a CORS preflight request."
_Defaults to `true`._
## `Environment`
-{% include callout.html content="The `Environment` module uses a mixture of feature detection and user agent sniffing to determine the browser and platform. In general, its results have proven to be very accurate, but it should be taken with a grain of salt regardless. Especially the browser checks are designed to be your last resort, please use feature detection instead whenever it is possible!" type="warning" %}
+!!! warning "The `Environment` module uses a mixture of feature detection and user agent sniffing to determine the browser and platform. In general, its results have proven to be very accurate, but it should be taken with a grain of salt regardless. Especially the browser checks are designed to be your last resort, please use feature detection instead whenever it is possible!"
Sometimes it may be necessary to alter the behavior of your code depending on
the browser platform (e. g. mobile devices) or based on a specific browser in
## `ObjectMap`
-{% include callout.html content="This class uses a `WeakMap` internally, the keys are only weakly referenced and do not prevent garbage collection." type="info" %}
+!!! info "This class uses a `WeakMap` internally, the keys are only weakly referenced and do not prevent garbage collection."
Represents a collection where any kind of objects, such as class instances or
DOM elements, can be used as key. These keys are weakly referenced and will not
## `getDialog(id: string | Object): Object`
-{% include callout.html content="This method returns an internal data object by reference, any modifications made do have an effect on the dialogs behavior and in particular no validation is performed on the modification. It is strongly recommended to use the `.set*()` methods only." type="warning" %}
+!!! warning "This method returns an internal data object by reference, any modifications made do have an effect on the dialogs behavior and in particular no validation is performed on the modification. It is strongly recommended to use the `.set*()` methods only."
Returns the internal dialog data that is attached to a dialog. The most important
key is `.content` which holds a reference to the dialog's inner content element.
#### `horizontal: string`
-{% include callout.html content="This value is automatically flipped for RTL (right-to-left) languages, `left` is changed into `right` and vice versa." type="info" %}
+!!! info "This value is automatically flipped for RTL (right-to-left) languages, `left` is changed into `right` and vice versa."
_Defaults to `"left"`._
#### `allowFlip: string`
-{% include callout.html content="The value for `horizontal` is automatically flipped for RTL (right-to-left) languages, `left` is changed into `right` and vice versa. This setting only controls the behavior when violating space constraints, therefore the aforementioned transformation is always applied." type="info" %}
+!!! info "The value for `horizontal` is automatically flipped for RTL (right-to-left) languages, `left` is changed into `right` and vice versa. This setting only controls the behavior when violating space constraints, therefore the aforementioned transformation is always applied."
_Defaults to `"both"`._
### Exceptions
-{% include callout.html content="These exceptions represent the built-in PIPs only, 3rd party plugins and apps may define their own exceptions." type="info" %}
+!!! info "These exceptions represent the built-in PIPs only, 3rd party plugins and apps may define their own exceptions."
| PIP | Default Value |
|-------|-------|
## cronjob.xml
-{% include callout.html content="Legacy cronjobs are assigned a non-deterministic generic name, the only way to assign them a name is removing them and then adding them again." type="warning" %}
+!!! warning "Legacy cronjobs are assigned a non-deterministic generic name, the only way to assign them a name is removing them and then adding them again."
Cronjobs can now be assigned a name using the name attribute as in `<cronjob name="com.woltlab.wcf.refreshPackageUpdates">`, it will be used to identify cronjobs during an update or delete.
## eventListener.xml
-{% include callout.html content="Legacy event listeners are assigned a non-deterministic generic name, the only way to assign them a name is removing them and then adding them again." type="warning" %}
+!!! warning "Legacy event listeners are assigned a non-deterministic generic name, the only way to assign them a name is removing them and then adding them again."
Event listeners can now be assigned a name using the name attribute as in `<eventlistener name="sessionPageAccessLog">`, it will be used to identify event listeners during an update or delete.
#### Converting from BBCode
-{% include callout.html content="Enabling message conversion for HTML messages is undefined and yields unexpected results." type="warning" %}
+!!! warning "Enabling message conversion for HTML messages is undefined and yields unexpected results."
Legacy message that still use raw bbcodes must be converted to be properly parsed by the html processors. This process is enabled by setting the fourth parameter of `process()` to `true`.
## Breadcrumbs / Page Location
-{% include callout.html content="Breadcrumbs used to be added left to right, but parent locations are added from the bottom to the top, starting with the first ancestor and going upwards. In most cases you simply need to reverse the order." type="warning" %}
+!!! warning "Breadcrumbs used to be added left to right, but parent locations are added from the bottom to the top, starting with the first ancestor and going upwards. In most cases you simply need to reverse the order."
Breadcrumbs used to be a lose collection of arbitrary links, but are now represented by actual page objects and the control has shifted over to the `PageLocationManager`.
## New Style Variables
-{% include callout.html content="The new style variables are only applied to styles that have the compatibility set to WSC 3.1" type="info" %}
+!!! info "The new style variables are only applied to styles that have the compatibility set to WSC 3.1"
### wcfContentContainer
## Re-Evaluate HTML Messages
-{% include callout.html content="You need to manually set the disallowed bbcodes in order to avoid unintentional bbcode evaluation. Please see [this commit](https://github.com/WoltLab/WCF/commit/7e058783da1378dda5393a9bb4df9cfe94e5b394) for a reference implementation inside worker processes." type="warning" %}
+!!! warning "You need to manually set the disallowed bbcodes in order to avoid unintentional bbcode evaluation. Please see [this commit](https://github.com/WoltLab/WCF/commit/7e058783da1378dda5393a9bb4df9cfe94e5b394) for a reference implementation inside worker processes."
The HtmlInputProcessor only supported two ways to handle an existing HTML message:
## Comment-System Overhaul
-{% include callout.html content="Unfortunately, there has been a breaking change related to the creation of comments. You need to apply the changes below before being able to create new comments." type="danger" %}
+!!! danger "Unfortunately, there has been a breaking change related to the creation of comments. You need to apply the changes below before being able to create new comments."
### Adding Comments
## Sidebar Toogle-Buttons on Mobile Device
-{% include callout.html content="You cannot override the button label for sidebars containing navigation menus." type="info" %}
+!!! info "You cannot override the button label for sidebars containing navigation menus."
The page sidebars are automatically collapsed and presented as one or, when both sidebar are present, two condensed buttons. They use generic sidebar-related labels when open or closed, with the exception of embedded menus which will change the button label to read "Show/Hide Navigation".
$earliestTime = $data['earliestTime'];
```
The method also returns `earliestTime` so that you can tell the user in the error message when they are able again to create new content of the relevant type.
- {% include callout.html content="Flood control entries are only stored for 31 days and older entries are cleaned up daily." type="info" %}
+ !!! info "Flood control entries are only stored for 31 days and older entries are cleaned up daily."
The previously mentioned methods of `FloodControl` use the active user and the current timestamp as reference point.
`FloodControl` also provides methods to register content or check flood control for other registered users or for guests via their IP address.
For further details on these methods, please refer to the [documentation in the FloodControl class](https://github.com/WoltLab/WCF/blob/master/wcfsetup/install/files/lib/system/flood/FloodControl.class.php).
-{% include callout.html content="Do not interact directly with the flood control database table but only via the `FloodControl` class!" type="warning" %}
+!!! warning "Do not interact directly with the flood control database table but only via the `FloodControl` class!"
## PHP Database API
]);
```
-{% include callout.html content="Like with every change to existing database tables, packages can only rename columns that they installed." type="info" %}
+!!! info "Like with every change to existing database tables, packages can only rename columns that they installed."
## Captcha
The actual session storage is considered an implementation detail and you *must not* directly interact with the session tables.
Future versions might support alternative session backends, such as Redis.
-{% include callout.html content="Do not interact directly with the session database tables but only via the `SessionHandler` class!" type="warning" %}
+!!! warning "Do not interact directly with the session database tables but only via the `SessionHandler` class!"
### Reauthentication
As a self-contained example, you can find the initial implementation of the email multi-factor method in [WoltLab/WCF#3729](https://github.com/WoltLab/WCF/pull/3729).
Please check [the version history](https://github.com/WoltLab/WCF/commits/master/wcfsetup/install/files/lib/system/user/multifactor/EmailMultifactorMethod.class.php) of the PHP class to make sure you do not miss important changes that were added later.
-{% include callout.html content="Multi-factor authentication is security sensitive.
-Make sure to carefully read the remarks in IMultifactorMethod for possible issues.
-Also make sure to carefully test your implementation against all sorts of incorrect input and consider attack vectors such as race conditions.
-It is strongly recommended to generously check the current state by leveraging assertions and exceptions." type="warning" %}
+!!! warning "Multi-factor authentication is security sensitive. Make sure to carefully read the remarks in `IMultifactorMethod` for possible issues. Also make sure to carefully test your implementation against all sorts of incorrect input and consider attack vectors such as race conditions. It is strongly recommended to generously check the current state by leveraging assertions and exceptions."
## Deprecations and Removals
# Database PHP API
-{% include callout.html content="Available since WoltLab Suite 5.2." type="info" %}
+!!! info "Available since WoltLab Suite 5.2."
While the [sql](package_pip_sql.md) package installation plugin supports adding and removing tables, columns, and indices, it is not able to handle cases where the added table, column, or index already exist.
We have added a new PHP-based API to manipulate the database scheme which can be used in combination with the [script](package_pip_script.md) package installation plugin that skips parts that already exist:
The attribute `version` must be a valid version number as described in the [\<version\>](#version) section. In the example above it will be impossible to install this package in WoltLab Suite Core 3.1.0 Alpha 1 or higher.
### `<compatibility>`
-{% include callout.html content="Available since WoltLab Suite 3.1" type="info" %}
-{% include callout.html content="With the release of WoltLab Suite 5.2 the API versions were abolished. Instead of using API versions packages should exclude version `6.0.0 Alpha 1` of `com.woltlab.wcf` going forward." type="warning" %}
+!!! info "Available since WoltLab Suite 3.1"
+!!! warning "With the release of WoltLab Suite 5.2 the API versions were abolished. Instead of using API versions packages should exclude version `6.0.0 Alpha 1` of `com.woltlab.wcf` going forward."
WoltLab Suite 3.1 introduced a new versioning system that focused around the API compatibility and is intended to replace the `<excludedpackage>` instruction for the Core for most plugins.
The attribute `fromversion` must be a valid version number as described in the [\<version\>](#version) section and specifies a possible update from that very version to the package's version.
-{% include callout.html content="The installation process will pick exactly one update instruction, ignoring everything else. Please read the explanation below!" type="warning" %}
+!!! warning "The installation process will pick exactly one update instruction, ignoring everything else. Please read the explanation below!"
Example:
There is a [list of all default PIPs](package_pip.md) available.
-{% include callout.html content="Both the `type`-attribute and the element value are case-sensitive. Windows does not care if the file is called `objecttypedefinition.xml` but was referenced as `objectTypeDefinition.xml`, but both Linux and Mac systems will be unable to find the file." type="warning" %}
+!!! warning "Both the `type`-attribute and the element value are case-sensitive. Windows does not care if the file is called `objecttypedefinition.xml` but was referenced as `objectTypeDefinition.xml`, but both Linux and Mac systems will be unable to find the file."
In addition to the `type` attribute, an optional `run` attribute (with `standalone` as the only valid value) is supported which forces the installation to execute this PIP in an isolated request, allowing a single, resource-heavy PIP to execute without encountering restrictions such as PHP’s `memory_limit` or `max_execution_time`:
Package Installation Plugins (PIPs) are interfaces to deploy and edit content as well as components.
-{% include callout.html content="For XML-based PIPs: `<![CDATA[]]>` must be used for language items and page contents. In all other cases it may only be used when necessary." type="info" %}
+!!! info "For XML-based PIPs: `<![CDATA[]]>` must be used for language items and page contents. In all other cases it may only be used when necessary."
## Built-In PIPs
Add templates for acp pages and forms by providing an archive containing the template files.
-{% include callout.html content="You cannot overwrite acp templates provided by other packages." type="warning" %}
+!!! warning "You cannot overwrite acp templates provided by other packages."
## Archive
### `<htmlopen>`
-{% include callout.html content="Optional: Must not be provided if the BBCode is being processed a PHP class (`<classname>`)." type="info" %}
+!!! info "Optional: Must not be provided if the BBCode is being processed a PHP class (`<classname>`)."
The contents of this tag are literally copied into the opening tag of the bbcode.
### `<htmlclose>`
-{% include callout.html content="Optional: Must not be provided if `<htmlopen>` is not given." type="info" %}
+!!! info "Optional: Must not be provided if `<htmlopen>` is not given."
Must match the `<htmlopen>` tag.
Do not provide for self-closing tags.
### `<buttonlabel>`
-{% include callout.html content="Optional: Must be provided if an icon is given." type="info" %}
+!!! info "Optional: Must be provided if an icon is given."
Explanatory text to show when hovering the icon.
#### `<html>`
-{% include callout.html content="Optional: Must not be provided if the BBCode is being processed a PHP class (`<classname>`)." type="info" %}
+!!! info "Optional: Must not be provided if the BBCode is being processed a PHP class (`<classname>`)."
The contents of this tag are copied into the opening tag of the bbcode.
`%s` is replaced by the attribute value.
#### `<usetext>`
<span class="label label-info">Optional</span>
-{% include callout.html content="Should only be set to `1` for the attribute with name `0`." type="info" %}
+!!! info "Should only be set to `1` for the attribute with name `0`."
Specifies whether the text content of the BBCode should become this attribute's value.
The listener class name is the name of the class which is triggered if the relevant event is fired.
The PHP class has to implement the `wcf\system\event\listener\IParameterizedEventListener` interface.
-{% include callout.html content="Legacy event listeners are only required to implement the deprecated `wcf\system\event\IEventListener` interface. When writing new code or update existing code, you should always implement the `wcf\system\event\listener\IParameterizedEventListener` interface!" type="warning" %}
+!!! warning "Legacy event listeners are only required to implement the deprecated `wcf\system\event\IEventListener` interface. When writing new code or update existing code, you should always implement the `wcf\system\event\listener\IParameterizedEventListener` interface!"
### `<inherit>`
Event listeners with smaller nice values are executed first.
If the nice value of two event listeners is equal, they are sorted by the listener class name.
-{% include callout.html content="If you pass a value out of the mentioned interval, the value will be adjusted to the closest value in the interval." type="info" %}
+!!! info "If you pass a value out of the mentioned interval, the value will be adjusted to the closest value in the interval."
### `<options>`
Adds any type of files with the exception of templates.
-{% include callout.html content="You cannot overwrite files provided by other packages." type="warning" %}
+!!! warning "You cannot overwrite files provided by other packages."
The `application` attribute behaves like it does for [acp templates](package_pip_acp-template.md#application).
parent: package_pip
---
-{% include callout.html content="Available since WoltLab Suite 3.1" type="info" %}
+!!! info "Available since WoltLab Suite 3.1"
Media providers are responsible to detect and convert links to a 3rd party service inside messages.
### `<className>`
-{% include callout.html content="`<className>` and `<html>` are mutually exclusive." type="warning" %}
+!!! warning "`<className>` and `<html>` are mutually exclusive."
PHP-Callback-Class that is invoked to process the matched link in case that additional logic must be applied that cannot be handled through a simple replacement as defined by the `<html>` element.
### `<html>`
-{% include callout.html content="`<className>` and `<html>` are mutually exclusive." type="warning" %}
+!!! warning "`<className>` and `<html>` are mutually exclusive."
Replacement HTML that gets populated using the captured matches in `<regex>`, variables are accessed as `{$VariableName}`. For example, the capture group `(?P<ID>...)` is accessed using `{$ID}`.
### `<selectoptions>`
<span class="label label-info">Optional</span>
-{% include callout.html content="Defined only for `select`, `multiSelect` and `radioButton` types." type="warning" %}
+!!! warning "Defined only for `select`, `multiSelect` and `radioButton` types."
Specifies a newline-separated list of selectable values.
Each line consists of an internal handle, followed by a colon (`:`, U+003A), followed by a language item.
### `<enableoptions>`
<span class="label label-info">Optional</span>
-{% include callout.html content="Defined only for `boolean`, `select` and `radioButton` types." type="warning" %}
+!!! warning "Defined only for `boolean`, `select` and `radioButton` types."
Specifies a comma-separated list of options which should be visually enabled when this option is enabled.
A leading exclamation mark (`!`, U+0021) will disable the specified option when this option is enabled.
### `<permissions>`
-{% include callout.html content="The comma represents a logical `or`, the check is successful if at least one permission is set." type="warning" %}
+!!! warning "The comma represents a logical `or`, the check is successful if at least one permission is set."
Comma separated list of permission names that will be checked one after another until at least one permission is set.
### `<options>`
-{% include callout.html content="The comma represents a logical `or`, the check is successful if at least one option is enabled." type="warning" %}
+!!! warning "The comma represents a logical `or`, the check is successful if at least one option is enabled."
Comma separated list of options that will be checked one after another until at least one option is set.
Each package installation plugin is described as an `<pip>` element with a `name` attribute and a PHP classname as the text content.
-{% include callout.html content="The package installation plugin’s class file must be installed into the `wcf` application and must not include classes outside the `\wcf\*` hierarchy to allow for proper uninstallation!" type="warning" %}
+!!! warning "The package installation plugin’s class file must be installed into the `wcf` application and must not include classes outside the `\wcf\*` hierarchy to allow for proper uninstallation!"
## Example
Execute arbitrary PHP code during installation, update and uninstallation of the package.
-{% include callout.html content="You must install the PHP script through the [file package installation plugin](package_pip_file.md)." type="warning" %}
+!!! warning "You must install the PHP script through the [file package installation plugin](package_pip_file.md)."
-{% include callout.html content="The installation will attempt to delete the script after successful execution." type="warning" %}
+!!! warning "The installation will attempt to delete the script after successful execution."
## Attributes
Execute SQL instructions using a MySQL-flavored syntax.
-{% include callout.html content="This file is parsed by WoltLab Suite Core to allow reverting of certain changes, but not every syntax MySQL supports is recognized by the parser. To avoid any troubles, you should always use statements relying on the SQL standard." type="warning" %}
+!!! warning "This file is parsed by WoltLab Suite Core to allow reverting of certain changes, but not every syntax MySQL supports is recognized by the parser. To avoid any troubles, you should always use statements relying on the SQL standard."
## Expected Value
The original template is not modified.
If multiple template listeners listen to a single event their output is concatenated using the line feed character (`\n`, U+000A) in the order defined by the [`niceValue`](#niceValue).
-{% include callout.html content="It is recommend that the only code is an `{include}` of a template to enable changes by the administrator. Names of templates included by a template listener start with two underscores by convention." type="warning" %}
+!!! warning "It is recommend that the only code is an `{include}` of a template to enable changes by the administrator. Names of templates included by a template listener start with two underscores by convention."
### `<environment>`
Template listeners with smaller nice values are executed first.
If the nice value of two template listeners is equal, the order is undefined.
-{% include callout.html content="If you pass a value out of the mentioned interval, the value will be adjusted to the closest value in the interval." type="info" %}
+!!! info "If you pass a value out of the mentioned interval, the value will be adjusted to the closest value in the interval."
### `<options>`
Add templates for frontend pages and forms by providing an archive containing the template files.
-{% include callout.html content="You cannot overwrite templates provided by other packages." type="warning" %}
+!!! warning "You cannot overwrite templates provided by other packages."
This package installation plugin behaves exactly like the [acpTemplate package installation plugin](package_pip_acp-template.md) except for installing frontend templates instead of backend/acp templates.
### `<presetmailnotificationtype>`
-{% include callout.html content="Avoid using this option, as sending unsolicited mail can be seen as spamming." type="info" %}
+!!! info "Avoid using this option, as sending unsolicited mail can be seen as spamming."
One of `instant` or `daily`.
Defines whether this type of email notifications is enabled by default.
Cronjobs offer an easy way to execute actions periodically, like cleaning up the database.
-{% include callout.html content="The execution of cronjobs is not guaranteed but requires someone to access the page with JavaScript enabled." type="warning" %}
+!!! warning "The execution of cronjobs is not guaranteed but requires someone to access the page with JavaScript enabled."
This page focuses on the technical aspects of cronjobs, [the cronjob package installation plugin page](package_pip_cronjob.md) covers how you can actually register a cronjob.
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`.
-{% include callout.html content="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." type="info" %}
+!!! 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`
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.
-{% include callout.html content="When creating the container object, its id has to be the id of the form field that will manage the actual text." type="warning" %}
+!!! 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:
`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.
- {% include callout.html content="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`." type="warning" %}
+ !!! 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](php_api_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.
The basis for all three elements are form nodes.
-{% include callout.html content="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." type="info" %}
+!!! 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
`CustomFormNode` is a form node whose contents can be set directly via `content($content)`.
-{% include callout.html content="This class should generally not be relied on. Instead, `TemplateFormNode` should be used." type="warning" %}
+!!! warning "This class should generally not be relied on. Instead, `TemplateFormNode` should be used."
### `TemplateFormNode`
If multilingual input is enabled for a specific form field, classes using `TI18nFormField` register a [custom form field data processor](php_api_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.
-{% include callout.html content="`TI18nFormField` already provides a default implementation of `IFormField::validate()`." type="info" %}
+!!! info "`TI18nFormField` already provides a default implementation of `IFormField::validate()`."
#### `IImmutableFormField` / `TImmutableFormField`
A maximum of `null` signals that no maximum value has been set.
`TMaximumFormField` provides a default implementation of these two methods.
-{% include callout.html content="The implementing class has to validate the entered value against the maximum value manually." type="warning" %}
+!!! warning "The implementing class has to validate the entered value against the maximum value manually."
#### `IMaximumLengthFormField` / `TMaximumLengthFormField`
A maximum length of `null` signals that no maximum length has been set.
`TMaximumLengthFormField` provides a default implementation of these two methods.
-{% include callout.html content="The implementing class has to validate the entered value against the maximum value manually by calling `validateMaximumLength()`." type="warning" %}
+!!! warning "The implementing class has to validate the entered value against the maximum value manually by calling `validateMaximumLength()`."
#### `IMinimumFormField` / `TMinimumFormField`
A minimum of `null` signals that no minimum value has been set.
`TMinimumFormField` provides a default implementation of these three methods.
-{% include callout.html content="The implementing class has to validate the entered value against the minimum value manually." type="warning" %}
+!!! warning "The implementing class has to validate the entered value against the minimum value manually."
#### `IMinimumLengthFormField` / `TMinimumLengthFormField`
A minimum length of `null` signals that no minimum length has been set.
`TMinimumLengthFormField` provides a default implementation of these three methods.
-{% include callout.html content="The implementing class has to validate the entered value against the minimum value manually by calling `validateMinimumLength()`." type="warning" %}
+!!! warning "The implementing class has to validate the entered value against the minimum value manually by calling `validateMinimumLength()`."
#### `IMultipleFormField` / `TMultipleFormField`
`TMultipleFormField` provides a default implementation of these six methods and classes using `TMultipleFormField` register a [custom form field data processor](php_api_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.
-{% include callout.html content="The implementing class has to validate the values against the minimum and maximum number of values manually." type="warning" %}
+!!! warning "The implementing class has to validate the values against the minimum and maximum number of values manually."
#### `INullableFormField` / `TNullableFormField`
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.
-{% include callout.html content="`IFormDataProcessor` should not be implemented directly. Instead, `AbstractFormDataProcessor` should be extended." type="warning" %}
+!!! warning "`IFormDataProcessor` should not be implemented directly. Instead, `AbstractFormDataProcessor` should be extended."
-{% include callout.html content="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." type="info" %}
+!!! 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."
# Form Builder
-{% include callout.html content="Form builder is only available since WoltLab Suite Core 5.2." type="info" %}
+!!! info "Form builder is only available since WoltLab Suite Core 5.2."
-{% include callout.html content="The [migration guide for WoltLab Suite Core 5.2](migration_wsc-31_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." type="info" %}
+!!! info "The [migration guide for WoltLab Suite Core 5.2](migration_wsc-31_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
1. [Form validation and form data](php_api_form_builder-validation_data.md)
1. [Form node dependencies](php_api_form_builder-dependencies.md)
-{% include callout.html content="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." type="warning" %}
+!!! 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`
# Sitemaps
-{% include callout.html content="This feature is available with WoltLab Suite 3.1 or newer only." type="warning" %}
+!!! warning "This feature is available with WoltLab Suite 3.1 or newer only."
Since version 3.1, WoltLab Suite Core is capable of automatically creating a sitemap.
This sitemap contains all static pages registered via the page package installation plugin and which may be indexed by search engines (checking the `allowSpidersToIndex` parameter and page permissions) and do not expect an object ID.
## Creating an App
-{% include callout.html content="This is a non-reversible operation! Once a package has been installed, its type cannot be changed without uninstalling and reinstalling the entire package, an app will always be an app and vice versa." type="danger" %}
+!!! danger "This is a non-reversible operation! Once a package has been installed, its type cannot be changed without uninstalling and reinstalling the entire package, an app will always be an app and vice versa."
### `package.xml`
# Code Style
-{% include callout.html content="The following code style conventions are used by us for our own packages. While you do not have to follow every rule, you are encouraged to do so." type="info" %}
+!!! info "The following code style conventions are used by us for our own packages. While you do not have to follow every rule, you are encouraged to do so."
For information about how to document your code, please refer to the [documentation page](php_code-style_documentation.md).
parent: php_code-style
---
-{% include callout.html content="The following documentation conventions are used by us for our own packages. While you do not have to follow every rule, you are encouraged to do so." type="info" %}
+!!! info "The following documentation conventions are used by us for our own packages. While you do not have to follow every rule, you are encouraged to do so."
## Database Objects
### Fetching a Single Result
-{% include callout.html content="Do not attempt to use `fetchSingleRow()` or `fetchSingleColumn()` if the result contains more than one row." type="danger" %}
+!!! danger "Do not attempt to use `fetchSingleRow()` or `fetchSingleColumn()` if the result contains more than one row."
You can opt-in to retrieve only a single row from database and make use of shortcut methods to reduce the code that you have to write.
### Fetch by Column
-{% include callout.html content="There is no way to return another column from the same row if you use `fetchColumn()` to retrieve data." type="warning" %}
+!!! warning "There is no way to return another column from the same row if you use `fetchColumn()` to retrieve data."
Fetching an array is only useful if there is going to be more than one column per result row, otherwise accessing the column directly is much more convenient and increases the code readability.
`$map` is a one-dimensional array where each `exampleID` value maps to the corresponding `userID` value.
-{% include callout.html content="If there are multiple entries for a certain `exampleID` value with different `userID` values, the existing entry in the array will be overwritten and contain the last read value from the database table. Therefore, this method should generally only be used for unique combinations." type="warning" %}
+!!! warning "If there are multiple entries for a certain `exampleID` value with different `userID` values, the existing entry in the array will be overwritten and contain the last read value from the database table. Therefore, this method should generally only be used for unique combinations."
If you do not have a combination of columns with unique pairs of values, but you want to get a list of `userID` values with the same `exampleID`, you can set the third parameter of `fetchMap()` to `false` and get a list:
## DatabaseObjectEditor
-{% include callout.html content="This is the low-level interface to manipulate data rows, it is recommended to use `AbstractDatabaseObjectAction`." type="info" %}
+!!! info "This is the low-level interface to manipulate data rows, it is recommended to use `AbstractDatabaseObjectAction`."
Adding, editing and deleting models is done using the `DatabaseObjectEditor` class that decorates a `DatabaseObject` and uses its data to perform the actions.
### Updating an existing row
-{% include callout.html content="The internal state of the decorated `DatabaseObject` is not altered at any point, the values will still be the same after editing or deleting the represented row. If you need an object with the latest data, you'll have to discard the current object and refetch the data from database." type="warning" %}
+!!! warning "The internal state of the decorated `DatabaseObject` is not altered at any point, the values will still be the same after editing or deleting the represented row. If you need an object with the latest data, you'll have to discard the current object and refetch the data from database."
```php
<?php
### Deleting a row
-{% include callout.html content="Similar to the update process, the decorated `DatabaseObject` is not altered and will then point to an inexistent row." type="warning" %}
+!!! warning "Similar to the update process, the decorated `DatabaseObject` is not altered and will then point to an inexistent row."
```php
<?php
### Executing an Action
-{% include callout.html content="The method `AbstractDatabaseObjectAction::validateAction()` is internally used for AJAX method invocation and must not be called programmatically." type="warning" %}
+!!! warning "The method `AbstractDatabaseObjectAction::validateAction()` is internally used for AJAX method invocation and must not be called programmatically."
The next example represents the same functionality as seen for `DatabaseObjectEditor`:
## Custom Exceptions
-{% include callout.html content="Do not use `wcf\system\exception\SystemException` anymore, use specific exception classes!" type="warning" %}
+!!! warning "Do not use `wcf\system\exception\SystemException` anymore, use specific exception classes!"
The following table contains a list of custom exceptions that are commonly used.
#### submit()
-{% include callout.html content="The methods `submit()` up until `save()` are only invoked if either `$_POST` or `$_FILES` are not empty, otherwise they won't be invoked and the execution will continue with `readData()`." type="warning" %}
+!!! warning "The methods `submit()` up until `save()` are only invoked if either `$_POST` or `$_FILES` are not empty, otherwise they won't be invoked and the execution will continue with `readData()`."
This is an internal method that is responsible of input processing and validation.
#### saved()
-{% include callout.html content="This method is not called automatically and must be invoked manually by executing `$this->saved()` inside `save()`." type="warning" %}
+!!! warning "This method is not called automatically and must be invoked manually by executing `$this->saved()` inside `save()`."
The only purpose of this method is to fire the event `saved` that signals that the form data has been processed successfully and data has been saved. It is somewhat special as it is dispatched after the data has been saved, but before the data is purged during form reset. This is by default the last event that has access to the processed data.
The data properties of `PersonAddForm` are populated with the data of the edited person so that this data is shown in the form for the initial request.
1. `save()` handles saving the changed data.
- {% include callout.html content="Do not call `parent::save()` because that would cause `PersonAddForm::save()` to be executed and thus a new person would to be created! In order for the `save` event to be fired, call `AbstractForm::save()` instead!" type="warning" %}
+ !!! warning "Do not call `parent::save()` because that would cause `PersonAddForm::save()` to be executed and thus a new person would to be created! In order for the `save` event to be fired, call `AbstractForm::save()` instead!"
The only differences compared to `PersonAddForm::save()` are that we pass the edited object to the `PersonAction` constructor, execute the `update` action instead of the `create` action and do not clear the input fields after saving the changes.
1. In `assignVariables()`, we assign the edited `Person` object to the template, which is required to create the link in the form’s action property.
Furthermore, we assign the template variable `$action` `edit` as value.
- {% include callout.html content="After calling `parent::assignVariables()`, the template variable `$action` actually has the value `add` so that here, we are overwriting this already assigned value." type="info" %}
+ !!! info "After calling `parent::assignVariables()`, the template variable `$action` actually has the value `add` so that here, we are overwriting this already assigned value."
## Frontend
{% include tutorial/tutorial-series/part-2/files/lib/system/event/listener/BirthdaySortFieldPersonListPageListener.class.php %}
{% endhighlight %}
-{% include callout.html content="We use `SortablePage` as a type hint instead of `wcf\acp\page\PersonListPage` because we will be using the same event listener class in the front end to also allow sorting that list by birthday." type="info" %}
+!!! info "We use `SortablePage` as a type hint instead of `wcf\acp\page\PersonListPage` because we will be using the same event listener class in the front end to also allow sorting that list by birthday."
As the relevant template codes are only one line each, we will simply put them directly in the `templateListener.xml` file that will be shown [later on](#templatelistenerxml).
The code for the table head is similar to the other `th` elements:
{% include tutorial/tutorial-series/part-2/templates/__personListBirthdaySortField.tpl %}
{% endhighlight %}
-{% include callout.html content="You might have noticed the two underscores at the beginning of the template file. For templates that are included via template listeners, this is the naming convention we use." type="info" %}
+!!! info "You might have noticed the two underscores at the beginning of the template file. For templates that are included via template listeners, this is the naming convention we use."
Putting the template code into a file has the advantage that in the administrator is able to edit the code directly via a custom template group, even though in this case this might not be very probable.
└── userGroupOption.xml
```
-{% include callout.html content="We will not mention every code change between the first part and this part, as we only want to focus on the important, new parts of the code. For example, there is a new `Person::getLink()` method and new language items have been added. For all changes, please refer to the [source code on GitHub](https://github.com/WoltLab/woltlab.github.io/tree/master/_includes/tutorial/tutorial-series/part-3)." type="warning" %}
+!!! warning "We will not mention every code change between the first part and this part, as we only want to focus on the important, new parts of the code. For example, there is a new `Person::getLink()` method and new language items have been added. For all changes, please refer to the [source code on GitHub](https://github.com/WoltLab/woltlab.github.io/tree/master/_includes/tutorial/tutorial-series/part-3)."
## Runtime Cache
Additionally, we have added a new `enableComments` database table column to the `wcf1_person` database table whose value can be set when creating or editing a person in the ACP.
With this option, comments on individual people can be disabled.
-{% include callout.html content="Liking comments is already built-in and only requires some extra code in the `PersonPage` class for showing the likes of pre-loaded comments." type="info" %}
+!!! info "Liking comments is already built-in and only requires some extra code in the `PersonPage` class for showing the likes of pre-loaded comments."
## Person Page
### Available Breakpoints
-{% include callout.html content="Some very large smartphones, for example the Apple iPhone 7 Plus, do match the media query for `Tablets (portrait)` when viewed in landscape mode." type="info" %}
+!!! info "Some very large smartphones, for example the Apple iPhone 7 Plus, do match the media query for `Tablets (portrait)` when viewed in landscape mode."
| Name | Devices | `@media` equivalent |
|-------|-------|-------|
### `wcf.date.dateFormat`
-{% include callout.html content="Many characters in the format have a special meaning and will be replaced with date fragments. If you want to include a literal character, you'll have to use the backslash `\` as an escape sequence to indicate that the character should be output as-is rather than being replaced. For example, `Y-m-d` will be output as `2018-03-30`, but `\Y-m-d` will result in `Y-03-30`." type="warning" %}
+!!! warning "Many characters in the format have a special meaning and will be replaced with date fragments. If you want to include a literal character, you'll have to use the backslash `\` as an escape sequence to indicate that the character should be output as-is rather than being replaced. For example, `Y-m-d` will be output as `2018-03-30`, but `\Y-m-d` will result in `Y-03-30`."
_Defaults to `M jS Y`._
{cycle name=fooCycle} {* prints 'bar' *}
```
-{% include callout.html content="The values attribute only has to be present for the first call. If `cycle` is used in a loop, the presence of the same values in consecutive calls has no effect. Only once the values change, the cycle is reset." type="info" %}
+!!! info "The values attribute only has to be present for the first call. If `cycle` is used in a loop, the presence of the same values in consecutive calls has no effect. Only once the values change, the cycle is reset."
| Attribute | Description |
|-----------|-------------|
If you follow this convention, WoltLab Suite Core will automatically determine the template name so that you do not have to explicitly set it.
-{% include callout.html content="For forms that handle creating and editing objects, in general, there are two form classes: `FooAddForm` and `FooEditForm`. WoltLab Suite Core, however, generally only uses one template `fooAdd.tpl` and the template variable `$action` to distinguish between creating a new object (`$action = 'add'`) and editing an existing object (`$action = 'edit'`) as the differences between templates for adding and editing an object are minimal." type="info" %}
+!!! info "For forms that handle creating and editing objects, in general, there are two form classes: `FooAddForm` and `FooEditForm`. WoltLab Suite Core, however, generally only uses one template `fooAdd.tpl` and the template variable `$action` to distinguish between creating a new object (`$action = 'add'`) and editing an existing object (`$action = 'edit'`) as the differences between templates for adding and editing an object are minimal."
### Forms
-{% include callout.html content="For new forms, use the new [form builder API](php_api_form_builder.md) introduced with WoltLab Suite 5.2." type="info" %}
+!!! info "For new forms, use the new [form builder API](php_api_form_builder.md) introduced with WoltLab Suite 5.2."
```smarty
<form method="post" action="{link controller='FooBar'}{/link}">
comment *}
```
-{% include callout.html content="The template compiler discards the comments, so that they not included in the compiled template." type="info" %}
+!!! info "The template compiler discards the comments, so that they not included in the compiled template."
### Conditions