diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc index 7ab2059..56ce402 100644 --- a/core/includes/ajax.inc +++ b/core/includes/ajax.inc @@ -237,11 +237,7 @@ * @see ajax_pre_render_element() */ function ajax_process_form($element, &$form_state) { - $element = ajax_pre_render_element($element); - if (!empty($element['#ajax_processed'])) { - $form_state['cache'] = TRUE; - } - return $element; + return \Drupal\Core\Render\Element\Textfield::processAjaxForm($element, $form_state); } /** diff --git a/core/includes/form.inc b/core/includes/form.inc index de7bc39..313ad06 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -1439,12 +1439,7 @@ function form_pre_render_actions_dropbutton(array $element) { * @see form_validate_pattern() */ function form_process_pattern($element, &$form_state) { - if (isset($element['#pattern']) && !isset($element['#attributes']['pattern'])) { - $element['#attributes']['pattern'] = $element['#pattern']; - $element['#element_validate'][] = 'form_validate_pattern'; - } - - return $element; + return \Drupal\Core\Render\Element\Textfield::processPattern($element, $form_state); } /** @@ -1989,21 +1984,7 @@ function form_validate_machine_name(&$element, &$form_state) { * The processed element. */ function form_process_group(&$element, &$form_state) { - $parents = implode('][', $element['#parents']); - - // Each details element forms a new group. The #type 'vertical_tabs' basically - // only injects a new details element. - $form_state['groups'][$parents]['#group_exists'] = TRUE; - $element['#groups'] = &$form_state['groups']; - - // Process vertical tabs group member details elements. - if (isset($element['#group'])) { - // Add this details element to the defined group (by reference). - $group = $element['#group']; - $form_state['groups'][$group][] = &$element; - } - - return $element; + return \Drupal\Core\Render\Element\Textfield::processGroup($element, $form_state); } /** @@ -2051,53 +2032,7 @@ function form_pre_render_details($element) { * The modified element with all group members. */ function form_pre_render_group($element) { - // The element may be rendered outside of a Form API context. - if (!isset($element['#parents']) || !isset($element['#groups'])) { - return $element; - } - - // Inject group member elements belonging to this group. - $parents = implode('][', $element['#parents']); - $children = Element::children($element['#groups'][$parents]); - if (!empty($children)) { - foreach ($children as $key) { - // Break references and indicate that the element should be rendered as - // group member. - $child = (array) $element['#groups'][$parents][$key]; - $child['#group_details'] = TRUE; - // Inject the element as new child element. - $element[] = $child; - - $sort = TRUE; - } - // Re-sort the element's children if we injected group member elements. - if (isset($sort)) { - $element['#sorted'] = FALSE; - } - } - - if (isset($element['#group'])) { - // Contains form element summary functionalities. - $element['#attached']['library'][] = 'core/drupal.form'; - - $group = $element['#group']; - // If this element belongs to a group, but the group-holding element does - // not exist, we need to render it (at its original location). - if (!isset($element['#groups'][$group]['#group_exists'])) { - // Intentionally empty to clarify the flow; we simply return $element. - } - // If we injected this element into the group, then we want to render it. - elseif (!empty($element['#group_details'])) { - // Intentionally empty to clarify the flow; we simply return $element. - } - // Otherwise, this element belongs to a group and the group exists, so we do - // not render it. - elseif (Element::children($element['#groups'][$group])) { - $element['#printed'] = TRUE; - } - } - - return $element; + return \Drupal\Core\Render\Element\Textfield::preRenderGroup($element); } /** @@ -2208,20 +2143,7 @@ function template_preprocess_vertical_tabs(&$variables) { * The form element. */ function form_process_autocomplete($element, &$form_state) { - $access = FALSE; - if (!empty($element['#autocomplete_route_name'])) { - $parameters = isset($element['#autocomplete_route_parameters']) ? $element['#autocomplete_route_parameters'] : array(); - - $path = \Drupal::urlGenerator()->generate($element['#autocomplete_route_name'], $parameters); - $access = \Drupal::service('access_manager')->checkNamedRoute($element['#autocomplete_route_name'], $parameters, \Drupal::currentUser()); - } - if ($access) { - $element['#attributes']['class'][] = 'form-autocomplete'; - $element['#attached']['library'][] = 'core/drupal.autocomplete'; - // Provide a data attribute for the JavaScript behavior to bind to. - $element['#attributes']['data-autocomplete-path'] = $path; - } - return $element; + return \Drupal\Core\Render\Element\Textfield::processAutocomplete($element, $form_state); } /** @@ -2336,11 +2258,7 @@ function form_pre_render_hidden($element) { * The $element with prepared variables ready for theme_input(). */ function form_pre_render_textfield($element) { - $element['#attributes']['type'] = 'text'; - Element::setAttributes($element, array('id', 'name', 'value', 'size', 'maxlength', 'placeholder')); - _form_set_attributes($element, array('form-text')); - - return $element; + return \Drupal\Core\Render\Element\Textfield::preRenderTextfield($element); } /** diff --git a/core/lib/Drupal/Core/Render/Element/Textfield.php b/core/lib/Drupal/Core/Render/Element/Textfield.php index c3828c3..be9a870 100644 --- a/core/lib/Drupal/Core/Render/Element/Textfield.php +++ b/core/lib/Drupal/Core/Render/Element/Textfield.php @@ -7,29 +7,124 @@ namespace Drupal\Core\Render\Element; -use Drupal\Core\Render\ElementBase; +use Drupal\Core\Render\Element; +use Drupal\Core\Render\FormElementBase; /** * Formats a single-line text field. * * @FormElement("textfield") */ -class Textfield extends ElementBase { +class Textfield extends FormElementBase { /** * {@inheritdoc} */ public function getInfo() { + $class = get_class($this); return array( '#input' => TRUE, '#size' => 60, '#maxlength' => 128, '#autocomplete_route_name' => FALSE, - '#process' => array('form_process_autocomplete', 'ajax_process_form', 'form_process_pattern', 'form_process_group'), - '#pre_render' => array('form_pre_render_textfield', 'form_pre_render_group'), + '#process' => array( + array($class, 'processAutocomplete'), + array($class, 'processAjaxForm'), + array($class, 'processPattern'), + array($class, 'processGroup'), + ), + '#pre_render' => array( + array($class, 'preRenderTextfield'), + array($class, 'preRenderGroup'), + ), '#theme' => 'input__textfield', '#theme_wrappers' => array('form_element'), ); } + /** + * #process callback for #pattern form element property. + * + * @param $element + * An associative array containing the properties and children of the + * generic input element. + * @param $form_state + * The $form_state array for the form this element belongs to. + * + * @return + * The processed element. + * + * @see form_validate_pattern() + */ + public static function processPattern($element, &$form_state) { + if (isset($element['#pattern']) && !isset($element['#attributes']['pattern'])) { + $element['#attributes']['pattern'] = $element['#pattern']; + $element['#element_validate'][] = 'form_validate_pattern'; + } + + return $element; + } + + /** + * Adds autocomplete functionality to elements with a valid + * #autocomplete_route_name. + * + * Suppose your autocomplete route name is 'mymodule.autocomplete' and its path + * is: '/mymodule/autocomplete/{a}/{b}' + * In your form you have: + * @code + * '#autocomplete_route_name' => 'mymodule.autocomplete', + * '#autocomplete_route_parameters' => array('a' => $some_key, 'b' => $some_id), + * @endcode + * The user types in "keywords" so the full path called is: + * 'mymodule_autocomplete/$some_key/$some_id?q=keywords' + * + * @param array $element + * The form element to process. Properties used: + * - #autocomplete_route_name: A route to be used as callback URL by the + * autocomplete JavaScript library. + * - #autocomplete_route_parameters: The parameters to be used in conjunction + * with the route name. + * @param array $form_state + * An associative array containing the current state of the form. + * + * @return array + * The form element. + */ + public static function processAutocomplete($element, &$form_state) { + $access = FALSE; + if (!empty($element['#autocomplete_route_name'])) { + $parameters = isset($element['#autocomplete_route_parameters']) ? $element['#autocomplete_route_parameters'] : array(); + + $path = \Drupal::urlGenerator()->generate($element['#autocomplete_route_name'], $parameters); + $access = \Drupal::service('access_manager')->checkNamedRoute($element['#autocomplete_route_name'], $parameters, \Drupal::currentUser()); + } + if ($access) { + $element['#attributes']['class'][] = 'form-autocomplete'; + $element['#attached']['library'][] = 'core/drupal.autocomplete'; + // Provide a data attribute for the JavaScript behavior to bind to. + $element['#attributes']['data-autocomplete-path'] = $path; + } + return $element; + } + + /** + * Prepares a #type 'textfield' render element for theme_input(). + * + * @param array $element + * An associative array containing the properties of the element. + * Properties used: #title, #value, #description, #size, #maxlength, + * #placeholder, #required, #attributes. + * + * @return array + * The $element with prepared variables ready for theme_input(). + */ + public static function preRenderTextfield($element) { + $element['#attributes']['type'] = 'text'; + Element::setAttributes($element, array('id', 'name', 'value', 'size', 'maxlength', 'placeholder')); + _form_set_attributes($element, array('form-text')); + + return $element; + } + } diff --git a/core/lib/Drupal/Core/Render/ElementBase.php b/core/lib/Drupal/Core/Render/ElementBase.php index f7f2362..9061aed 100644 --- a/core/lib/Drupal/Core/Render/ElementBase.php +++ b/core/lib/Drupal/Core/Render/ElementBase.php @@ -20,4 +20,64 @@ */ abstract class ElementBase extends PluginBase implements ElementInterface { + /** + * Adds members of this group as actual elements for rendering. + * + * @param array $element + * An associative array containing the properties and children of the + * element. + * + * @return array + * The modified element with all group members. + */ + public static function preRenderGroup($element) { + // The element may be rendered outside of a Form API context. + if (!isset($element['#parents']) || !isset($element['#groups'])) { + return $element; + } + + // Inject group member elements belonging to this group. + $parents = implode('][', $element['#parents']); + $children = Element::children($element['#groups'][$parents]); + if (!empty($children)) { + foreach ($children as $key) { + // Break references and indicate that the element should be rendered as + // group member. + $child = (array) $element['#groups'][$parents][$key]; + $child['#group_details'] = TRUE; + // Inject the element as new child element. + $element[] = $child; + + $sort = TRUE; + } + // Re-sort the element's children if we injected group member elements. + if (isset($sort)) { + $element['#sorted'] = FALSE; + } + } + + if (isset($element['#group'])) { + // Contains form element summary functionalities. + $element['#attached']['library'][] = 'core/drupal.form'; + + $group = $element['#group']; + // If this element belongs to a group, but the group-holding element does + // not exist, we need to render it (at its original location). + if (!isset($element['#groups'][$group]['#group_exists'])) { + // Intentionally empty to clarify the flow; we simply return $element. + } + // If we injected this element into the group, then we want to render it. + elseif (!empty($element['#group_details'])) { + // Intentionally empty to clarify the flow; we simply return $element. + } + // Otherwise, this element belongs to a group and the group exists, so we do + // not render it. + elseif (Element::children($element['#groups'][$group])) { + $element['#printed'] = TRUE; + } + } + + return $element; + } + } diff --git a/core/lib/Drupal/Core/Render/FormElementBase.php b/core/lib/Drupal/Core/Render/FormElementBase.php new file mode 100644 index 0000000..28ff981 --- /dev/null +++ b/core/lib/Drupal/Core/Render/FormElementBase.php @@ -0,0 +1,65 @@ +