Newer
Older

Dries Buytaert
committed
/**
* @file
* Functions for form and batch generation and processing.
*/

Alex Pott
committed
use Drupal\Component\Utility\Color;

Dries Buytaert
committed
use Drupal\Component\Utility\NestedArray;

catch
committed
use Drupal\Component\Utility\Number;
use Drupal\Component\Utility\SafeMarkup;

Angie Byron
committed
use Drupal\Component\Utility\String;

Dries Buytaert
committed
use Drupal\Component\Utility\UrlHelper;

Angie Byron
committed
use Drupal\Component\Utility\Xss;
use Drupal\Core\Database\Database;

Dries Buytaert
committed
use Drupal\Core\Form\FormStateInterface;

Alex Pott
committed
use Drupal\Core\Form\OptGroup;

Angie Byron
committed
use Drupal\Core\Render\Element;
use Drupal\Core\Template\Attribute;
use Symfony\Component\HttpFoundation\RedirectResponse;

Angie Byron
committed
/**

Dries Buytaert
committed
* Fetches a form from the cache.

Alex Pott
committed
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::formBuilder()->getCache().
*
* @see \Drupal\Core\Form\FormBuilderInterface::getCache().
*/

Dries Buytaert
committed
function form_get_cache($form_build_id, FormStateInterface $form_state) {

Alex Pott
committed
return \Drupal::formBuilder()->getCache($form_build_id, $form_state);
}
/**

Dries Buytaert
committed
* Stores a form in the cache.

Alex Pott
committed
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::formBuilder()->setCache().
*
* @see \Drupal\Core\Form\FormBuilderInterface::setCache().
*/

Alex Pott
committed
function form_set_cache($form_build_id, $form, FormStateInterface $form_state) {

Alex Pott
committed
\Drupal::formBuilder()->setCache($form_build_id, $form, $form_state);

Dries Buytaert
committed
}

Dries Buytaert
committed
/**
* Ensures an include file is loaded whenever the form is processed.

Dries Buytaert
committed
*
* Example:
* @code
* // Load node.admin.inc from Node module.
* form_load_include($form_state, 'inc', 'node', 'node.admin');
* @endcode
*
* Use this function instead of module_load_include() from inside a form
* constructor or any form processing logic as it ensures that the include file
* is loaded whenever the form is processed. In contrast to using
* module_load_include() directly, form_load_include() makes sure the include
* file is correctly loaded also if the form is cached.
*
* @param $form_state
* The current state of the form.
* @param $type
* The include file's type (file extension).
* @param $module
* The module to which the include file belongs.
* @param $name
* (optional) The base file name (without the $type extension). If omitted,
* $module is used; i.e., resulting in "$module.$type" by default.
*
* @return
* The filepath of the loaded include file, or FALSE if the include file was
* not found or has been loaded already.
*
* @see module_load_include()
*/

Dries Buytaert
committed
function form_load_include(FormStateInterface $form_state, $type, $module, $name = NULL) {

Dries Buytaert
committed
if (!isset($name)) {
$name = $module;
}
if (!isset($form_state['build_info']['files']["$module:$name.$type"])) {
// Only add successfully included files to the form state.
if ($result = module_load_include($type, $module, $name)) {
$form_state['build_info']['files']["$module:$name.$type"] = array(
'type' => $type,
'module' => $module,
'name' => $name,
);
return $result;
}
}
return FALSE;
}
/**

Dries Buytaert
committed
* Retrieves, populates, and processes a form.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::formBuilder()->submitForm().
*
* @see \Drupal\Core\Form\FormBuilderInterface::submitForm().
*/

Dries Buytaert
committed
function drupal_form_submit($form_arg, FormStateInterface $form_state) {

Alex Pott
committed
\Drupal::formBuilder()->submitForm($form_arg, $form_state);
}

Dries Buytaert
committed
/**

Dries Buytaert
committed
* Processes a form submission.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::formBuilder()->processForm().
*
* @see \Drupal\Core\Form\FormBuilderInterface::processForm().

Dries Buytaert
committed
function drupal_process_form($form_id, &$form, FormStateInterface $form_state) {

Alex Pott
committed
\Drupal::formBuilder()->processForm($form_id, $form, $form_state);

Dries Buytaert
committed
}

Dries Buytaert
committed
/**

Dries Buytaert
committed
* Executes custom validation and submission handlers for a given form.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use either \Drupal::service('form_submitter')->executeSubmitHandlers() or

Alex Pott
committed
* \Drupal::service('form_validator')->executeValidateHandlers().
*
* @see \Drupal\Core\Form\FormSubmitterInterface::executeSubmitHandlers()

Alex Pott
committed
* @see \Drupal\Core\Form\FormValidatorInterface::executeValidateHandlers()

Dries Buytaert
committed
*/

Dries Buytaert
committed
function form_execute_handlers($type, &$form, FormStateInterface $form_state) {

Alex Pott
committed
if ($type == 'submit') {
\Drupal::service('form_submitter')->executeSubmitHandlers($form, $form_state);

Alex Pott
committed
}
elseif ($type == 'validate') {
\Drupal::service('form_validator')->executeValidateHandlers($form, $form_state);
}

Dries Buytaert
committed
}
/**

Dries Buytaert
committed
* Files an error against a form element.
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use $form_state->setErrorByName().
*
* @see \Drupal\Core\Form\FormStateInterface::setErrorByName().
*/

Dries Buytaert
committed
function form_set_error($name, FormStateInterface $form_state, $message = '') {
$form_state->setErrorByName($name, $message);
}
/**

Dries Buytaert
committed
* Returns an associative array of all errors.

Alex Pott
committed
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use $form_state->getErrors().
*
* @see \Drupal\Core\Form\FormStateInterface::getErrors()
*/

Dries Buytaert
committed
function form_get_errors(FormStateInterface $form_state) {
return $form_state->getErrors();
}
/**

Dries Buytaert
committed
* Returns the error message filed against the given form element.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use $form_state->getError().
*
* @see \Drupal\Core\Form\FormStateInterface::getError().
*/

Dries Buytaert
committed
function form_get_error($element, FormStateInterface $form_state) {
return $form_state->getError($element);
}

Dries Buytaert
committed
* Flags an element as having an error.

Alex Pott
committed
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use $form_state->setError().
*
* @see \Drupal\Core\Form\FormStateInterface::setError().

Dries Buytaert
committed
function form_error(&$element, FormStateInterface $form_state, $message = '') {
$form_state->setError($element, $message);

Dries Buytaert
committed
* Builds and processes all elements in the structured form array.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::formBuilder()->doBuildForm().
*
* @see \Drupal\Core\Form\FormBuilderInterface::doBuildForm().

Dries Buytaert
committed
function form_builder($form_id, &$element, FormStateInterface $form_state) {

Alex Pott
committed
return \Drupal::formBuilder()->doBuildForm($form_id, $element, $form_state);
}
/**
* Removes internal Form API elements and buttons from submitted form values.
*
* This function can be used when a module wants to store all submitted form
* values, for example, by serializing them into a single database column. In
* such cases, all internal Form API values and all form button elements should
* not be contained, and this function allows to remove them before the module
* proceeds to storage. Next to button elements, the following internal values
* are removed:
* - form_id
* - form_token
* - form_build_id
* - op
*
* @param $form_state

Dries Buytaert
committed
* The current state of the form, including submitted form values.
*/

Dries Buytaert
committed
function form_state_values_clean(FormStateInterface $form_state) {
// Remove internal Form API values.

Alex Pott
committed
$form_state
->unsetValue('form_id')
->unsetValue('form_token')
->unsetValue('form_build_id')
->unsetValue('op');
// Remove button values.
// form_builder() collects all button elements in a form. We remove the button
// value separately for each button element.
foreach ($form_state['buttons'] as $button) {
// Remove this button's value from the submitted form values by finding
// the value corresponding to this button.
// We iterate over the #parents of this button and move a reference to

Alex Pott
committed
// each parent in $form_state->getValues(). For example, if #parents is:
// array('foo', 'bar', 'baz')

Alex Pott
committed
// then the corresponding $form_state->getValues() part will look like this:
// array(
// 'foo' => array(
// 'bar' => array(
// 'baz' => 'button_value',
// ),
// ),
// )
// We start by (re)moving 'baz' to $last_parent, so we are able unset it
// at the end of the iteration. Initially, $values will contain a

Alex Pott
committed
// reference to $form_state->getValues(), but in the iteration we move the
// reference to $form_state->getValue('foo'), and finally to
// $form_state->getValue(array('foo', 'bar')), which is the level where we
// can unset 'baz' (that is stored in $last_parent).
$parents = $button['#parents'];
$last_parent = array_pop($parents);
$key_exists = NULL;

Alex Pott
committed
$values = &NestedArray::getValue($form_state->getValues(), $parents, $key_exists);
if ($key_exists && is_array($values)) {
unset($values[$last_parent]);
}
}
}

Gábor Hojtsy
committed
/**

Dries Buytaert
committed
* Determines the value for an image button form element.

Gábor Hojtsy
committed
*
* @param $form
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,

Gábor Hojtsy
committed
* the element's default value should be returned.
* @param $form_state

Dries Buytaert
committed
* The current state of the form.

Dries Buytaert
committed
*

Gábor Hojtsy
committed
* @return

Alex Pott
committed
* The data that will appear in the $form_state->getValues() collection

Gábor Hojtsy
committed
* for this element. Return nothing to use the default.
*/

Alex Pott
committed
function form_type_image_button_value($form, $input, FormStateInterface $form_state) {
if ($input !== FALSE) {
if (!empty($input)) {

Gábor Hojtsy
committed
// If we're dealing with Mozilla or Opera, we're lucky. It will
// return a proper value, and we can get on with things.
return $form['#return_value'];
}
else {
// Unfortunately, in IE we never get back a proper value for THIS
// form element. Instead, we get back two split values: one for the
// X and one for the Y coordinates on which the user clicked the
// button. We'll find this element in the #post data, and search
// in the same spot for its name, with '_x'.
$input = $form_state['input'];
foreach (explode('[', $form['#name']) as $element_name) {

Gábor Hojtsy
committed
// chop off the ] that may exist.
if (substr($element_name, -1) == ']') {
$element_name = substr($element_name, 0, -1);
}
if (!isset($input[$element_name])) {
if (isset($input[$element_name . '_x'])) {

Gábor Hojtsy
committed
return $form['#return_value'];
}
return NULL;
}
$input = $input[$element_name];

Gábor Hojtsy
committed
}
return $form['#return_value'];
}
}
}

Dries Buytaert
committed
/**

Dries Buytaert
committed
* Determines the value for a checkbox form element.

Dries Buytaert
committed
*
* @param $form

Dries Buytaert
committed
* The form element whose value is being populated.

Dries Buytaert
committed
* @param $input
* The incoming input to populate the form element. If this is FALSE,

Dries Buytaert
committed
* the element's default value should be returned.

Dries Buytaert
committed
*

Dries Buytaert
committed
* @return
* The data that will appear in the $element_state['values'] collection

Dries Buytaert
committed
* for this element. Return nothing to use the default.

Dries Buytaert
committed
*/
function form_type_checkbox_value($element, $input = FALSE) {

Dries Buytaert
committed
if ($input === FALSE) {
// Use #default_value as the default value of a checkbox, except change

Alex Pott
committed
// NULL to 0, because FormBuilder::handleInputElement() would otherwise

Dries Buytaert
committed
// 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

Alex Pott
committed
// FormBuilder::handleInputElement() detects this, and calls this

Dries Buytaert
committed
// 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.

Dries Buytaert
committed
return isset($input) ? $element['#return_value'] : 0;

Dries Buytaert
committed
}
}
/**

Dries Buytaert
committed
* Determines the value for a checkboxes form element.

Dries Buytaert
committed
*

Dries Buytaert
committed
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,

Dries Buytaert
committed
* the element's default value should be returned.

Dries Buytaert
committed
*

Dries Buytaert
committed
* @return
* The data that will appear in the $element_state['values'] collection

Dries Buytaert
committed
* for this element. Return nothing to use the default.

Dries Buytaert
committed
*/
function form_type_checkboxes_value($element, $input = FALSE) {
if ($input === FALSE) {

Dries Buytaert
committed
$value = array();
$element += array('#default_value' => array());
foreach ($element['#default_value'] as $key) {
$value[$key] = $key;

Dries Buytaert
committed
}
return $value;
}

Dries Buytaert
committed
elseif (is_array($input)) {
// Programmatic form submissions use NULL to indicate that a checkbox
// should be unchecked; see drupal_form_submit(). We therefore remove all
// NULL elements from the array before constructing the return value, to
// simulate the behavior of web browsers (which do not send unchecked
// checkboxes to the server at all). This will not affect non-programmatic

Angie Byron
committed
// form submissions, since all values in \Drupal::request()->request are
// strings.

Dries Buytaert
committed
foreach ($input as $key => $value) {
if (!isset($value)) {

Dries Buytaert
committed
unset($input[$key]);
}
}

catch
committed
return array_combine($input, $input);

Dries Buytaert
committed
}

Dries Buytaert
committed
else {

Dries Buytaert
committed
return array();
}

Dries Buytaert
committed
}
/**
* Determines the value of a table form element.
*
* @param array $element
* The form element whose value is being populated.
* @param array|false $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
*
* @return array

Alex Pott
committed
* The data that will appear in the $form_state->getValues() collection
* for this element. Return nothing to use the default.
*/
function form_type_table_value(array $element, $input = FALSE) {
// If #multiple is FALSE, the regular default value of radio buttons is used.
if (!empty($element['#tableselect']) && !empty($element['#multiple'])) {
// Contrary to #type 'checkboxes', the default value of checkboxes in a
// table is built from the array keys (instead of array values) of the
// #default_value property.
// @todo D8: Remove this inconsistency.
if ($input === FALSE) {
$element += array('#default_value' => array());

catch
committed
$value = array_keys(array_filter($element['#default_value']));
return array_combine($value, $value);
}
else {

catch
committed
return is_array($input) ? array_combine($input, $input) : array();
}
}
}
/**
* Form value callback: Determines the value for a #type radios form element.
*
* @param $element
* The form element whose value is being populated.
* @param $input
* (optional) The incoming input to populate the form element. If FALSE, the
* element's default value is returned. Defaults to FALSE.
*
* @return
* The data that will appear in the $element_state['values'] collection for
* this element.
*/
function form_type_radios_value(&$element, $input = FALSE) {
if ($input !== FALSE) {

Angie Byron
committed
// When there's user input (including NULL), return it as the value.

Alex Pott
committed
// However, if NULL is submitted, FormBuilder::handleInputElement() will

Angie Byron
committed
// apply the default value, and we want that validated against #options
// unless it's empty. (An empty #default_value, such as NULL or FALSE, can
// be used to indicate that no radio button is selected by default.)
if (!isset($input) && !empty($element['#default_value'])) {
$element['#needs_validation'] = TRUE;
}
return $input;
}

Angie Byron
committed
else {
// For default value handling, simply return #default_value. Additionally,
// for a NULL default value, set #has_garbage_value to prevent

Alex Pott
committed
// FormBuilder::handleInputElement() converting the NULL to an empty

Angie Byron
committed
// string, so that code can distinguish between nothing selected and the
// selection of a radio button whose value is an empty string.
$value = isset($element['#default_value']) ? $element['#default_value'] : NULL;
if (!isset($value)) {
$element['#has_garbage_value'] = TRUE;
}
return $value;
}
}

Angie Byron
committed
/**

Dries Buytaert
committed
* Determines the value for a tableselect form element.

Angie Byron
committed
*
* @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.

Dries Buytaert
committed
*

Angie Byron
committed
* @return
* The data that will appear in the $element_state['values'] collection
* for this element. Return nothing to use the default.
*/
function form_type_tableselect_value($element, $input = FALSE) {
// If $element['#multiple'] == FALSE, then radio buttons are displayed and
// the default value handling is used.
if (isset($element['#multiple']) && $element['#multiple']) {
// Checkboxes are being displayed with the default value coming from the
// keys of the #default_value property. This differs from the checkboxes
// element which uses the array values.
if ($input === FALSE) {
$value = array();
$element += array('#default_value' => array());
foreach ($element['#default_value'] as $key => $flag) {
if ($flag) {
$value[$key] = $key;
}
}
return $value;
}
else {

catch
committed
return is_array($input) ? array_combine($input, $input) : array();

Angie Byron
committed
}
}
}

Dries Buytaert
committed
/**

Dries Buytaert
committed
* Determines the value for a password_confirm form element.

Dries Buytaert
committed
*

Dries Buytaert
committed
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,

Dries Buytaert
committed
* the element's default value should be returned.

Dries Buytaert
committed
*

Dries Buytaert
committed
* @return
* The data that will appear in the $element_state['values'] collection

Dries Buytaert
committed
* for this element. Return nothing to use the default.

Dries Buytaert
committed
*/
function form_type_password_confirm_value($element, $input = FALSE) {
if ($input === FALSE) {
$element += array('#default_value' => array());
return $element['#default_value'] + array('pass1' => '', 'pass2' => '');

Dries Buytaert
committed
}
}
/**

Dries Buytaert
committed
* Determines the value for a select form element.

Dries Buytaert
committed
*

Dries Buytaert
committed
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,

Dries Buytaert
committed
* the element's default value should be returned.

Dries Buytaert
committed
*

Dries Buytaert
committed
* @return
* The data that will appear in the $element_state['values'] collection

Dries Buytaert
committed
* for this element. Return nothing to use the default.

Dries Buytaert
committed
*/
function form_type_select_value($element, $input = FALSE) {
if ($input !== FALSE) {
if (isset($element['#multiple']) && $element['#multiple']) {

Dries Buytaert
committed
// If an enabled multi-select submits NULL, it means all items are
// unselected. A disabled multi-select always submits NULL, and the
// default value should be used.
if (empty($element['#disabled'])) {

catch
committed
return (is_array($input)) ? array_combine($input, $input) : array();

Dries Buytaert
committed
}
else {
return (isset($element['#default_value']) && is_array($element['#default_value'])) ? $element['#default_value'] : array();
}

Dries Buytaert
committed
}

Dries Buytaert
committed
// Non-multiple select elements may have an empty option preprended to them
// (see form_process_select()). When this occurs, usually #empty_value is
// an empty string, but some forms set #empty_value to integer 0 or some
// other non-string constant. PHP receives all submitted form input as
// strings, but if the empty option is selected, set the value to match the
// empty value exactly.
elseif (isset($element['#empty_value']) && $input === (string) $element['#empty_value']) {
return $element['#empty_value'];
}

Dries Buytaert
committed
else {

Dries Buytaert
committed
}
}
}
/**

Dries Buytaert
committed
* Determines the value for a textfield form element.

Dries Buytaert
committed
*

Angie Byron
committed
* @deprecated Use \Drupal\Core\Render\Element\Textfield::valueCallback().

Dries Buytaert
committed
*/

Angie Byron
committed
function form_type_textfield_value(&$element, $input, &$form_state) {
return Element\Textfield::valueCallback($element, $input, $form_state);

Dries Buytaert
committed
}
/**

Dries Buytaert
committed
* Determines the value for form's token value.

Dries Buytaert
committed
*

Dries Buytaert
committed
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,

Dries Buytaert
committed
* the element's default value should be returned.

Dries Buytaert
committed
*

Dries Buytaert
committed
* @return
* The data that will appear in the $element_state['values'] collection

Dries Buytaert
committed
* for this element. Return nothing to use the default.

Dries Buytaert
committed
*/
function form_type_token_value($element, $input = FALSE) {
if ($input !== FALSE) {
return (string) $input;

Dries Buytaert
committed
}
}

Dries Buytaert
committed
* Changes submitted form values during form validation.
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.

Angie Byron
committed
* Use $form_state->setValueForElement().
*

Angie Byron
committed
* @see \Drupal\Core\Form\FormStateInterface::setValueForElement().

Dries Buytaert
committed
function form_set_value($element, $value, FormStateInterface $form_state) {

Angie Byron
committed
$form_state->setValueForElement($element, $value);
/**
* Allows PHP array processing of multiple select options with the same value.
*
* Used for form select elements which need to validate HTML option groups
* and multiple options which may return the same value. Associative PHP arrays
* cannot handle these structures, since they share a common key.
*
* @param $array
* The form options array to process.
*
* @return
* An array with all hierarchical elements flattened to a single array.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.

Alex Pott
committed
* Use \Drupal\Core\Form\OptGroup::flattenOptions().

Angie Byron
committed
function form_options_flatten($array) {

Alex Pott
committed
return OptGroup::flattenOptions($array);

Dries Buytaert
committed
/**
* Processes a select list form element.
*
* This process callback is mandatory for select fields, since all user agents
* automatically preselect the first available option of single (non-multiple)
* select lists.
*
* @param $element
* The form element to process. Properties used:
* - #multiple: (optional) Indicates whether one or more options can be
* selected. Defaults to FALSE.
* - #default_value: Must be NULL or not set in case there is no value for the
* element yet, in which case a first default option is inserted by default.
* Whether this first option is a valid option depends on whether the field
* is #required or not.
* - #required: (optional) Whether the user needs to select an option (TRUE)

Dries Buytaert
committed
* or not (FALSE). Defaults to FALSE.

Dries Buytaert
committed
* - #empty_option: (optional) The label to show for the first default option.
* By default, the label is automatically set to "- Please select -" for a
* required field and "- None -" for an optional field.
* - #empty_value: (optional) The value for the first default option, which is

Dries Buytaert
committed
* used to determine whether the user submitted a value or not.
* - If #required is TRUE, this defaults to '' (an empty string).
* - If #required is not TRUE and this value isn't set, then no extra option
* is added to the select control, leaving the control in a slightly
* illogical state, because there's no way for the user to select nothing,
* since all user agents automatically preselect the first available
* option. But people are used to this being the behavior of select
* controls.
* @todo Address the above issue in Drupal 8.
* - If #required is not TRUE and this value is set (most commonly to an
* empty string), then an extra option (see #empty_option above)
* representing a "non-selection" is added with this as its value.

Dries Buytaert
committed
*
* @see _form_validate()
*/
function form_process_select($element) {
// #multiple select fields need a special #name.
if ($element['#multiple']) {
$element['#attributes']['multiple'] = 'multiple';
$element['#attributes']['name'] = $element['#name'] . '[]';
}
// A non-#multiple select needs special handling to prevent user agents from
// preselecting the first option without intention. #multiple select lists do
// not get an empty option, as it would not make sense, user interface-wise.
else {

Angie Byron
committed
// If the element is set to #required through #states, override the
// element's #required setting.
$required = isset($element['#states']['required']) ? TRUE : $element['#required'];

Dries Buytaert
committed
// If the element is required and there is no #default_value, then add an
// empty option that will fail validation, so that the user is required to
// make a choice. Also, if there's a value for #empty_value or
// #empty_option, then add an option that represents emptiness.
if (($required && !isset($element['#default_value'])) || isset($element['#empty_value']) || isset($element['#empty_option'])) {

Dries Buytaert
committed
$element += array(
'#empty_value' => '',
'#empty_option' => $required ? t('- Select -') : t('- None -'),

Dries Buytaert
committed
);
// The empty option is prepended to #options and purposively not merged
// to prevent another option in #options mistakenly using the same value
// as #empty_value.
$empty_option = array($element['#empty_value'] => $element['#empty_option']);
$element['#options'] = $empty_option + $element['#options'];
}
}
return $element;
}
* Prepares variables for select element templates.
*
* Default template: select.html.twig.
*
* It is possible to group options together; to do this, change the format of
* $options to an associative array in which the keys are group labels, and the
* values are associative arrays in the normal $options format.

Dries Buytaert
committed
* @param $variables
* An associative array containing:
* - element: An associative array containing the properties of the element.
* Properties used: #title, #value, #options, #description, #extra,
* #multiple, #required, #name, #attributes, #size.
function template_preprocess_select(&$variables) {

Dries Buytaert
committed
$element = $variables['element'];

Angie Byron
committed
Element::setAttributes($element, array('id', 'name', 'size'));
_form_set_attributes($element, array('form-select'));

Dries Buytaert
committed
$variables['attributes'] = $element['#attributes'];
$variables['options'] = form_select_options($element);

Dries Buytaert
committed
}

Angie Byron
committed
/**

Dries Buytaert
committed
* Converts a select form element's options array into HTML.

Angie Byron
committed
*
* @param $element
* An associative array containing the properties of the element.
* @param $choices
* Mixed: Either an associative array of items to list as choices, or an
* object with an 'option' member that is an associative array. This
* parameter is only used internally and should not be passed.

Dries Buytaert
committed
*

Angie Byron
committed
* @return
* An HTML string of options for the select form element.
*/

Dries Buytaert
committed
function form_select_options($element, $choices = NULL) {
if (!isset($choices)) {
if (empty($element['#options'])) {
return '';
}

Dries Buytaert
committed
$choices = $element['#options'];
}
// array_key_exists() accommodates the rare event where $element['#value'] is NULL.
// isset() fails in this situation.
$value_valid = isset($element['#value']) || array_key_exists('#value', $element);

Dries Buytaert
committed
$value_is_array = $value_valid && is_array($element['#value']);
// Check if the element is multiple select and no value has been selected.
$empty_value = (empty($element['#value']) && !empty($element['#multiple']));

Dries Buytaert
committed
$options = '';
foreach ($choices as $key => $choice) {

Alex Pott
committed
$options .= '<optgroup label="' . String::checkPlain($key) . '">';

Dries Buytaert
committed
$options .= form_select_options($element, $choice);
$options .= '</optgroup>';
elseif (is_object($choice) && isset($choice->option)) {

Neil Drumm
committed
$options .= form_select_options($element, $choice->option);
}
$key = (string) $key;
$empty_choice = $empty_value && $key == '_none';
if ($value_valid && ((!$value_is_array && (string) $element['#value'] === $key || ($value_is_array && in_array($key, $element['#value']))) || $empty_choice)) {
$selected = ' selected="selected"';
}
else {
$selected = '';
}

Angie Byron
committed
$options .= '<option value="' . String::checkPlain($key) . '"' . $selected . '>' . String::checkPlain($choice) . '</option>';
return SafeMarkup::set($options);

Steven Wittens
committed
/**

Dries Buytaert
committed
* Returns the indexes of a select element's options matching a given key.
*
* This function is useful if you need to modify the options that are
* already in a form element; for example, to remove choices which are
* not valid because of additional filters imposed by another module.
* One example might be altering the choices in a taxonomy selector.
* To correctly handle the case of a multiple hierarchy taxonomy,
* #options arrays can now hold an array of objects, instead of a
* direct mapping of keys to labels, so that multiple choices in the
* selector can have the same key (and label). This makes it difficult
* to manipulate directly, which is why this helper function exists.
*
* This function does not support optgroups (when the elements of the
* #options array are themselves arrays), and will return FALSE if
* arrays are found. The caller must either flatten/restore or
* manually do their manipulations in this case, since returning the
* index is not sufficient, and supporting this would make the
* "helper" too complicated and cumbersome to be of any help.
*
* As usual with functions that can return array() or FALSE, do not
* forget to use === and !== if needed.

Steven Wittens
committed
*
* @param $element
* The select element to search.

Steven Wittens
committed
* @param $key
* The key to look for.

Dries Buytaert
committed
*

Steven Wittens
committed
* @return
* An array of indexes that match the given $key. Array will be
* empty if no elements were found. FALSE if optgroups were found.

Steven Wittens
committed
*/
function form_get_options($element, $key) {
$keys = array();
foreach ($element['#options'] as $index => $choice) {
if (is_array($choice)) {
return FALSE;
}

Angie Byron
committed
elseif (is_object($choice)) {
if (isset($choice->option[$key])) {
$keys[] = $index;
}
}

Angie Byron
committed
elseif ($index == $key) {
$keys[] = $index;

Steven Wittens
committed
}
}
return $keys;

Steven Wittens
committed
}

Angie Byron
committed
* Prepares variables for fieldset element templates.

Angie Byron
committed
* Default template: fieldset.html.twig.
*
* @param array $variables

Dries Buytaert
committed
* An associative array containing:
* - element: An associative array containing the properties of the element.

Angie Byron
committed
* Properties used: #attributes, #children, #description, #id, #title,
* #value.

Angie Byron
committed
function template_preprocess_fieldset(&$variables) {

Dries Buytaert
committed
$element = $variables['element'];

Angie Byron
committed
Element::setAttributes($element, array('id'));
_form_set_attributes($element, array('form-wrapper'));

Angie Byron
committed
$variables['attributes'] = $element['#attributes'];
$variables['attributes']['class'][] = 'form-item';
$variables['prefix'] = isset($element['#field_prefix']) ? $element['#field_prefix'] : NULL;
$variables['suffix'] = isset($element['#field_suffix']) ? $element['#field_suffix'] : NULL;
$variables['children'] = $element['#children'];

Dries Buytaert
committed
$legend_attributes = array();
if (isset($element['#title_display']) && $element['#title_display'] == 'invisible') {
$legend_attributes['class'][] = 'visually-hidden';

Dries Buytaert
committed
}

Angie Byron
committed
$variables['legend']['attributes'] = new Attribute($legend_attributes);
$variables['legend']['title'] = (isset($element['#title']) && $element['#title'] !== '') ? Xss::filterAdmin($element['#title']) : '';
$legend_span_attributes = array('class' => array('fieldset-legend'));
if (!empty($element['#required'])) {
$legend_span_attributes['class'][] = 'form-required';
$variables['legend_span']['attributes'] = new Attribute($legend_span_attributes);
}
if (!empty($element['#description'])) {

Angie Byron
committed
$description_id = $element['#attributes']['id'] . '--description';
$description_attributes = array(
'class' => 'description',
'id' => $description_id,
);
$variables['description']['attributes'] = new Attribute($description_attributes);
$variables['description']['content'] = $element['#description'];
// Add the description's id to the fieldset aria attributes.
$variables['attributes']['aria-describedby'] = $description_id;

Angie Byron
committed
}
/**
* Prepares variables for details element templates.
*
* Default template: details.html.twig.
*
* @param array $variables
* An associative array containing:
* - element: An associative array containing the properties of the element.

Angie Byron
committed
* Properties used: #attributes, #children, #open,

Angie Byron
committed
* #description, #id, #title, #value, #optional.
*/
function template_preprocess_details(&$variables) {
$element = $variables['element'];
$variables['attributes'] = $element['#attributes'];
$variables['summary_attributes'] = new Attribute();
if (!empty($element['#title'])) {
$variables['summary_attributes']['role'] = 'button';
if (!empty($element['#attributes']['id'])) {
$variables['summary_attributes']['aria-controls'] = $element['#attributes']['id'];

Angie Byron
committed
$variables['summary_attributes']['aria-expanded'] = !empty($element['#attributes']['open']);
$variables['summary_attributes']['aria-pressed'] = $variables['summary_attributes']['aria-expanded'];
}
$variables['title'] = (!empty($element['#title'])) ? $element['#title'] : '';
$variables['description'] = (!empty($element['#description'])) ? $element['#description'] : '';
$variables['children'] = (isset($element['#children'])) ? $element['#children'] : '';
$variables['value'] = (isset($element['#value'])) ? $element['#value'] : '';
}
* Prepares a #type 'radio' render element for theme_input().
*
* @param array $element
* An associative array containing the properties of the element.
* Properties used: #required, #return_value, #value, #attributes, #title,
* #description.

Angie Byron
committed
* Note: The input "name" attribute needs to be sanitized before output, which
* is currently done by initializing Drupal\Core\Template\Attribute with
* all the attributes.

Angie Byron
committed
*
* @return array
* The $element with prepared variables ready for theme_input().
function form_pre_render_radio($element) {

Dries Buytaert
committed
$element['#attributes']['type'] = 'radio';

Angie Byron
committed
Element::setAttributes($element, array('id', 'name', '#return_value' => 'value'));

Dries Buytaert
committed

Angie Byron
committed
if (isset($element['#return_value']) && $element['#value'] !== FALSE && $element['#value'] == $element['#return_value']) {

Dries Buytaert
committed
$element['#attributes']['checked'] = 'checked';
}
_form_set_attributes($element, array('form-radio'));

Angie Byron
committed
return $element;
* Prepares variables for radios templates.
* Default template: radios.html.twig.
*
* @param array $variables

Dries Buytaert
committed
* An associative array containing:
* - element: An associative array containing the properties of the element.
* Properties used: #title, #value, #options, #description, #required,
* #attributes, #children.
function template_preprocess_radios(&$variables) {

Dries Buytaert
committed
$element = $variables['element'];
$variables['attributes'] = array();

Dries Buytaert
committed
if (isset($element['#id'])) {
$variables['attributes']['id'] = $element['#id'];

Dries Buytaert
committed
}
$variables['attributes']['class'][] = 'form-radios';
if (!empty($element['#attributes']['class'])) {
$variables['attributes']['class'] = array_merge($variables['attributes']['class'], $element['#attributes']['class']);
if (isset($element['#attributes']['title'])) {
$variables['attributes']['title'] = $element['#attributes']['title'];
$variables['children'] = $element['#children'];
}

Dries Buytaert
committed
/**

Gerhard Killesreiter
committed
* Expand a password_confirm field into two text boxes.
*/
function form_process_password_confirm($element) {
$element['pass1'] = array(
'#type' => 'password',
'#title' => t('Password'),

Dries Buytaert
committed
'#value' => empty($element['#value']) ? NULL : $element['#value']['pass1'],

Dries Buytaert
committed
'#required' => $element['#required'],
'#attributes' => array('class' => array('password-field')),
);
$element['pass2'] = array(
'#type' => 'password',
'#title' => t('Confirm password'),

Dries Buytaert
committed
'#value' => empty($element['#value']) ? NULL : $element['#value']['pass2'],

Dries Buytaert
committed
'#required' => $element['#required'],
'#attributes' => array('class' => array('password-confirm')),

Dries Buytaert
committed
$element['#element_validate'] = array('password_confirm_validate');

Gerhard Killesreiter
committed
$element['#tree'] = TRUE;
if (isset($element['#size'])) {
$element['pass1']['#size'] = $element['pass2']['#size'] = $element['#size'];
}

Gerhard Killesreiter
committed
return $element;
}
/**

Dries Buytaert
committed
* Validates a password_confirm element.
*/
function password_confirm_validate($element, &$element_state) {
$pass1 = trim($element['pass1']['#value']);
$pass2 = trim($element['pass2']['#value']);
if (!empty($pass1) || !empty($pass2)) {
if (strcmp($pass1, $pass2)) {

Angie Byron
committed
form_error($element, $element_state, t('The specified passwords do not match.'));
}

Dries Buytaert
committed
}
elseif ($element['#required'] && !empty($element_state['input'])) {

Angie Byron
committed
form_error($element, $element_state, t('Password field is required.'));
}

Gerhard Killesreiter
committed

Gerhard Killesreiter
committed
// Password field must be converted from a two-element array into a single
// string regardless of validation results.
form_set_value($element['pass1'], NULL, $element_state);
form_set_value($element['pass2'], NULL, $element_state);
form_set_value($element, $pass1, $element_state);

Gerhard Killesreiter
committed

Dries Buytaert
committed
}
* Adds form-specific attributes to a 'date' #type element.

Dries Buytaert
committed
* Supports HTML5 types of 'date', 'datetime', 'datetime-local', and 'time'.
* Falls back to a plain textfield. Used as a sub-element by the datetime
* element type.
*
* @param array $element
* An associative array containing the properties of the element.
* Properties used: #title, #value, #options, #description, #required,
* #attributes, #id, #name, #type, #min, #max, #step, #value, #size.

Dries Buytaert
committed
*
* Note: The input "name" attribute needs to be sanitized before output, which