Issue #2305839 by jhodgdon, tim.plunkett: Convert hook_element_info() to annotated classes.
parent
fef9fd278d
commit
9c87059f0f
|
@ -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 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
/**
|
||||
* 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 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
|
|||
|
||||
// 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 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
|
|||
// 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 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
|
|||
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.
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?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 {
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?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 @@ namespace Drupal\Core\Render;
|
|||
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 @@ class 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 @@ class Element {
|
|||
* @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) {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<?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();
|
||||
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
<?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.
|
||||
*
|
||||
* @return array
|
||||
* The form element.
|
||||
*/
|
||||
public static function processAutocomplete(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Render\Element\FormElementInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Render\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for form element plugins.
|
||||
*
|
||||
* Form element plugins are a subset of render elements, specifically
|
||||
* representing HTML elements that take input as part of a form. Form element
|
||||
* plugins are discovered via the same mechanism as regular render element
|
||||
* plugins. See \Drupal\Core\Render\Element\ElementInterface for general
|
||||
* information about render element plugins.
|
||||
*
|
||||
* @see \Drupal\Core\Render\ElementInfoManager
|
||||
* @see \Drupal\Core\Render\Element\FormElement
|
||||
* @see \Drupal\Core\Render\Annotation\FormElement
|
||||
* @see plugin_api
|
||||
*
|
||||
* @ingroup theme_render
|
||||
*/
|
||||
interface FormElementInterface extends ElementInterface {
|
||||
|
||||
/**
|
||||
* Determines how user input is mapped to an element's #value property.
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the properties of the element.
|
||||
* @param mixed $input
|
||||
* The incoming input to populate the form element. If this is FALSE,
|
||||
* the element's default value should be returned.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return mixed
|
||||
* The value to assign to the element.
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state);
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Render\Element\Link.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Render\Element;
|
||||
|
||||
/**
|
||||
* Provides a link render element.
|
||||
*
|
||||
* @RenderElement("link")
|
||||
*/
|
||||
class Link extends RenderElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return array(
|
||||
'#pre_render' => array(
|
||||
array($class, 'preRenderLink'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-render callback: Renders a link into #markup.
|
||||
*
|
||||
* Doing so during pre_render gives modules a chance to alter the link parts.
|
||||
*
|
||||
* @param array $element
|
||||
* 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) 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 array
|
||||
* The passed-in element containing a rendered link in '#markup'.
|
||||
*/
|
||||
public static function preRenderLink($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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Render\Element\MachineName.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Render\Element;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a machine name render element.
|
||||
*
|
||||
* Provides a form element to enter a machine name, which is validated to ensure
|
||||
* that the name is unique and does not contain disallowed characters. All
|
||||
* disallowed characters are replaced with a replacement character via
|
||||
* JavaScript.
|
||||
*
|
||||
* @FormElement("machine_name")
|
||||
*/
|
||||
class MachineName extends Textfield {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return array(
|
||||
'#input' => TRUE,
|
||||
'#default_value' => NULL,
|
||||
'#required' => TRUE,
|
||||
'#maxlength' => 64,
|
||||
'#size' => 60,
|
||||
'#autocomplete_route_name' => FALSE,
|
||||
'#process' => array(
|
||||
array($class, 'processMachineName'),
|
||||
array($class, 'processAutocomplete'),
|
||||
array($class, 'processAjaxForm'),
|
||||
),
|
||||
'#element_validate' => array(
|
||||
array($class, 'validateMachineName'),
|
||||
),
|
||||
'#pre_render' => array(
|
||||
array($class, 'preRenderTextfield'),
|
||||
),
|
||||
'#theme' => 'input__textfield',
|
||||
'#theme_wrappers' => array('form_element'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a machine-readable name form element.
|
||||
*
|
||||
* @param array $element
|
||||
* The form element to process. Properties used:
|
||||
* - #machine_name: An associative array containing:
|
||||
* - exists: A callable to invoke for checking whether a submitted machine
|
||||
* name value already exists. The submitted value is passed as an
|
||||
* argument. In most cases, an existing API or menu argument loader
|
||||
* function can be re-used. The callback is only invoked if the
|
||||
* submitted value differs from the element's #default_value.
|
||||
* - source: (optional) The #array_parents of the form element containing
|
||||
* the human-readable name (i.e., as contained in the $form structure)
|
||||
* to use as source for the machine name. Defaults to array('label').
|
||||
* - label: (optional) Text to display as label for the machine name value
|
||||
* after the human-readable name form element. Defaults to "Machine
|
||||
* name".
|
||||
* - replace_pattern: (optional) A regular expression (without delimiters)
|
||||
* matching disallowed characters in the machine name. Defaults to
|
||||
* '[^a-z0-9_]+'.
|
||||
* - replace: (optional) A character to replace disallowed characters in
|
||||
* the machine name via JavaScript. Defaults to '_' (underscore). When
|
||||
* using a different character, 'replace_pattern' needs to be set
|
||||
* accordingly.
|
||||
* - error: (optional) A custom form error message string to show, if the
|
||||
* machine name contains disallowed characters.
|
||||
* - standalone: (optional) Whether the live preview should stay in its
|
||||
* own form element rather than in the suffix of the source
|
||||
* element. Defaults to FALSE.
|
||||
* - #maxlength: (optional) Maximum allowed length of the machine name.
|
||||
* Defaults to 64.
|
||||
* - #disabled: (optional) Should be set to TRUE if an existing machine
|
||||
* name must not be changed after initial creation.
|
||||
* @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 processMachineName(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
// We need to pass the langcode to the client.
|
||||
$language = \Drupal::languageManager()->getCurrentLanguage();
|
||||
|
||||
// Apply default form element properties.
|
||||
$element += array(
|
||||
'#title' => t('Machine-readable name'),
|
||||
'#description' => t('A unique machine-readable name. Can only contain lowercase letters, numbers, and underscores.'),
|
||||
'#machine_name' => array(),
|
||||
'#field_prefix' => '',
|
||||
'#field_suffix' => '',
|
||||
'#suffix' => '',
|
||||
);
|
||||
// A form element that only wants to set one #machine_name property (usually
|
||||
// 'source' only) would leave all other properties undefined, if the defaults
|
||||
// were defined in hook_element_info(). Therefore, we apply the defaults here.
|
||||
$element['#machine_name'] += array(
|
||||
'source' => array('label'),
|
||||
'target' => '#' . $element['#id'],
|
||||
'label' => t('Machine name'),
|
||||
'replace_pattern' => '[^a-z0-9_]+',
|
||||
'replace' => '_',
|
||||
'standalone' => FALSE,
|
||||
'field_prefix' => $element['#field_prefix'],
|
||||
'field_suffix' => $element['#field_suffix'],
|
||||
);
|
||||
|
||||
// By default, machine names are restricted to Latin alphanumeric characters.
|
||||
// So, default to LTR directionality.
|
||||
if (!isset($element['#attributes'])) {
|
||||
$element['#attributes'] = array();
|
||||
}
|
||||
$element['#attributes'] += array('dir' => 'ltr');
|
||||
|
||||
// The source element defaults to array('name'), but may have been overidden.
|
||||
if (empty($element['#machine_name']['source'])) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
// Retrieve the form element containing the human-readable name from the
|
||||
// complete form in $form_state. By reference, because we may need to append
|
||||
// a #field_suffix that will hold the live preview.
|
||||
$key_exists = NULL;
|
||||
$source = NestedArray::getValue($form_state['complete_form'], $element['#machine_name']['source'], $key_exists);
|
||||
if (!$key_exists) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
$suffix_id = $source['#id'] . '-machine-name-suffix';
|
||||
$element['#machine_name']['suffix'] = '#' . $suffix_id;
|
||||
|
||||
if ($element['#machine_name']['standalone']) {
|
||||
$element['#suffix'] = SafeMarkup::set($element['#suffix'] . ' <small id="' . $suffix_id . '"> </small>');
|
||||
}
|
||||
else {
|
||||
// Append a field suffix to the source form element, which will contain
|
||||
// the live preview of the machine name.
|
||||
$source += array('#field_suffix' => '');
|
||||
$source['#field_suffix'] = SafeMarkup::set($source['#field_suffix'] . ' <small id="' . $suffix_id . '"> </small>');
|
||||
|
||||
$parents = array_merge($element['#machine_name']['source'], array('#field_suffix'));
|
||||
NestedArray::setValue($form_state['complete_form'], $parents, $source['#field_suffix']);
|
||||
}
|
||||
|
||||
$js_settings = array(
|
||||
'type' => 'setting',
|
||||
'data' => array(
|
||||
'machineName' => array(
|
||||
'#' . $source['#id'] => $element['#machine_name'],
|
||||
),
|
||||
'langcode' => $language->id,
|
||||
),
|
||||
);
|
||||
$element['#attached']['library'][] = 'core/drupal.machine-name';
|
||||
$element['#attached']['js'][] = $js_settings;
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validation handler for machine_name elements.
|
||||
*
|
||||
* Note that #maxlength is validated by _form_validate() already.
|
||||
*
|
||||
* This checks that the submitted value:
|
||||
* - Does not contain the replacement character only.
|
||||
* - Does not contain disallowed characters.
|
||||
* - Is unique; i.e., does not already exist.
|
||||
* - Does not exceed the maximum length (via #maxlength).
|
||||
* - Cannot be changed after creation (via #disabled).
|
||||
*/
|
||||
public static function validateMachineName(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
// Verify that the machine name not only consists of replacement tokens.
|
||||
if (preg_match('@^' . $element['#machine_name']['replace'] . '+$@', $element['#value'])) {
|
||||
form_error($element, $form_state, t('The machine-readable name must contain unique characters.'));
|
||||
}
|
||||
|
||||
// Verify that the machine name contains no disallowed characters.
|
||||
if (preg_match('@' . $element['#machine_name']['replace_pattern'] . '@', $element['#value'])) {
|
||||
if (!isset($element['#machine_name']['error'])) {
|
||||
// Since a hyphen is the most common alternative replacement character,
|
||||
// a corresponding validation error message is supported here.
|
||||
if ($element['#machine_name']['replace'] == '-') {
|
||||
form_error($element, $form_state, t('The machine-readable name must contain only lowercase letters, numbers, and hyphens.'));
|
||||
}
|
||||
// Otherwise, we assume the default (underscore).
|
||||
else {
|
||||
form_error($element, $form_state, t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
form_error($element, $form_state, $element['#machine_name']['error']);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the machine name is unique.
|
||||
if ($element['#default_value'] !== $element['#value']) {
|
||||
$function = $element['#machine_name']['exists'];
|
||||
if (call_user_func($function, $element['#value'], $element, $form_state)) {
|
||||
form_error($element, $form_state, t('The machine-readable name is already in use. It must be unique.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Render\Element\RenderElement.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Render\Element;
|
||||
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\Core\Render\Element;
|
||||
|
||||
/**
|
||||
* Provides a base class for element render plugins.
|
||||
*
|
||||
* @see \Drupal\Core\Render\Annotation\RenderElement
|
||||
* @see \Drupal\Core\Render\ElementInterface
|
||||
* @see \Drupal\Core\Render\ElementInfoManager
|
||||
* @see plugin_api
|
||||
*
|
||||
* @ingroup theme_render
|
||||
*/
|
||||
abstract class RenderElement 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Render\Element\Textfield.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Render\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
|
||||
/**
|
||||
* Provides a one-line text field form element.
|
||||
*
|
||||
* @FormElement("textfield")
|
||||
*/
|
||||
class Textfield extends FormElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return array(
|
||||
'#input' => TRUE,
|
||||
'#size' => 60,
|
||||
'#maxlength' => 128,
|
||||
'#autocomplete_route_name' => FALSE,
|
||||
'#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'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Render\ElementInfo.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Render;
|
||||
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
|
||||
/**
|
||||
* Provides the default element info implementation.
|
||||
*/
|
||||
class ElementInfo implements ElementInfoInterface {
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Stores the available element information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $elementInfo;
|
||||
|
||||
/**
|
||||
* Constructs a new ElementInfo instance.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
*/
|
||||
public function __construct(ModuleHandlerInterface $module_handler) {
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo($type) {
|
||||
if (!isset($this->elementInfo)) {
|
||||
$this->elementInfo = $this->buildInfo();
|
||||
}
|
||||
return isset($this->elementInfo[$type]) ? $this->elementInfo[$type] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds up all element information.
|
||||
*/
|
||||
protected function buildInfo() {
|
||||
$info = $this->moduleHandler->invokeAll('element_info');
|
||||
foreach ($info as $element_type => $element) {
|
||||
$info[$element_type]['#type'] = $element_type;
|
||||
}
|
||||
// Allow modules to alter the element type defaults.
|
||||
$this->moduleHandler->alter('element_info', $info);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Render\ElementInfoInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Render;
|
||||
|
||||
/**
|
||||
* Defines available render array element types.
|
||||
*/
|
||||
interface ElementInfoInterface {
|
||||
|
||||
/**
|
||||
* Retrieves the default properties for the defined element type.
|
||||
*
|
||||
* Each of the form element types defined by this hook is assumed to have
|
||||
* a matching theme function, e.g. theme_elementtype(), which should be
|
||||
* registered with hook_theme() as normal.
|
||||
*
|
||||
* For more information about custom element types see the explanation at
|
||||
* http://drupal.org/node/169815.
|
||||
*
|
||||
* @param string $type
|
||||
* An element type as defined by hook_element_info().
|
||||
*
|
||||
* @return array
|
||||
* An associative array describing the element types being defined. The array
|
||||
* contains a sub-array for each element type, with the machine-readable type
|
||||
* name as the key. Each sub-array has a number of possible attributes:
|
||||
* - "#input": boolean indicating whether or not this element carries a value
|
||||
* (even if it's hidden).
|
||||
* - "#process": array of callback functions taking $element, $form_state,
|
||||
* and $complete_form.
|
||||
* - "#after_build": array of callables taking $element and $form_state.
|
||||
* - "#validate": array of callback functions taking $form and $form_state.
|
||||
* - "#element_validate": array of callback functions taking $element and
|
||||
* $form_state.
|
||||
* - "#pre_render": array of callables taking $element.
|
||||
* - "#post_render": array of callables taking $children and $element.
|
||||
* - "#submit": array of callback functions taking $form and $form_state.
|
||||
* - "#title_display": optional string indicating if and how #title should be
|
||||
* displayed, see the form-element template and theme_form_element_label().
|
||||
*
|
||||
* @see hook_element_info()
|
||||
* @see hook_element_info_alter()
|
||||
*/
|
||||
public function getInfo($type);
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Render\ElementInfoManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Render;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\Core\Render\Element\FormElementInterface;
|
||||
|
||||
/**
|
||||
* Provides a plugin manager for element plugins.
|
||||
*
|
||||
* @see \Drupal\Core\Render\Annotation\RenderElement
|
||||
* @see \Drupal\Core\Render\Annotation\FormElement
|
||||
* @see \Drupal\Core\Render\Element\RenderElement
|
||||
* @see \Drupal\Core\Render\Element\FormElement
|
||||
* @see \Drupal\Core\Render\Element\ElementInterface
|
||||
* @see \Drupal\Core\Render\Element\FormElementInterface
|
||||
* @see plugin_api
|
||||
*/
|
||||
class ElementInfoManager extends DefaultPluginManager implements ElementInfoManagerInterface {
|
||||
|
||||
/**
|
||||
* Stores the available element information.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $elementInfo;
|
||||
|
||||
/**
|
||||
* Constructs a ElementInfoManager object.
|
||||
*
|
||||
* @param \Traversable $namespaces
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* Cache backend instance to use.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler to invoke the alter hook with.
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
|
||||
$this->setCacheBackend($cache_backend, 'element_info');
|
||||
|
||||
parent::__construct('Element', $namespaces, $module_handler, 'Drupal\Core\Render\Annotation\RenderElement');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo($type) {
|
||||
if (!isset($this->elementInfo)) {
|
||||
$this->elementInfo = $this->buildInfo();
|
||||
}
|
||||
return isset($this->elementInfo[$type]) ? $this->elementInfo[$type] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds up all element information.
|
||||
*/
|
||||
protected function buildInfo() {
|
||||
// @todo Remove this hook once all elements are converted to plugins in
|
||||
// https://www.drupal.org/node/2311393.
|
||||
$info = $this->moduleHandler->invokeAll('element_info');
|
||||
|
||||
foreach ($this->getDefinitions() as $element_type => $definition) {
|
||||
$element = $this->createInstance($element_type);
|
||||
$element_info = $element->getInfo();
|
||||
|
||||
// If this is element is to be used exclusively in a form, denote that it
|
||||
// will receive input, and assign the value callback.
|
||||
if ($element instanceof FormElementInterface) {
|
||||
$element_info['#input'] = TRUE;
|
||||
$element_info['#value_callback'] = array($definition['class'], 'valueCallback');
|
||||
}
|
||||
$info[$element_type] = $element_info;
|
||||
}
|
||||
foreach ($info as $element_type => $element) {
|
||||
$info[$element_type]['#type'] = $element_type;
|
||||
}
|
||||
// Allow modules to alter the element type defaults.
|
||||
$this->moduleHandler->alter('element_info', $info);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return \Drupal\Core\Render\Element\ElementInterface
|
||||
*/
|
||||
public function createInstance($plugin_id, array $configuration = array()) {
|
||||
return parent::createInstance($plugin_id, $configuration);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Render\ElementInfoManagerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Render;
|
||||
|
||||
/**
|
||||
* Collects available render array element types.
|
||||
*/
|
||||
interface ElementInfoManagerInterface {
|
||||
|
||||
/**
|
||||
* Retrieves the default properties for the defined element type.
|
||||
*
|
||||
* Each of the form element types defined by this hook is assumed to have
|
||||
* a matching theme hook, which should be registered with hook_theme() as
|
||||
* normal.
|
||||
*
|
||||
* For more information about custom element types see the explanation at
|
||||
* http://drupal.org/node/169815.
|
||||
*
|
||||
* @param string $type
|
||||
* An element type as defined by hook_element_info() or the machine name
|
||||
* of an element type plugin.
|
||||
*
|
||||
* @return array
|
||||
* An associative array describing the element types being defined. The
|
||||
* array contains a sub-array for each element type, with the
|
||||
* machine-readable type name as the key. Each sub-array has a number of
|
||||
* possible attributes:
|
||||
* - #input: boolean indicating whether or not this element carries a value
|
||||
* (even if it's hidden).
|
||||
* - #process: array of callback functions taking $element, $form_state,
|
||||
* and $complete_form.
|
||||
* - #after_build: array of callables taking $element and $form_state.
|
||||
* - #validate: array of callback functions taking $form and $form_state.
|
||||
* - #element_validate: array of callback functions taking $element and
|
||||
* $form_state.
|
||||
* - #pre_render: array of callables taking $element.
|
||||
* - #post_render: array of callables taking $children and $element.
|
||||
* - #submit: array of callback functions taking $form and $form_state.
|
||||
* - #title_display: optional string indicating if and how #title should be
|
||||
* displayed (see form-element.html.twig).
|
||||
*
|
||||
* @see hook_element_info()
|
||||
* @see hook_element_info_alter()
|
||||
* @see \Drupal\Core\Render\Element\ElementInterface
|
||||
* @see \Drupal\Core\Render\Element\ElementInterface::getInfo()
|
||||
*/
|
||||
public function getInfo($type);
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
@todo This must be here because DrupalKernel will only allow namespaces to
|
||||
provide plugins if there is a Plugin subdirectory, and git does not allow
|
||||
empty subdirectories. This file should be removed once
|
||||
https://www.drupal.org/node/2309889 is fixed.
|
|
@ -193,11 +193,14 @@ function callback_queue_worker($queue_item_data) {
|
|||
* specify their default values. The values returned by this hook will be
|
||||
* merged with the elements returned by form constructor implementations and so
|
||||
* can return defaults for any Form APIs keys in addition to those explicitly
|
||||
* documented by \Drupal\Core\Render\ElementInfoInterface::getInfo().
|
||||
* documented by \Drupal\Core\Render\ElementInfoManagerInterface::getInfo().
|
||||
*
|
||||
* @return array
|
||||
* An associative array with structure identical to that of the return value
|
||||
* of \Drupal\Core\Render\ElementInfoInterface::getInfo().
|
||||
* of \Drupal\Core\Render\ElementInfoManagerInterface::getInfo().
|
||||
*
|
||||
* @deprecated Use an annotated class instead, see
|
||||
* \Drupal\Core\Render\Element\ElementInterface.
|
||||
*
|
||||
* @see hook_element_info_alter()
|
||||
* @see system_element_info()
|
||||
|
@ -217,7 +220,7 @@ function hook_element_info() {
|
|||
*
|
||||
* @param array $types
|
||||
* An associative array with structure identical to that of the return value
|
||||
* of \Drupal\Core\Render\ElementInfoInterface::getInfo().
|
||||
* of \Drupal\Core\Render\ElementInfoManagerInterface::getInfo().
|
||||
*
|
||||
* @see hook_element_info()
|
||||
*/
|
||||
|
|
|
@ -368,16 +368,6 @@ function system_element_info() {
|
|||
'#pre_render' => array('form_pre_render_image_button'),
|
||||
'#theme_wrappers' => array('input__image_button'),
|
||||
);
|
||||
$types['textfield'] = 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'),
|
||||
'#theme' => 'input__textfield',
|
||||
'#theme_wrappers' => array('form_element'),
|
||||
);
|
||||
$types['tel'] = array(
|
||||
'#input' => TRUE,
|
||||
'#size' => 30,
|
||||
|
@ -448,19 +438,6 @@ function system_element_info() {
|
|||
'#theme' => 'input__color',
|
||||
'#theme_wrappers' => array('form_element'),
|
||||
);
|
||||
$types['machine_name'] = array(
|
||||
'#input' => TRUE,
|
||||
'#default_value' => NULL,
|
||||
'#required' => TRUE,
|
||||
'#maxlength' => 64,
|
||||
'#size' => 60,
|
||||
'#autocomplete_route_name' => FALSE,
|
||||
'#process' => array('form_process_machine_name', 'form_process_autocomplete', 'ajax_process_form'),
|
||||
'#element_validate' => array('form_validate_machine_name'),
|
||||
'#pre_render' => array('form_pre_render_textfield'),
|
||||
'#theme' => 'input__textfield',
|
||||
'#theme_wrappers' => array('form_element'),
|
||||
);
|
||||
$types['password'] = array(
|
||||
'#input' => TRUE,
|
||||
'#size' => 60,
|
||||
|
@ -586,9 +563,6 @@ function system_element_info() {
|
|||
$types['value'] = array(
|
||||
'#input' => TRUE,
|
||||
);
|
||||
$types['link'] = array(
|
||||
'#pre_render' => array('drupal_pre_render_link'),
|
||||
);
|
||||
$types['fieldset'] = array(
|
||||
'#value' => NULL,
|
||||
'#process' => array('form_process_group', 'ajax_process_form'),
|
||||
|
|
|
@ -163,11 +163,21 @@
|
|||
* requests and the CSS files used to style that markup. In order to ensure that
|
||||
* a theme can completely customize the markup, module developers should avoid
|
||||
* directly writing HTML markup for pages, blocks, and other user-visible output
|
||||
* in their modules, and instead return structured "render arrays" (described
|
||||
* below). Doing this also increases usability, by ensuring that the markup used
|
||||
* for similar functionality on different areas of the site is the same, which
|
||||
* gives users fewer user interface patterns to learn.
|
||||
* in their modules, and instead return structured "render arrays" (see @ref
|
||||
* arrays below). Doing this also increases usability, by ensuring that the
|
||||
* markup used for similar functionality on different areas of the site is the
|
||||
* same, which gives users fewer user interface patterns to learn.
|
||||
*
|
||||
* For further information on the Theme and Render APIs, see:
|
||||
* - https://drupal.org/documentation/theme
|
||||
* - https://drupal.org/node/722174
|
||||
* - https://drupal.org/node/933976
|
||||
* - https://drupal.org/node/930760
|
||||
*
|
||||
* @todo Check these links. Some are for Drupal 7, and might need updates for
|
||||
* Drupal 8.
|
||||
*
|
||||
* @section arrays Render arrays
|
||||
* The core structure of the Render API is the render array, which is a
|
||||
* hierarchical associative array containing data to be rendered and properties
|
||||
* describing how the data should be rendered. A render array that is returned
|
||||
|
@ -194,11 +204,8 @@
|
|||
* - #type: Specifies that the array contains data and options for a particular
|
||||
* type of "render element" (examples: 'form', for an HTML form; 'textfield',
|
||||
* 'submit', and other HTML form element types; 'table', for a table with
|
||||
* rows, columns, and headers). Modules define render elements by implementing
|
||||
* hook_element_info(), which specifies the properties that are used in render
|
||||
* arrays to provide the data and options, and default values for these
|
||||
* properties. Look through implementations of hook_element_info() to discover
|
||||
* what render elements are available.
|
||||
* rows, columns, and headers). See @ref elements below for more on render
|
||||
* element types.
|
||||
* - #theme: Specifies that the array contains data to be themed by a particular
|
||||
* theme hook. Modules define theme hooks by implementing hook_theme(), which
|
||||
* specifies the input "variables" used to provide data and options; if a
|
||||
|
@ -214,15 +221,29 @@
|
|||
* normally preferable to use #theme or #type instead, so that the theme can
|
||||
* customize the markup.
|
||||
*
|
||||
* For further information on the Theme and Render APIs, see:
|
||||
* - https://drupal.org/documentation/theme
|
||||
* - https://drupal.org/developing/modules/8
|
||||
* - https://drupal.org/node/722174
|
||||
* - https://drupal.org/node/933976
|
||||
* - https://drupal.org/node/930760
|
||||
* @section elements Render elements
|
||||
* Render elements are defined by Drupal core and modules. The primary way to
|
||||
* define a render element is to create a render element plugin. There are
|
||||
* two types of render element plugins:
|
||||
* - Generic elements: Generic render element plugins implement
|
||||
* \Drupal\Core\Render\Element\ElementInterface, are annotated with
|
||||
* \Drupal\Core\Render\Annotation\RenderElement annotation, go in plugin
|
||||
* namespace Element, and generally extend the
|
||||
* \Drupal\Core\Render\Element\RenderElement base class.
|
||||
* - Form input elements: Render elements representing form input elements
|
||||
* implement \Drupal\Core\Render\Element\FormElementInterface, are annotated
|
||||
* with \Drupal\Core\Render\Annotation\FormElement annotation, go in plugin
|
||||
* namespace Element, and generally extend the
|
||||
* \Drupal\Core\Render\Element\FormElement base class.
|
||||
* See the @link plugin_api Plugin API topic @endlink for general information
|
||||
* on plugins, and look for classes with the RenderElement or FormElement
|
||||
* annotation to discover what render elements are available.
|
||||
*
|
||||
* Modules can also currently define render elements by implementing
|
||||
* hook_element_info(), although defining a plugin is preferred.
|
||||
* properties. Look through implementations of hook_element_info() to discover
|
||||
* elements defined this way.
|
||||
*
|
||||
* @todo Check these links. Some are for Drupal 7, and might need updates for
|
||||
* Drupal 8.
|
||||
* @see themeable
|
||||
*
|
||||
* @}
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Render\ElementInfoManagerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Render;
|
||||
|
||||
use Drupal\Core\Render\ElementInfoManager;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Render\ElementInfoManager
|
||||
* @group Render
|
||||
*/
|
||||
class ElementInfoManagerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The class under test.
|
||||
*
|
||||
* @var \Drupal\Core\Render\ElementInfoManagerInterface
|
||||
*/
|
||||
protected $elementInfo;
|
||||
|
||||
/**
|
||||
* The cache backend to use.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The mocked module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @covers ::__construct
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
|
||||
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
|
||||
|
||||
$this->elementInfo = new ElementInfoManager(new \ArrayObject(), $this->cache, $this->moduleHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getInfo method.
|
||||
*
|
||||
* @covers ::getInfo
|
||||
* @covers ::buildInfo
|
||||
*
|
||||
* @dataProvider providerTestGetInfo
|
||||
*/
|
||||
public function testGetInfo($type, $expected_info, $element_info, callable $alter_callback = NULL) {
|
||||
$this->moduleHandler->expects($this->once())
|
||||
->method('invokeAll')
|
||||
->with('element_info')
|
||||
->will($this->returnValue($element_info));
|
||||
$this->moduleHandler->expects($this->once())
|
||||
->method('alter')
|
||||
->with('element_info', $this->anything())
|
||||
->will($this->returnCallback($alter_callback ?: function($info) {
|
||||
return $info;
|
||||
}));
|
||||
|
||||
$this->assertEquals($expected_info, $this->elementInfo->getInfo($type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides tests data for getInfo.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestGetInfo() {
|
||||
$data = array();
|
||||
// Provide an element and expect it is returned.
|
||||
$data[] = array(
|
||||
'page',
|
||||
array(
|
||||
'#type' => 'page',
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
),
|
||||
array('page' => array(
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
)),
|
||||
);
|
||||
// Provide an element but request an non existent one.
|
||||
$data[] = array(
|
||||
'form',
|
||||
array(
|
||||
),
|
||||
array('page' => array(
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
)),
|
||||
);
|
||||
// Provide an element and alter it to ensure it is altered.
|
||||
$data[] = array(
|
||||
'page',
|
||||
array(
|
||||
'#type' => 'page',
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
'#number' => 597219,
|
||||
),
|
||||
array('page' => array(
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
)),
|
||||
function ($alter_name, array &$info) {
|
||||
$info['page']['#number'] = 597219;
|
||||
}
|
||||
);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getInfo() method when render element plugins are used.
|
||||
*
|
||||
* @covers ::getInfo
|
||||
* @covers ::buildInfo
|
||||
*
|
||||
* @dataProvider providerTestGetInfoElementPlugin
|
||||
*/
|
||||
public function testGetInfoElementPlugin($plugin_class, $expected_info) {
|
||||
$this->moduleHandler->expects($this->once())
|
||||
->method('invokeAll')
|
||||
->with('element_info')
|
||||
->willReturn(array());
|
||||
$this->moduleHandler->expects($this->once())
|
||||
->method('alter')
|
||||
->with('element_info', $this->anything())
|
||||
->will($this->returnArgument(0));
|
||||
|
||||
$plugin = $this->getMock($plugin_class);
|
||||
$plugin->expects($this->once())
|
||||
->method('getInfo')
|
||||
->willReturn(array(
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
));
|
||||
|
||||
$element_info = $this->getMockBuilder('Drupal\Core\Render\ElementInfoManager')
|
||||
->setConstructorArgs(array(new \ArrayObject(), $this->cache, $this->moduleHandler))
|
||||
->setMethods(array('getDefinitions', 'createInstance'))
|
||||
->getMock();
|
||||
$element_info->expects($this->once())
|
||||
->method('createInstance')
|
||||
->with('page')
|
||||
->willReturn($plugin);
|
||||
$element_info->expects($this->once())
|
||||
->method('getDefinitions')
|
||||
->willReturn(array(
|
||||
'page' => array('class' => 'TestElementPlugin'),
|
||||
));
|
||||
|
||||
$this->assertEquals($expected_info, $element_info->getInfo('page'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides tests data for testGetInfoElementPlugin().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestGetInfoElementPlugin() {
|
||||
$data = array();
|
||||
$data[] = array(
|
||||
'Drupal\Core\Render\Element\ElementInterface',
|
||||
array(
|
||||
'#type' => 'page',
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
),
|
||||
);
|
||||
|
||||
$data[] = array(
|
||||
'Drupal\Core\Render\Element\FormElementInterface',
|
||||
array(
|
||||
'#type' => 'page',
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
'#input' => TRUE,
|
||||
'#value_callback' => array('TestElementPlugin', 'valueCallback'),
|
||||
),
|
||||
);
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Render\ElementInfoTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Render;
|
||||
|
||||
use Drupal\Core\Render\ElementInfo;
|
||||
use Drupal\Core\Render\ElementInfoInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Render\ElementInfo
|
||||
* @group Render
|
||||
*/
|
||||
class ElementInfoTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The class under test.
|
||||
*
|
||||
* @var \Drupal\Core\Render\ElementInfoInterface
|
||||
*/
|
||||
protected $elementInfo;
|
||||
|
||||
/**
|
||||
* The mocked module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @covers ::__construct
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
|
||||
|
||||
$this->elementInfo = new ElementInfo($this->moduleHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getInfo method.
|
||||
*
|
||||
* @covers ::getInfo
|
||||
* @covers ::buildInfo
|
||||
*
|
||||
* @dataProvider providerTestGetInfo
|
||||
*/
|
||||
public function testGetInfo($type, $expected_info, $element_info, callable $alter_callback = NULL) {
|
||||
$this->moduleHandler->expects($this->once())
|
||||
->method('invokeAll')
|
||||
->with('element_info')
|
||||
->will($this->returnValue($element_info));
|
||||
$this->moduleHandler->expects($this->once())
|
||||
->method('alter')
|
||||
->with('element_info', $this->anything())
|
||||
->will($this->returnCallback($alter_callback ?: function($info) {
|
||||
return $info;
|
||||
}));
|
||||
|
||||
$this->assertEquals($expected_info, $this->elementInfo->getInfo($type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides tests data for getInfo.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestGetInfo() {
|
||||
$data = array();
|
||||
// Provide an element and expect it is returned.
|
||||
$data[] = array(
|
||||
'page',
|
||||
array(
|
||||
'#type' => 'page',
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
),
|
||||
array('page' => array(
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
)),
|
||||
);
|
||||
// Provide an element but request an non existent one.
|
||||
$data[] = array(
|
||||
'form',
|
||||
array(
|
||||
),
|
||||
array('page' => array(
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
)),
|
||||
);
|
||||
// Provide an element and alter it to ensure it is altered.
|
||||
$data[] = array(
|
||||
'page',
|
||||
array(
|
||||
'#type' => 'page',
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
'#number' => 597219,
|
||||
),
|
||||
array('page' => array(
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
)),
|
||||
function ($alter_name, array &$info) {
|
||||
$info['page']['#number'] = 597219;
|
||||
}
|
||||
);
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue