Commit 9db4af56 authored by webchick's avatar webchick

Issue #2326891 by tim.plunkett, jhodgdon: Convert system_element_info() to Element classes.

parent 9c537ec4
......@@ -163,10 +163,10 @@
/**
* Form element processing handler for the #ajax form property.
*
* @deprecated Use \Drupal\Core\Render\Element\FormElement::processAjaxForm().
* @deprecated Use \Drupal\Core\Render\Element\RenderElement::processAjaxForm().
*/
function ajax_process_form($element, FormStateInterface $form_state, &$complete_form) {
return Element\FormElement::processAjaxForm($element, $form_state, $complete_form);
function ajax_process_form(&$element, FormStateInterface $form_state, &$complete_form) {
return Element\RenderElement::processAjaxForm($element, $form_state, $complete_form);
}
/**
......
This diff is collapsed.
This diff is collapsed.
......@@ -11,7 +11,6 @@
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\StorageException;
......@@ -801,25 +800,6 @@ function theme_disable($theme_list) {
return \Drupal::service('theme_handler')->disable($theme_list);
}
/**
* Renders a twig string directly.
*
* @param string $template_string
* The template string to render with placeholders.
* @param array $context
* An array of parameters to pass to the template.
*
* @return string
* The rendered inline template.
*/
function drupal_render_inline_template(&$element) {
/** @var \Drupal\Core\Template\TwigEnvironment $environment */
$environment = \Drupal::service('twig');
$markup = $environment->renderInline($element['#template'], $element['#context']);
$element['#markup'] = $markup;
return $element;
}
/**
* @addtogroup themeable
* @{
......@@ -1088,129 +1068,6 @@ function template_preprocess_image(&$variables) {
}
}
/**
* #pre_render callback to transform children of an element into #rows suitable for theme_table().
*
* This function converts sub-elements of an element of #type 'table' to be
* suitable for theme_table():
* - The first level of sub-elements are table rows. Only the #attributes
* property is taken into account.
* - The second level of sub-elements is converted into columns for the
* corresponding first-level table row.
*
* Simple example usage:
* @code
* $form['table'] = array(
* '#type' => 'table',
* '#header' => array(t('Title'), array('data' => t('Operations'), 'colspan' => '1')),
* // Optionally, to add tableDrag support:
* '#tabledrag' => array(
* array(
* 'action' => 'order',
* 'relationship' => 'sibling',
* 'group' => 'thing-weight',
* ),
* ),
* );
* foreach ($things as $row => $thing) {
* $form['table'][$row]['#weight'] = $thing['weight'];
*
* $form['table'][$row]['title'] = array(
* '#type' => 'textfield',
* '#default_value' => $thing['title'],
* );
*
* // Optionally, to add tableDrag support:
* $form['table'][$row]['#attributes']['class'][] = 'draggable';
* $form['table'][$row]['weight'] = array(
* '#type' => 'textfield',
* '#title' => t('Weight for @title', array('@title' => $thing['title'])),
* '#title_display' => 'invisible',
* '#size' => 4,
* '#default_value' => $thing['weight'],
* '#attributes' => array('class' => array('thing-weight')),
* );
*
* // The amount of link columns should be identical to the 'colspan'
* // attribute in #header above.
* $form['table'][$row]['edit'] = array(
* '#type' => 'link',
* '#title' => t('Edit'),
* '#href' => 'thing/' . $row . '/edit',
* );
* }
* @endcode
*
* @param array $element
* A structured array containing two sub-levels of elements. Properties used:
* - #tabledrag: The value is a list of $options arrays that are passed to
* drupal_attach_tabledrag(). The HTML ID of the table is added to each
* $options array.
*
* @see system_element_info()
* @see theme_table()
* @see drupal_process_attached()
* @see drupal_attach_tabledrag()
*/
function drupal_pre_render_table(array $element) {
foreach (Element::children($element) as $first) {
$row = array('data' => array());
// Apply attributes of first-level elements as table row attributes.
if (isset($element[$first]['#attributes'])) {
$row += $element[$first]['#attributes'];
}
// Turn second-level elements into table row columns.
// @todo Do not render a cell for children of #type 'value'.
// @see http://drupal.org/node/1248940
foreach (Element::children($element[$first]) as $second) {
// Assign the element by reference, so any potential changes to the
// original element are taken over.
$column = array('data' => &$element[$first][$second]);
// Apply wrapper attributes of second-level elements as table cell
// attributes.
if (isset($element[$first][$second]['#wrapper_attributes'])) {
$column += $element[$first][$second]['#wrapper_attributes'];
}
$row['data'][] = $column;
}
$element['#rows'][] = $row;
}
// Take over $element['#id'] as HTML ID attribute, if not already set.
Element::setAttributes($element, array('id'));
// Add sticky headers, if applicable.
if (count($element['#header']) && $element['#sticky']) {
$element['#attached']['library'][] = 'core/drupal.tableheader';
// Add 'sticky-enabled' class to the table to identify it for JS.
// This is needed to target tables constructed by this function.
$element['#attributes']['class'][] = 'sticky-enabled';
}
// If the table has headers and it should react responsively to columns hidden
// with the classes represented by the constants RESPONSIVE_PRIORITY_MEDIUM
// and RESPONSIVE_PRIORITY_LOW, add the tableresponsive behaviors.
if (count($element['#header']) && $element['#responsive']) {
$element['#attached']['library'][] = 'core/drupal.tableresponsive';
// Add 'responsive-enabled' class to the table to identify it for JS.
// This is needed to target tables constructed by this function.
$element['#attributes']['class'][] = 'responsive-enabled';
}
// If the custom #tabledrag is set and there is a HTML ID, add the table's
// HTML ID to the options and attach the behavior.
if (!empty($element['#tabledrag']) && isset($element['#attributes']['id'])) {
foreach ($element['#tabledrag'] as $options) {
$options['table_id'] = $element['#attributes']['id'];
drupal_attach_tabledrag($element, $options);
}
}
return $element;
}
/**
* Prepares variables for table templates.
*
......@@ -1732,38 +1589,10 @@ function _template_preprocess_default_variables() {
/**
* #pre_render callback for the html element type.
*
* @param array $element
* A structured array containing the html element type build properties.
*
* @see system_element_info()
* @deprecated Use \Drupal\Core\Render\Element\Html::preRenderHtml().
*/
function drupal_pre_render_html(array $element) {
// Add favicon.
if (theme_get_setting('features.favicon')) {
$favicon = theme_get_setting('favicon.url');
$type = theme_get_setting('favicon.mimetype');
$element['#attached']['drupal_add_html_head_link'][][] = array(
'rel' => 'shortcut icon',
'href' => UrlHelper::stripDangerousProtocols($favicon),
'type' => $type,
);
}
return $element;
}
/**
* #pre_render callback for the page element type.
*
* @param array $element
* A structured array containing the page element type build properties.
*
* @see system_element_info()
*/
function drupal_pre_render_page(array $element) {
$element['#cache']['tags']['theme'] = \Drupal::theme()->getActiveTheme()->getName();
$element['#cache']['tags']['theme_global_settings'] = TRUE;
return $element;
return Element\Html::preRenderHtml($element);
}
/**
......
......@@ -9,6 +9,7 @@
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Render\Element\Email;
use Drupal\Core\TypedData\DataDefinition;
/**
......@@ -42,7 +43,7 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
'columns' => array(
'value' => array(
'type' => 'varchar',
'length' => EMAIL_MAX_LENGTH,
'length' => Email::EMAIL_MAX_LENGTH,
'not null' => FALSE,
),
),
......@@ -59,8 +60,8 @@ public function getConstraints() {
$constraints[] = $constraint_manager->create('ComplexData', array(
'value' => array(
'Length' => array(
'max' => EMAIL_MAX_LENGTH,
'maxMessage' => t('%name: the email address can not be longer than @max characters.', array('%name' => $this->getFieldDefinition()->getLabel(), '@max' => EMAIL_MAX_LENGTH)),
'max' => Email::EMAIL_MAX_LENGTH,
'maxMessage' => t('%name: the email address can not be longer than @max characters.', array('%name' => $this->getFieldDefinition()->getLabel(), '@max' => Email::EMAIL_MAX_LENGTH)),
)
),
));
......
<?php
/**
* @file
* Contains \Drupal\Core\Render\Element\Actions.
*/
namespace Drupal\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
/**
* Provides a wrapper element to group one or more buttons in a form.
*
* Use of the 'actions' element as an array key helps to ensure proper styling
* in themes and to enable other modules to properly alter a form's actions.
*
* @RenderElement("actions")
*/
class Actions extends Container {
/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return array(
'#process' => array(
// @todo Move this to #pre_render.
array($class, 'preRenderActionsDropbutton'),
array($class, 'processActions'),
array($class, 'processContainer'),
),
'#weight' => 100,
'#theme_wrappers' => array('container'),
);
}
/**
* Processes a form actions container element.
*
* @param array $element
* An associative array containing the properties and children of the
* form actions container.
* @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 processActions(&$element, FormStateInterface $form_state, &$complete_form) {
$element['#attributes']['class'][] = 'form-actions';
return $element;
}
/**
* #pre_render callback for #type 'actions'.
*
* This callback iterates over all child elements of the #type 'actions'
* container to look for elements with a #dropbutton property, so as to group
* those elements into dropbuttons. As such, it works similar to #group, but is
* specialized for dropbuttons.
*
* The value of #dropbutton denotes the dropbutton to group the child element
* into. For example, two different values of 'foo' and 'bar' on child elements
* would generate two separate dropbuttons, which each contain the corresponding
* buttons.
*
* @param array $element
* The #type 'actions' element to process.
* @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 #type 'actions' element, including individual buttons grouped
* into new #type 'dropbutton' elements.
*/
public static function preRenderActionsDropbutton(&$element, FormStateInterface $form_state, &$complete_form) {
$dropbuttons = array();
foreach (Element::children($element, TRUE) as $key) {
if (isset($element[$key]['#dropbutton'])) {
$dropbutton = $element[$key]['#dropbutton'];
// If there is no dropbutton for this button group yet, create one.
if (!isset($dropbuttons[$dropbutton])) {
$dropbuttons[$dropbutton] = array(
'#type' => 'dropbutton',
);
}
// Add this button to the corresponding dropbutton.
// @todo Change #type 'dropbutton' to be based on theme_item_list()
// instead of links.html.twig to avoid this preemptive rendering.
$button = drupal_render($element[$key]);
$dropbuttons[$dropbutton]['#links'][$key] = array(
'title' => $button,
'html' => TRUE,
);
}
}
// @todo For now, all dropbuttons appear first. Consider to invent a more
// fancy sorting/injection algorithm here.
return $dropbuttons + $element;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Render\Element\Ajax.
*/
namespace Drupal\Core\Render\Element;
/**
* Provides a render element for adding Ajax to a render element.
*
* Holds an array whose values control the Ajax behavior of the element.
*
* @ingroup ajax
*
* @RenderElement("ajax")
*/
class Ajax extends RenderElement {
/**
* {@inheritdoc}
*/
public function getInfo() {
// By default, we don't want Ajax commands being rendered in the context of
// an HTML page, so we don't provide defaults for #theme or #theme_wrappers.
// However, modules can set these properties (for example, to provide an
// HTML debugging page that displays rather than executes Ajax commands).
return array(
'#header' => TRUE,
'#commands' => array(),
'#error' => NULL,
);
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Render\Element\Button.
*/
namespace Drupal\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
/**
* Provides an action button form element.
*
* When the button is pressed, the form will be submitted to Drupal, where it is
* validated and rebuilt. The submit handler is not invoked.
*
* @FormElement("button")
*/
class Button extends FormElement {
/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return array(
'#input' => TRUE,
'#name' => 'op',
'#is_button' => TRUE,
'#executes_submit_callback' => FALSE,
'#limit_validation_errors' => FALSE,
'#process' => array(
array($class, 'processButton'),
array($class, 'processAjaxForm'),
),
'#pre_render' => array(
array($class, 'preRenderButton'),
),
'#theme_wrappers' => array('input__submit'),
);
}
/**
* Processes a form button element.
*/
public static function processButton(&$element, FormStateInterface $form_state, &$complete_form) {
// If this is a button intentionally allowing incomplete form submission
// (e.g., a "Previous" or "Add another item" button), then also skip
// client-side validation.
if (isset($element['#limit_validation_errors']) && $element['#limit_validation_errors'] !== FALSE) {
$element['#attributes']['formnovalidate'] = 'formnovalidate';
}
return $element;
}
/**
* Prepares a #type 'button' render element for theme_input().
*
* @param array $element
* An associative array containing the properties of the element.
* Properties used: #attributes, #button_type, #name, #value.
*
* The #button_type property accepts any value, though core themes have CSS that
* styles the following button_types appropriately: 'primary', 'danger'.
*
* @return array
* The $element with prepared variables ready for theme_input().
*/
public static function preRenderButton($element) {
$element['#attributes']['type'] = 'submit';
Element::setAttributes($element, array('id', 'name', 'value'));
$element['#attributes']['class'][] = 'button';
if (!empty($element['#button_type'])) {
$element['#attributes']['class'][] = 'button--' . $element['#button_type'];
}
// @todo Various JavaScript depends on this button class.
$element['#attributes']['class'][] = 'form-submit';
if (!empty($element['#attributes']['disabled'])) {
$element['#attributes']['class'][] = 'is-disabled';
}
return $element;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Render\Element\Checkbox.
*/
namespace Drupal\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
/**
* Provides a form element for a single checkbox.
*
* @see \Drupal\Core\Render\Element\Checkboxes
*
* @FormElement("checkbox")
*/
class Checkbox extends FormElement {
/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return array(
'#input' => TRUE,
'#return_value' => 1,
'#process' => array(
array($class, 'processCheckbox'),
array($class, 'processAjaxForm'),
array($class, 'processGroup'),
),
'#pre_render' => array(
array($class, 'preRenderCheckbox'),
array($class, 'preRenderGroup'),
),
'#theme' => 'input__checkbox',
'#theme_wrappers' => array('form_element'),
'#title_display' => 'after',
);
}
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input === FALSE) {
// Use #default_value as the default value of a checkbox, except change
// NULL to 0, because FormBuilder::handleInputElement() would otherwise
// replace NULL with empty string, but an empty string is a potentially
// valid value for a checked checkbox.
return isset($element['#default_value']) ? $element['#default_value'] : 0;
}
else {
// Checked checkboxes are submitted with a value (possibly '0' or ''):
// http://www.w3.org/TR/html401/interact/forms.html#successful-controls.
// For checked checkboxes, browsers submit the string version of
// #return_value, but we return the original #return_value. For unchecked
// checkboxes, browsers submit nothing at all, but
// FormBuilder::handleInputElement() detects this, and calls this
// function with $input=NULL. Returning NULL from a value callback means
// to use the default value, which is not what is wanted when an unchecked
// checkbox is submitted, so we use integer 0 as the value indicating an
// unchecked checkbox. Therefore, modules must not use integer 0 as a
// #return_value, as doing so results in the checkbox always being treated
// as unchecked. The string '0' is allowed for #return_value. The most
// common use-case for setting #return_value to either 0 or '0' is for the
// first option within a 0-indexed array of checkboxes, and for this,
// form_process_checkboxes() uses the string rather than the integer.
return isset($input) ? $element['#return_value'] : 0;
}
}
/**
* Prepares a #type 'checkbox' render element for theme_input().
*
* @param array $element
* An associative array containing the properties of the element.
* Properties used: #title, #value, #return_value, #description, #required,
* #attributes, #checked.
*
* @return array
* The $element with prepared variables ready for theme_input().
*/
public static function preRenderCheckbox($element) {
$element['#attributes']['type'] = 'checkbox';
Element::setAttributes($element, array('id', 'name', '#return_value' => 'value'));
// Unchecked checkbox has #value of integer 0.
if (!empty($element['#checked'])) {
$element['#attributes']['checked'] = 'checked';
}
static::setAttributes($element, array('form-checkbox'));
return $element;
}
/**
* Sets the #checked property of a checkbox element.
*/
public static function processCheckbox(&$element, FormStateInterface $form_state, &$complete_form) {
$value = $element['#value'];
$return_value = $element['#return_value'];
// On form submission, the #value of an available and enabled checked
// checkbox is #return_value, and the #value of an available and enabled
// unchecked checkbox is integer 0. On not submitted forms, and for
// checkboxes with #access=FALSE or #disabled=TRUE, the #value is
// #default_value (integer 0 if #default_value is NULL). Most of the time,
// a string comparison of #value and #return_value is sufficient for
// determining the "checked" state, but a value of TRUE always means checked
// (even if #return_value is 'foo'), and a value of FALSE or integer 0
// always means unchecked (even if #return_value is '' or '0').
if ($value === TRUE || $value === FALSE || $value === 0) {
$element['#checked'] = (bool) $value;
}
else {
// Compare as strings, so that 15 is not considered equal to '15foo', but
// 1 is considered equal to '1'. This cast does not imply that either
// #value or #return_value is expected to be a string.
$element['#checked'] = ((string) $value === (string) $return_value);
}
return $element;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Render\Element\Checkboxes.
*/
namespace Drupal\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides a form element for a set of checkboxes.
*
* #options is an associative array, where the key is the #return_value of the
* checkbox and the value is displayed. The #options array cannot have a 0 key,
* as it would not be possible to discern checked and unchecked states.
*
* @see \Drupal\Core\Render\Element\Radios
* @see \Drupal\Core\Render\Element\Checkbox
*
* @FormElement("checkboxes")
*/
class Checkboxes extends FormElement {
use CompositeFormElementTrait;
/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return array(
'#input' => TRUE,
'#process' => array(
array($class, 'processCheckboxes'),
),
'#pre_render' => array(
array($class, 'preRenderCompositeFormElement'),
),
'#theme_wrappers' => array('checkboxes'),
);
}
/**
* Processes a checkboxes form element.
*/
public static function processCheckboxes(&$element, FormStateInterface $form_state, &$complete_form) {
$value = is_array($element['#value']) ? $element['#value'] : array();
$element['#tree'] = TRUE;
if (count($element['#options']) > 0) {
if (!isset($element['#default_value']) || $element['#default_value'] == 0) {
$element['#default_value'] = array();
}
$weight = 0;
foreach ($element['#options'] as $key => $choice) {
// Integer 0 is not a valid #return_value, so use '0' instead.
// @see form_type_checkbox_value().
// @todo For Drupal 8, cast all integer keys to strings for consistency
// with form_process_radios().
if ($key === 0) {
$key = '0';
}
// Maintain order of options as defined in #options, in case the element
// defines custom option sub-elements, but does not define all option
// sub-elements.
$weight += 0.001;
$element += array($key => array());
$element[$key] += array(
'#type' => 'checkbox',
'#title' => $choice,
'#return_value' => $key,
'#default_value' => isset($value[$key]) ? $key : NULL,
'#attributes' => $element['#attributes'],
'#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
'#weight' => $weight,
);
}
}
return $element;
}
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input === FALSE) {
$value = array();
$element += array('#default_value' => array());