Updated to Drupal 8.6.4, which is PHP 7.3 friendly. Also updated HTMLaw library....
[yaffs-website] / web / core / lib / Drupal / Core / Render / Element / FormElement.php
1 <?php
2
3 namespace Drupal\Core\Render\Element;
4
5 use Drupal\Core\Form\FormStateInterface;
6 use Drupal\Core\Render\BubbleableMetadata;
7 use Drupal\Core\Url;
8
9 /**
10  * Provides a base class for form element plugins.
11  *
12  * Form elements are a subset of render elements, representing elements for
13  * HTML forms, which can be referenced in form arrays. See the
14  * @link theme_render Render API topic @endlink for an overview of render
15  * arrays and render elements, and the @link form_api Form API topic @endlink
16  * for an overview of forms and form arrays.
17  *
18  * The elements of form arrays are divided up into properties (whose keys
19  * start with #) and children (whose keys do not start with #). The properties
20  * provide data or settings that are used in rendering and form processing.
21  * Some properties are specific to a particular type of form/render element,
22  * some are available for any render element, and some are available for any
23  * form input element. A list of the properties that are available for all form
24  * elements follows; see \Drupal\Core\Render\Element\RenderElement for some
25  * additional information, as well as a list of properties that are common to
26  * all render elements (including form elements). Properties specific to a
27  * particular element are documented on that element's class.
28  *
29  * Here is a list of properties that are used during the rendering and form
30  * processing of form elements:
31  * - #after_build: (array) Array of callables or function names, which are
32  *   called after the element is built. Arguments: $element, $form_state.
33  * - #ajax: (array) Array of elements to specify Ajax behavior. See
34  *   the @link ajax Ajax API topic @endlink for more information.
35  * - #array_parents: (string[], read-only) Array of names of all the element's
36  *   parents (including itself) in the render array. See also #parents, #tree.
37  * - #default_value: Default value for the element. See also #value.
38  * - #description: (string) Help or description text for the element. In an
39  *   ideal user interface, the #title should be enough to describe the element,
40  *   so most elements should not have a description; if you do need one, make
41  *   sure it is translated. If it is not already wrapped in a safe markup
42  *   object, it will be filtered for XSS safety.
43  * - #disabled: (bool) If TRUE, the element is shown but does not accept
44  *   user input.
45  * - #element_validate: (array) Array of callables or function names, which
46  *   are called to validate the input. Arguments: $element, $form_state, $form.
47  * - #field_prefix: (string) Prefix to display before the HTML input element.
48  *   Should be translated, normally. If it is not already wrapped in a safe
49  *   markup object, will be filtered for XSS safety.
50  * - #field_suffix: (string) Suffix to display after the HTML input element.
51  *   Should be translated, normally. If it is not already wrapped in a safe
52  *   markup object, will be filtered for XSS safety.
53  * - #input: (bool, internal) Whether or not the element accepts input.
54  * - #parents: (string[], read-only) Array of names of the element's parents
55  *   for purposes of getting values out of $form_state. See also
56  *   #array_parents, #tree.
57  * - #process: (array) Array of callables or function names, which are
58  *   called during form building. Arguments: $element, $form_state, $form.
59  * - #processed: (bool, internal) Set to TRUE when the element is processed.
60  * - #required: (bool) Whether or not input is required on the element.
61  * - #states: (array) Information about JavaScript states, such as when to
62  *   hide or show the element based on input on other elements.
63  *   See drupal_process_states() for documentation.
64  * - #title: (string) Title of the form element. Should be translated.
65  * - #title_display: (string) Where and how to display the #title. Possible
66  *   values:
67  *   - before: Label goes before the element (default for most elements).
68  *   - after: Label goes after the element (default for radio elements).
69  *   - invisible: Label is there but is made invisible using CSS.
70  *   - attribute: Make it the title attribute (hover tooltip).
71  * - #tree: (bool) TRUE if the values of this element and its children should
72  *   be hierarchical in $form_state; FALSE if the values should be flat.
73  *   See also #parents, #array_parents.
74  * - #value_callback: (callable) Callable or function name, which is called
75  *   to transform the raw user input to the element's value. Arguments:
76  *   $element, $input, $form_state.
77  *
78  * @see \Drupal\Core\Render\Annotation\FormElement
79  * @see \Drupal\Core\Render\Element\FormElementInterface
80  * @see \Drupal\Core\Render\ElementInfoManager
81  * @see plugin_api
82  *
83  * @ingroup theme_render
84  */
85 abstract class FormElement extends RenderElement implements FormElementInterface {
86
87   /**
88    * {@inheritdoc}
89    */
90   public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
91     return NULL;
92   }
93
94   /**
95    * #process callback for #pattern form element property.
96    *
97    * @param array $element
98    *   An associative array containing the properties and children of the
99    *   generic input element.
100    * @param \Drupal\Core\Form\FormStateInterface $form_state
101    *   The current state of the form.
102    * @param array $complete_form
103    *   The complete form structure.
104    *
105    * @return array
106    *   The processed element.
107    */
108   public static function processPattern(&$element, FormStateInterface $form_state, &$complete_form) {
109     if (isset($element['#pattern']) && !isset($element['#attributes']['pattern'])) {
110       $element['#attributes']['pattern'] = $element['#pattern'];
111       $element['#element_validate'][] = [get_called_class(), 'validatePattern'];
112     }
113
114     return $element;
115   }
116
117   /**
118    * #element_validate callback for #pattern form element property.
119    *
120    * @param $element
121    *   An associative array containing the properties and children of the
122    *   generic form element.
123    * @param $form_state
124    *   The current state of the form.
125    * @param array $complete_form
126    *   The complete form structure.
127    */
128   public static function validatePattern(&$element, FormStateInterface $form_state, &$complete_form) {
129     if ($element['#value'] !== '') {
130       // The pattern must match the entire string and should have the same
131       // behavior as the RegExp object in ECMA 262.
132       // - Use bracket-style delimiters to avoid introducing a special delimiter
133       //   character like '/' that would have to be escaped.
134       // - Put in brackets so that the pattern can't interfere with what's
135       //   prepended and appended.
136       $pattern = '{^(?:' . $element['#pattern'] . ')$}';
137
138       if (!preg_match($pattern, $element['#value'])) {
139         $form_state->setError($element, t('%name field is not in the right format.', ['%name' => $element['#title']]));
140       }
141     }
142   }
143
144   /**
145    * Adds autocomplete functionality to elements.
146    *
147    * This sets up autocomplete functionality for elements with an
148    * #autocomplete_route_name property, using the #autocomplete_route_parameters
149    * property if present.
150    *
151    * For example, suppose your autocomplete route name is
152    * 'mymodule.autocomplete' and its path is
153    * '/mymodule/autocomplete/{a}/{b}'. In a form array, you would create a text
154    * field with properties:
155    * @code
156    * '#autocomplete_route_name' => 'mymodule.autocomplete',
157    * '#autocomplete_route_parameters' => array('a' => $some_key, 'b' => $some_id),
158    * @endcode
159    * If the user types "keywords" in that field, the full path called would be:
160    * 'mymodule_autocomplete/$some_key/$some_id?q=keywords'
161    *
162    * @param array $element
163    *   The form element to process. Properties used:
164    *   - #autocomplete_route_name: A route to be used as callback URL by the
165    *     autocomplete JavaScript library.
166    *   - #autocomplete_route_parameters: The parameters to be used in
167    *     conjunction with the route name.
168    * @param \Drupal\Core\Form\FormStateInterface $form_state
169    *   The current state of the form.
170    * @param array $complete_form
171    *   The complete form structure.
172    *
173    * @return array
174    *   The form element.
175    */
176   public static function processAutocomplete(&$element, FormStateInterface $form_state, &$complete_form) {
177     $url = NULL;
178     $access = FALSE;
179
180     if (!empty($element['#autocomplete_route_name'])) {
181       $parameters = isset($element['#autocomplete_route_parameters']) ? $element['#autocomplete_route_parameters'] : [];
182       $url = Url::fromRoute($element['#autocomplete_route_name'], $parameters)->toString(TRUE);
183       /** @var \Drupal\Core\Access\AccessManagerInterface $access_manager */
184       $access_manager = \Drupal::service('access_manager');
185       $access = $access_manager->checkNamedRoute($element['#autocomplete_route_name'], $parameters, \Drupal::currentUser(), TRUE);
186     }
187
188     if ($access) {
189       $metadata = BubbleableMetadata::createFromRenderArray($element);
190       if ($access->isAllowed()) {
191         $element['#attributes']['class'][] = 'form-autocomplete';
192         $metadata->addAttachments(['library' => ['core/drupal.autocomplete']]);
193         // Provide a data attribute for the JavaScript behavior to bind to.
194         $element['#attributes']['data-autocomplete-path'] = $url->getGeneratedUrl();
195         $metadata = $metadata->merge($url);
196       }
197       $metadata
198         ->merge(BubbleableMetadata::createFromObject($access))
199         ->applyTo($element);
200     }
201
202     return $element;
203   }
204
205 }