Commit 9c87059f authored by webchick's avatar webchick

Issue #2305839 by jhodgdon, tim.plunkett: Convert hook_element_info() to annotated classes.

parent fef9fd27
......@@ -749,6 +749,9 @@ services:
plugin.manager.condition:
class: Drupal\Core\Condition\ConditionManager
parent: default_plugin_manager
plugin.manager.element_info:
class: Drupal\Core\Render\ElementInfoManager
parent: default_plugin_manager
kernel_destruct_subscriber:
class: Drupal\Core\EventSubscriber\KernelDestructionSubscriber
tags:
......@@ -907,8 +910,7 @@ services:
info_parser:
class: Drupal\Core\Extension\InfoParser
element_info:
class: Drupal\Core\Render\ElementInfo
arguments: ['@module_handler']
alias: plugin.manager.element_info
file.mime_type.guesser:
class: Drupal\Core\File\MimeType\MimeTypeGuesser
tags:
......
......@@ -6,6 +6,7 @@
*/
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
/**
* @defgroup ajax Ajax API
......@@ -162,20 +163,10 @@
/**
* Form element processing handler for the #ajax form property.
*
* @param $element
* An associative array containing the properties of the element.
*
* @return
* The processed element.
*
* @see ajax_pre_render_element()
* @deprecated Use \Drupal\Core\Render\Element\FormElement::processAjaxForm().
*/
function ajax_process_form($element, FormStateInterface $form_state) {
$element = ajax_pre_render_element($element);
if (!empty($element['#ajax_processed'])) {
$form_state['cache'] = TRUE;
}
return $element;
function ajax_process_form($element, FormStateInterface $form_state, &$complete_form) {
return Element\FormElement::processAjaxForm($element, $form_state, $complete_form);
}
/**
......
......@@ -2670,65 +2670,10 @@ function drupal_pre_render_html_tag($element) {
/**
* Pre-render callback: Renders a link into #markup.
*
* Doing so during pre_render gives modules a chance to alter the link parts.
*
* @param $elements
* A structured array whose keys form the arguments to l():
* - #title: The link text to pass as argument to l().
* - One of the following
* - #route_name and (optionally) and a #route_parameters array; The route
* name and route parameters which will be passed into the link generator.
* - #href: The system path or URL to pass as argument to l().
* - #options: (optional) An array of options to pass to l() or the link
* generator.
*
* @return
* The passed-in elements containing a rendered link in '#markup'.
* @deprecated Use \Drupal\Core\Render\Element\Link::preRenderLink().
*/
function drupal_pre_render_link($element) {
// By default, link options to pass to l() are normally set in #options.
$element += array('#options' => array());
// However, within the scope of renderable elements, #attributes is a valid
// way to specify attributes, too. Take them into account, but do not override
// attributes from #options.
if (isset($element['#attributes'])) {
$element['#options'] += array('attributes' => array());
$element['#options']['attributes'] += $element['#attributes'];
}
// This #pre_render callback can be invoked from inside or outside of a Form
// API context, and depending on that, a HTML ID may be already set in
// different locations. #options should have precedence over Form API's #id.
// #attributes have been taken over into #options above already.
if (isset($element['#options']['attributes']['id'])) {
$element['#id'] = $element['#options']['attributes']['id'];
}
elseif (isset($element['#id'])) {
$element['#options']['attributes']['id'] = $element['#id'];
}
// Conditionally invoke ajax_pre_render_element(), if #ajax is set.
if (isset($element['#ajax']) && !isset($element['#ajax_processed'])) {
// If no HTML ID was found above, automatically create one.
if (!isset($element['#id'])) {
$element['#id'] = $element['#options']['attributes']['id'] = drupal_html_id('ajax-link');
}
// If #ajax['path] was not specified, use the href as Ajax request URL.
if (!isset($element['#ajax']['path'])) {
$element['#ajax']['path'] = $element['#href'];
$element['#ajax']['options'] = $element['#options'];
}
$element = ajax_pre_render_element($element);
}
if (isset($element['#route_name'])) {
$element['#route_parameters'] = empty($element['#route_parameters']) ? array() : $element['#route_parameters'];
$element['#markup'] = \Drupal::linkGenerator()->generate($element['#title'], $element['#route_name'], $element['#route_parameters'], $element['#options']);
}
else {
$element['#markup'] = l($element['#title'], $element['#href'], $element['#options']);
}
return $element;
return Element\Link::preRenderLink($element);
}
/**
......
......@@ -566,22 +566,10 @@ function form_type_select_value($element, $input = FALSE) {
/**
* Determines the value for a textfield form element.
*
* @param $element
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
*
* @return
* The data that will appear in the $element_state['values'] collection
* for this element. Return nothing to use the default.
* @deprecated Use \Drupal\Core\Render\Element\Textfield::valueCallback().
*/
function form_type_textfield_value($element, $input = FALSE) {
if ($input !== FALSE && $input !== NULL) {
// Equate $input to the form value to ensure it's marked for
// validation.
return str_replace(array("\r", "\n"), '', $input);
}
function form_type_textfield_value(&$element, $input, &$form_state) {
return Element\Textfield::valueCallback($element, $input, $form_state);
}
/**
......@@ -1306,24 +1294,10 @@ function form_pre_render_actions_dropbutton(array $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 current state of the form for the form this element belongs to.
*
* @return
* The processed element.
*
* @see form_validate_pattern()
* @deprecated Use \Drupal\Core\Render\Element\FormElement::processPattern().
*/
function form_process_pattern($element, FormStateInterface $form_state) {
if (isset($element['#pattern']) && !isset($element['#attributes']['pattern'])) {
$element['#attributes']['pattern'] = $element['#pattern'];
$element['#element_validate'][] = 'form_validate_pattern';
}
return $element;
function form_process_pattern($element, FormStateInterface $form_state, &$complete_form) {
return Element\FormElement::processPattern($element, $form_state, $complete_form);
}
/**
......@@ -1922,61 +1896,10 @@ function form_pre_render_details($element) {
/**
* Adds members of this group as actual elements for rendering.
*
* @param $element
* An associative array containing the properties and children of the
* element.
*
* @return
* The modified element with all group members.
* @deprecated Use \Drupal\Core\Render\ElementElementBase::preRenderGroup().
*/
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 Element\RenderElement::preRenderGroup($element);
}
/**
......@@ -2064,43 +1987,10 @@ function template_preprocess_vertical_tabs(&$variables) {
* 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 \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* The form element.
* @deprecated Use \Drupal\Core\Render\Element\FormElement::processAutocomplete().
*/
function form_process_autocomplete($element, FormStateInterface $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;
function form_process_autocomplete($element, FormStateInterface $form_state, &$complete_form) {
return Element\FormElement::processAutocomplete($element, $form_state, $complete_form);
}
/**
......@@ -2206,20 +2096,10 @@ function form_pre_render_hidden($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().
* @deprecated Use \Drupal\Core\Render\Element\Textfield::preRenderTextfield().
*/
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 Element\Textfield::preRenderTextfield($element);
}
/**
......
......@@ -938,7 +938,14 @@ protected function handleInputElement($form_id, &$element, FormStateInterface &$
// Set the element's #value property.
if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
// @todo Once all elements are converted to plugins in
// https://www.drupal.org/node/2311393, rely on
// $element['#value_callback'] directly.
$value_callable = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value';
if (!is_callable($value_callable)) {
$value_callable = '\Drupal\Core\Render\Element\FormElement::valueCallback';
}
if ($process_input) {
// Get the input for the current element. NULL values in the input need
// to be explicitly distinguished from missing input. (see below)
......@@ -962,9 +969,8 @@ protected function handleInputElement($form_id, &$element, FormStateInterface &$
// If we have input for the current element, assign it to the #value
// property, optionally filtered through $value_callback.
if ($input_exists) {
if (is_callable($value_callable)) {
$element['#value'] = call_user_func_array($value_callable, array(&$element, $input, &$form_state));
}
$element['#value'] = call_user_func_array($value_callable, array(&$element, $input, &$form_state));
if (!isset($element['#value']) && isset($input)) {
$element['#value'] = $input;
}
......@@ -978,9 +984,8 @@ protected function handleInputElement($form_id, &$element, FormStateInterface &$
if (!isset($element['#value'])) {
// Call #type_value without a second argument to request default_value
// handling.
if (is_callable($value_callable)) {
$element['#value'] = call_user_func_array($value_callable, array(&$element, FALSE, &$form_state));
}
$element['#value'] = call_user_func_array($value_callable, array(&$element, FALSE, &$form_state));
// Final catch. If we haven't set a value yet, use the explicit default
// value. Avoid image buttons (which come with garbage value), so we
// only get value for the button actually clicked.
......
<?php
/**
* @file
* Contains \Drupal\Core\Render\Annotation\FormElement.
*/
namespace Drupal\Core\Render\Annotation;
/**
* Defines a form element plugin annotation object.
*
* See \Drupal\Core\Render\Element\FormElementInterface for more information
* about form element plugins.
*
* Plugin Namespace: Element
*
* For a working example, see \Drupal\Core\Render\Element\Textfield.
*
* @see \Drupal\Core\Render\ElementInfoManager
* @see \Drupal\Core\Render\Element\FormElementInterface
* @see \Drupal\Core\Render\Element\FormElement
* @see \Drupal\Core\Render\Annotation\RenderElement
* @see plugin_api
*
* @ingroup theme_render
*
* @Annotation
*/
class FormElement extends RenderElement {
}
<?php
/**
* @file
* Contains \Drupal\Core\Render\Annotation\RenderElement.
*/
namespace Drupal\Core\Render\Annotation;
use Drupal\Component\Annotation\PluginID;
/**
* Defines a render element plugin annotation object.
*
* See \Drupal\Core\Render\Element\ElementInterface for more information
* about render element plugins.
*
* Plugin Namespace: Element
*
* For a working example, see \Drupal\Core\Render\Element\Link.
*
* @see \Drupal\Core\Render\ElementInfoManager
* @see \Drupal\Core\Render\Element\ElementInterface
* @see \Drupal\Core\Render\Element\RenderElement
* @see \Drupal\Core\Render\Annotation\FormElement
* @see plugin_api
*
* @ingroup theme_render
*
* @Annotation
*/
class RenderElement extends PluginID {
}
......@@ -10,7 +10,11 @@
use Drupal\Component\Utility\String;
/**
* Deals with drupal render elements.
* Provides helper methods for Drupal render elements.
*
* @see \Drupal\Core\Render\Element\ElementInterface
*
* @ingroup theme_render
*/
class Element {
......@@ -47,7 +51,7 @@ public static function properties(array $element) {
* The key to check.
*
* @return bool
* TRUE if the element is a child, FALSE otherwise.
* TRUE if the element is a child, FALSE otherwise.
*/
public static function child($key) {
return !isset($key[0]) || $key[0] != '#';
......@@ -143,11 +147,11 @@ public static function getVisibleChildren(array $elements) {
* @param array $element
* The renderable element to process. Passed by reference.
* @param array $map
* An associative array whose keys are element property names and whose values
* are the HTML attribute names to set for corresponding the property; e.g.,
* array('#propertyname' => 'attributename'). If both names are identical
* except for the leading '#', then an attribute name value is sufficient and
* no property name needs to be specified.
* An associative array whose keys are element property names and whose
* values are the HTML attribute names to set on the corresponding
* property; e.g., array('#propertyname' => 'attributename'). If both names
* are identical except for the leading '#', then an attribute name value is
* sufficient and no property name needs to be specified.
*/
public static function setAttributes(array &$element, array $map) {
foreach ($map as $property => $attribute) {
......
<?php
/**
* @file
* Contains \Drupal\Core\Render\Element\ElementInterface.
*/
namespace Drupal\Core\Render\Element;
use Drupal\Component\Plugin\PluginInspectionInterface;
/**
* Provides an interface for element plugins.
*
* Render element plugins allow modules to declare their own Render API element
* types and specify the default values for the properties. The values returned
* by the getInfo() method of the element plugin will be merged with the
* properties specified in render arrays. Thus, you can specify defaults for any
* Render API keys, in addition to those explicitly documented by
* \Drupal\Core\Render\ElementInfoManagerInterface::getInfo().
*
* Some render elements are specifically form input elements; see
* \Drupal\Core\Render\Element\FormElementInterface for more information.
*
* @see \Drupal\Core\Render\ElementInfoManager
* @see \Drupal\Core\Render\Annotation\RenderElement
* @see \Drupal\Core\Render\Element\RenderElement
* @see plugin_api
*
* @ingroup theme_render
*/
interface ElementInterface extends PluginInspectionInterface {
/**
* Returns the element properties for this element.
*
* @return array
* An array of element properties. See
* \Drupal\Core\Render\ElementInfoManagerInterface::getInfo() for
* documentation of the standard properties of all elements, and the
* return value format.
*/
public function getInfo();
}
<?php
/**
* @file
* Contains \Drupal\Core\Render\Element\FormElement.
*/
namespace Drupal\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides a base class for form render plugins.
*
* @see \Drupal\Core\Render\Annotation\FormElement
* @see \Drupal\Core\Render\Element\FormElementInterface
* @see \Drupal\Core\Render\ElementInfoManager
* @see plugin_api
*
* @ingroup theme_render
*/
abstract class FormElement extends RenderElement implements FormElementInterface {
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
return NULL;
}
/**
* Form element processing handler for the #ajax form property.
*
* @param array $element
* An associative array containing the properties of the element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form structure.
*
* @return array
* The processed element.
*
* @see ajax_pre_render_element()
*/
public static function processAjaxForm(&$element, FormStateInterface $form_state, &$complete_form) {
$element = ajax_pre_render_element($element);
if (!empty($element['#ajax_processed'])) {
$form_state['cache'] = TRUE;
}
return $element;
}
/**
* Arranges elements into groups.
*
* @param array $element
* An associative array containing the properties and children of the
* element. Note that $element must be taken by reference here, so processed
* child elements are taken over into $form_state.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form structure.
*
* @return array
* The processed element.
*/
public static function processGroup(&$element, FormStateInterface $form_state, &$complete_form) {
$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;
}
/**
* #process callback for #pattern form element property.
*
* @param array $element
* An associative array containing the properties and children of the
* generic input element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form structure.
*
* @return array
* The processed element.
*
* @see form_validate_pattern()
*/
public static function processPattern(&$element, FormStateInterface $form_state, &$complete_form) {
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.
*
* This sets up autocomplete functionality for elements with an
* #autocomplete_route_name property, using the #autocomplete_route_parameters
* property if present.
*
* For example, suppose your autocomplete route name is
* 'mymodule.autocomplete' and its path is
* '/mymodule/autocomplete/{a}/{b}'. In a form array, you would create a text
* field with properties:
* @code
* '#autocomplete_route_name' => 'mymodule.autocomplete',
* '#autocomplete_route_parameters' => array('a' => $some_key, 'b' => $some_id),
* @endcode
* If the user types "keywords" in that field, the full path called would be:
* '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 \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form structure.