diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc
index 54de9e8..55b4a43 100644
--- a/core/includes/ajax.inc
+++ b/core/includes/ajax.inc
@@ -5,7 +5,7 @@
* Functions for use with Drupal's Ajax framework.
*/
-use Drupal\Core\Render\Element\Textfield;
+use Drupal\Core\Render\Element;
/**
* @defgroup ajax Ajax framework
@@ -230,16 +230,10 @@
/**
* Form element processing handler for the #ajax form property.
*
- * @param $element
- * An associative array containing the properties of the element.
- *
- * @return
- * The processed element.
- *
- * @see ajax_pre_render_element()
+ * @deprecated Use \Drupal\Core\Render\Element\FormElement::processAjaxForm().
*/
-function ajax_process_form($element, &$form_state) {
- return Textfield::processAjaxForm($element, $form_state);
+function ajax_process_form(&$element, &$form_state, &$complete_form) {
+ return Element\FormElement::processAjaxForm($element, $form_state, $complete_form);
}
/**
diff --git a/core/includes/common.inc b/core/includes/common.inc
index da65525..16468b3 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -2702,70 +2702,6 @@ 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'.
- */
-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;
-}
-
-/**
* Pre-render callback: Collects child links into a single array.
*
* This function can be added as a pre_render callback for a renderable array,
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 280d232..9e0b7c7 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -14,7 +14,6 @@
use Drupal\Core\Database\Database;
use Drupal\Core\Form\OptGroup;
use Drupal\Core\Render\Element;
-use Drupal\Core\Render\Element\Textfield;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Utility\Color;
use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -1428,19 +1427,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 $form_state array 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, &$form_state) {
- return Textfield::processPattern($element, $form_state);
+function form_process_pattern(&$element, &$form_state, &$complete_form) {
+ return Element\FormElement::processPattern($element, $form_state, $complete_form);
}
/**
@@ -1825,167 +1815,28 @@ function form_validate_table($element, &$form_state) {
/**
* Processes a machine-readable name form element.
*
- * @param $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) A 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) Should be set to the maximum allowed length of the
- * machine name. Defaults to 64.
- * - #disabled: (optional) Should be set to TRUE in case an existing machine
- * name must not be changed after initial creation.
+ * @deprecated Use \Drupal\Core\Render\Element\MachineName::processMachineName().
*/
-function form_process_machine_name($element, &$form_state) {
- // 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'] . ' ');
- }
- 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'] . ' ');
-
- $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;
+function form_process_machine_name(&$element, &$form_state, &$complete_form) {
+ return Element\MachineName::processMachineName($element, $form_state, $complete_form);
}
/**
* Form element validation handler for machine_name elements.
*
- * Note that #maxlength is validated by _form_validate() already.
+ * @deprecated Use \Drupal\Core\Render\Element\MachineName::validateMachineName().
*/
-function form_validate_machine_name(&$element, &$form_state) {
- // 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.'));
- }
- }
+function form_validate_machine_name(&$element, &$form_state, &$complete_form) {
+ Element\MachineName::validateMachineName($element, $form_state, $complete_form);
}
/**
* Arranges elements into groups.
*
- * @param $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 $form_state
- * The $form_state array for the form this element belongs to.
- *
- * @return
- * The processed element.
+ * @deprecated Use \Drupal\Core\Render\Element\FormElement::processGroup().
*/
-function form_process_group(&$element, &$form_state) {
- return Textfield::processGroup($element, $form_state);
+function form_process_group(&$element, &$form_state, &$complete_form) {
+ return Element\FormElement::processGroup($element, $form_state, $complete_form);
}
/**
@@ -2025,15 +1876,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) {
- return Textfield::preRenderGroup($element);
+ return Element\RenderElement::preRenderGroup($element);
}
/**
@@ -2121,30 +1967,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 array $form_state
- * An associative array containing the current state of the form.
- *
- * @return array
- * The form element.
+ * @deprecated Use \Drupal\Core\Render\Element\FormElement::processAutocomplete().
*/
-function form_process_autocomplete($element, &$form_state) {
- return Textfield::processAutocomplete($element, $form_state);
+function form_process_autocomplete(&$element, &$form_state, &$complete_form) {
+ return Element\FormElement::processAutocomplete($element, $form_state, $complete_form);
}
/**
@@ -2250,16 +2076,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) {
- return Textfield::preRenderTextfield($element);
+ return Element\Textfield::preRenderTextfield($element);
}
/**
diff --git a/core/lib/Drupal/Core/Render/ElementInterface.php b/core/lib/Drupal/Core/Render/Element/ElementInterface.php
similarity index 84%
rename from core/lib/Drupal/Core/Render/ElementInterface.php
rename to core/lib/Drupal/Core/Render/Element/ElementInterface.php
index b3ec1ab..7eb753a 100644
--- a/core/lib/Drupal/Core/Render/ElementInterface.php
+++ b/core/lib/Drupal/Core/Render/Element/ElementInterface.php
@@ -2,10 +2,10 @@
/**
* @file
- * Contains \Drupal\Core\Render\ElementInterface.
+ * Contains \Drupal\Core\Render\Element\ElementInterface.
*/
-namespace Drupal\Core\Render;
+namespace Drupal\Core\Render\Element;
use Drupal\Component\Plugin\PluginInspectionInterface;
diff --git a/core/lib/Drupal/Core/Render/Element/Textfield.php b/core/lib/Drupal/Core/Render/Element/FormElement.php
similarity index 53%
copy from core/lib/Drupal/Core/Render/Element/Textfield.php
copy to core/lib/Drupal/Core/Render/Element/FormElement.php
index be9a870..d3ee790 100644
--- a/core/lib/Drupal/Core/Render/Element/Textfield.php
+++ b/core/lib/Drupal/Core/Render/Element/FormElement.php
@@ -2,61 +2,89 @@
/**
* @file
- * Contains \Drupal\Core\Render\Element\Textfield.
+ * Contains \Drupal\Core\Render\Element\FormElement.
*/
namespace Drupal\Core\Render\Element;
-use Drupal\Core\Render\Element;
-use Drupal\Core\Render\FormElementBase;
-
/**
- * Formats a single-line text field.
- *
- * @FormElement("textfield")
+ * @todo.
*/
-class Textfield extends FormElementBase {
+abstract class FormElement extends RenderElement {
/**
- * {@inheritdoc}
+ * Form element processing handler for the #ajax form property.
+ *
+ * @param array $element
+ * An associative array containing the properties of the element.
+ * @param array $form_state
+ * An associative array containing 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 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'),
- );
+ public static function processAjaxForm(&$element, &$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 array $form_state
+ * An associative array containing the current state of the form.
+ * @param array $complete_form
+ * The complete form structure.
+ *
+ * @return array
+ * The processed element.
+ */
+ public static function processGroup(&$element, &$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 $element
+ * @param array $element
* An associative array containing the properties and children of the
* generic input element.
- * @param $form_state
- * The $form_state array for the form this element belongs to.
+ * @param array $form_state
+ * An associative array containing the current state of the form.
+ * @param array $complete_form
+ * The complete form structure.
*
- * @return
+ * @return array
* The processed element.
*
* @see form_validate_pattern()
*/
- public static function processPattern($element, &$form_state) {
+ public static function processPattern(&$element, &$form_state, &$complete_form) {
if (isset($element['#pattern']) && !isset($element['#attributes']['pattern'])) {
$element['#attributes']['pattern'] = $element['#pattern'];
$element['#element_validate'][] = 'form_validate_pattern';
@@ -87,11 +115,13 @@ public static function processPattern($element, &$form_state) {
* with the route name.
* @param array $form_state
* An associative array containing the current state of the form.
+ * @param array $complete_form
+ * The complete form structure.
*
* @return array
* The form element.
*/
- public static function processAutocomplete($element, &$form_state) {
+ public static function processAutocomplete(&$element, &$form_state, &$complete_form) {
$access = FALSE;
if (!empty($element['#autocomplete_route_name'])) {
$parameters = isset($element['#autocomplete_route_parameters']) ? $element['#autocomplete_route_parameters'] : array();
@@ -105,24 +135,6 @@ public static function processAutocomplete($element, &$form_state) {
// Provide a data attribute for the JavaScript behavior to bind to.
$element['#attributes']['data-autocomplete-path'] = $path;
}
- return $element;
- }
-
- /**
- * Prepares a #type 'textfield' render element for theme_input().
- *
- * @param array $element
- * An associative array containing the properties of the element.
- * Properties used: #title, #value, #description, #size, #maxlength,
- * #placeholder, #required, #attributes.
- *
- * @return array
- * The $element with prepared variables ready for theme_input().
- */
- public static function preRenderTextfield($element) {
- $element['#attributes']['type'] = 'text';
- Element::setAttributes($element, array('id', 'name', 'value', 'size', 'maxlength', 'placeholder'));
- _form_set_attributes($element, array('form-text'));
return $element;
}
diff --git a/core/lib/Drupal/Core/Render/Element/Link.php b/core/lib/Drupal/Core/Render/Element/Link.php
new file mode 100644
index 0000000..d97ad4c
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Element/Link.php
@@ -0,0 +1,93 @@
+ 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) 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 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;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Render/Element/MachineName.php b/core/lib/Drupal/Core/Render/Element/MachineName.php
new file mode 100644
index 0000000..457bb62
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Element/MachineName.php
@@ -0,0 +1,216 @@
+ 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'),
+ );
+ }
+
+ /**
+ * 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) A 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) Should be set to the maximum allowed length of the
+ * machine name. Defaults to 64.
+ * - #disabled: (optional) Should be set to TRUE in case an existing machine
+ * name must not be changed after initial creation.
+ * @param array $form_state
+ * An associative array containing the current state of the form.
+ * @param array $complete_form
+ * The complete form structure.
+ *
+ * @return array
+ * The processed element.
+ */
+ public static function processMachineName(&$element, &$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'] . ' ');
+ }
+ 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'] . ' ');
+
+ $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, &$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.'));
+ }
+ }
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Render/ElementBase.php b/core/lib/Drupal/Core/Render/Element/RenderElement.php
similarity index 92%
rename from core/lib/Drupal/Core/Render/ElementBase.php
rename to core/lib/Drupal/Core/Render/Element/RenderElement.php
index 9061aed..cf6ad20 100644
--- a/core/lib/Drupal/Core/Render/ElementBase.php
+++ b/core/lib/Drupal/Core/Render/Element/RenderElement.php
@@ -2,12 +2,13 @@
/**
* @file
- * Contains \Drupal\Core\Render\ElementBase.
+ * Contains \Drupal\Core\Render\Element\RenderElement.
*/
-namespace Drupal\Core\Render;
+namespace Drupal\Core\Render\Element;
use Drupal\Core\Plugin\PluginBase;
+use Drupal\Core\Render\Element;
/**
* Provides a base class for element plugins.
@@ -18,7 +19,7 @@
*
* @ingroup plugin_api
*/
-abstract class ElementBase extends PluginBase implements ElementInterface {
+abstract class RenderElement extends PluginBase implements ElementInterface {
/**
* Adds members of this group as actual elements for rendering.
diff --git a/core/lib/Drupal/Core/Render/Element/Textfield.php b/core/lib/Drupal/Core/Render/Element/Textfield.php
index be9a870..7799a03 100644
--- a/core/lib/Drupal/Core/Render/Element/Textfield.php
+++ b/core/lib/Drupal/Core/Render/Element/Textfield.php
@@ -8,14 +8,13 @@
namespace Drupal\Core\Render\Element;
use Drupal\Core\Render\Element;
-use Drupal\Core\Render\FormElementBase;
/**
* Formats a single-line text field.
*
* @FormElement("textfield")
*/
-class Textfield extends FormElementBase {
+class Textfield extends FormElement {
/**
* {@inheritdoc}
@@ -43,72 +42,6 @@ public function getInfo() {
}
/**
- * #process callback for #pattern form element property.
- *
- * @param $element
- * An associative array containing the properties and children of the
- * generic input element.
- * @param $form_state
- * The $form_state array for the form this element belongs to.
- *
- * @return
- * The processed element.
- *
- * @see form_validate_pattern()
- */
- public static function processPattern($element, &$form_state) {
- if (isset($element['#pattern']) && !isset($element['#attributes']['pattern'])) {
- $element['#attributes']['pattern'] = $element['#pattern'];
- $element['#element_validate'][] = 'form_validate_pattern';
- }
-
- return $element;
- }
-
- /**
- * Adds autocomplete functionality to elements with a valid
- * #autocomplete_route_name.
- *
- * Suppose your autocomplete route name is 'mymodule.autocomplete' and its path
- * is: '/mymodule/autocomplete/{a}/{b}'
- * In your form you have:
- * @code
- * '#autocomplete_route_name' => 'mymodule.autocomplete',
- * '#autocomplete_route_parameters' => array('a' => $some_key, 'b' => $some_id),
- * @endcode
- * The user types in "keywords" so the full path called is:
- * 'mymodule_autocomplete/$some_key/$some_id?q=keywords'
- *
- * @param array $element
- * The form element to process. Properties used:
- * - #autocomplete_route_name: A route to be used as callback URL by the
- * autocomplete JavaScript library.
- * - #autocomplete_route_parameters: The parameters to be used in conjunction
- * with the route name.
- * @param array $form_state
- * An associative array containing the current state of the form.
- *
- * @return array
- * The form element.
- */
- public static function processAutocomplete($element, &$form_state) {
- $access = FALSE;
- if (!empty($element['#autocomplete_route_name'])) {
- $parameters = isset($element['#autocomplete_route_parameters']) ? $element['#autocomplete_route_parameters'] : array();
-
- $path = \Drupal::urlGenerator()->generate($element['#autocomplete_route_name'], $parameters);
- $access = \Drupal::service('access_manager')->checkNamedRoute($element['#autocomplete_route_name'], $parameters, \Drupal::currentUser());
- }
- if ($access) {
- $element['#attributes']['class'][] = 'form-autocomplete';
- $element['#attached']['library'][] = 'core/drupal.autocomplete';
- // Provide a data attribute for the JavaScript behavior to bind to.
- $element['#attributes']['data-autocomplete-path'] = $path;
- }
- return $element;
- }
-
- /**
* Prepares a #type 'textfield' render element for theme_input().
*
* @param array $element
diff --git a/core/lib/Drupal/Core/Render/ElementInfoManager.php b/core/lib/Drupal/Core/Render/ElementInfoManager.php
index 418436a..43d0b86 100644
--- a/core/lib/Drupal/Core/Render/ElementInfoManager.php
+++ b/core/lib/Drupal/Core/Render/ElementInfoManager.php
@@ -75,7 +75,7 @@ protected function buildInfo() {
/**
* {@inheritdoc}
*
- * @return \Drupal\Core\Render\ElementInterface
+ * @return \Drupal\Core\Render\Element\ElementInterface
*/
public function createInstance($plugin_id, array $configuration = array()) {
return parent::createInstance($plugin_id, $configuration);
diff --git a/core/lib/Drupal/Core/Render/FormElementBase.php b/core/lib/Drupal/Core/Render/FormElementBase.php
deleted file mode 100644
index 28ff981..0000000
--- a/core/lib/Drupal/Core/Render/FormElementBase.php
+++ /dev/null
@@ -1,65 +0,0 @@
- '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,
@@ -568,9 +555,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'),