Newer
Older

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

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

catch
committed
use Drupal\Component\Utility\Number;

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;

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

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

Angie Byron
committed
* @defgroup forms Form builder functions
* @{
* Functions that build an abstract representation of a HTML form.
*
* All modules should declare their form builder functions to be in this
* group and each builder function should reference its validate and submit
* functions using \@see. Conversely, validate and submit functions should
* reference the form builder function using \@see. For examples, of this see
* system_modules_uninstall() or user_pass(), the latter of which has the
* following in its doxygen documentation:

Jennifer Hodgdon
committed
* - \@ingroup forms
* - \@see user_pass_validate()
* - \@see user_pass_submit()

Jennifer Hodgdon
committed
* @}
*/
/**
* @defgroup form_api Form generation

Jennifer Hodgdon
committed
* Describes how to generate and manipulate forms and process form submissions.

Jennifer Hodgdon
committed
* Drupal provides a Form API in order to achieve consistency in its form
* processing and presentation, while simplifying code and reducing the amount
* of HTML that must be explicitly generated by a module.

Dries Buytaert
committed
*

Jennifer Hodgdon
committed
* @section generating_forms Creating forms
* Forms are defined as classes that implement the
* \Drupal\Core\Form\FormInterface and are built using the
* \Drupal\Core\Form\FormBuilder class. Drupal provides a couple of utility
* classes that can be extended as a starting point for most basic forms, the
* most commonly used of which is \Drupal\Core\Form\FormBase. FormBuilder
* handles the low level processing of forms such as rendering the necessary
* HTML, initial processing of incoming $_POST data, and delegating to your
* implementation of FormInterface for validation and processing of submitted
* data.

Dries Buytaert
committed
*

Jennifer Hodgdon
committed
* Here is an example of a Form class:

Jennifer Hodgdon
committed
* namespace Drupal\mymodule\Form;
*
* use Drupal\Core\Form\FormBase;
*
* class ExampleForm extends FormBase {
* public function getFormId() {
* // Unique ID of the form.
* return 'example_form';
* }
*
* public function buildForm(array $form, array &$form_state) {
* // Create a $form API array.
* $form['phone_number'] = array(
* '#type' => 'tel',
* '#title' => $this->t('Your phone number')
* );
* return $form;
* }
*
* public function validateForm(array &$form, array &$form_state) {
* // Validate submitted form data.
* }
*
* public function submitForm(array &$form, array &$form_state) {
* // Handle submitted form data.
* }

Dries Buytaert
committed
* }

Dries Buytaert
committed
*

Jennifer Hodgdon
committed
* @section retrieving_forms Retrieving and displaying forms
* \Drupal::formBuilder()->getForm() should be used to handle retrieving,
* processing, and displaying a rendered HTML form. Given the ExampleForm
* defined above,
* \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm') would
* return the rendered HTML of the form defined by ExampleForm::buildForm(), or
* call the validateForm() and submitForm(), methods depending on the current
* processing state.
*
* The argument to \Drupal::formBuilder()->getForm() is the name of a class that
* implements FormBuilderInterface. Any additional arguments passed to the
* getForm() method will be passed along as additional arguments to the
* ExampleForm::buildForm() method.
*
* For example:

Dries Buytaert
committed
* @code

Jennifer Hodgdon
committed
* $extra = '612-123-4567';
* $form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm', $extra);

Dries Buytaert
committed
* ...

Jennifer Hodgdon
committed
* public function buildForm(array $form, array &$form_state, $extra = NULL)
* $form['phone_number'] = array(
* '#type' => 'tel',
* '#title' => $this->t('Your phone number'),

Dries Buytaert
committed
* '#value' => $extra,
* );

Dries Buytaert
committed
* }
* @endcode

Dries Buytaert
committed
*

Jennifer Hodgdon
committed
* Alternatively, forms can be built directly via the routing system which will
* take care of calling \Drupal::formBuilder()->getForm(). The following example
* demonstrates the use of a routing.yml file to display a form at the the
* given route.
*
* @code
* example.form:
* path: '/example-form'
* defaults:
* _title: 'Example form'
* _form: '\Drupal\mymodule\Form\ExampleForm'
* @endcode
*

Dries Buytaert
committed
* The $form argument to form-related functions is a structured array containing
* the elements and properties of the form. For information on the array
* components and format, and more detailed explanations of the Form API
* workflow, see the

Jennifer Hodgdon
committed
* @link forms_api_reference.html Form API reference @endlink

Dries Buytaert
committed
* and the

Jennifer Hodgdon
committed
* @link https://drupal.org/node/2117411 Form API documentation section. @endlink

Dries Buytaert
committed
* In addition, there is a set of Form API tutorials in
* @link form_example_tutorial.inc the Form Example Tutorial @endlink which
* provide basics all the way up through multistep forms.
*

Jennifer Hodgdon
committed
* In the form builder, validation, submission, and other form methods,

Dries Buytaert
committed
* $form_state is the primary influence on the processing of the form and is

Jennifer Hodgdon
committed
* passed by reference to most methods, so they can use it to communicate with

Dries Buytaert
committed
* the form system and each other.
*

Jennifer Hodgdon
committed
* See \Drupal\Core\Form\FormBuilder::buildForm() for documentation of
* $form_state keys.

Dries Buytaert
committed
* Returns a renderable form array for a given form ID.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::formBuilder()->getForm().
*
* @see \Drupal\Core\Form\FormBuilderInterface::getForm().

Dries Buytaert
committed
*/
function drupal_get_form($form_arg) {

Alex Pott
committed
return call_user_func_array(array(\Drupal::formBuilder(), 'getForm'), func_get_args());
}

Dries Buytaert
committed
/**

Dries Buytaert
committed
* Builds and processes a form for a given form ID.

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

Dries Buytaert
committed
*/
function drupal_build_form($form_id, &$form_state) {

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

Dries Buytaert
committed
}

Dries Buytaert
committed

Dries Buytaert
committed
/**

Dries Buytaert
committed
* Retrieves default values for the $form_state array.

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

Dries Buytaert
committed
*/
function form_state_defaults() {

Alex Pott
committed
return \Drupal::formBuilder()->getFormStateDefaults();

Dries Buytaert
committed
}

Gábor Hojtsy
committed
/**

Dries Buytaert
committed
* Constructs a new $form from the information in $form_state.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::formBuilder()->rebuildForm().
*
* @see \Drupal\Core\Form\FormBuilderInterface::rebuildForm().

Gábor Hojtsy
committed
*/

Dries Buytaert
committed
function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) {

Alex Pott
committed
return \Drupal::formBuilder()->rebuildForm($form_id, $form_state, $old_form);

Gábor Hojtsy
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().
*/
function form_get_cache($form_build_id, &$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().
*/
function form_set_cache($form_build_id, $form, $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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
*
* 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()
*/
function form_load_include(&$form_state, $type, $module, $name = NULL) {
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().
*/

Angie Byron
committed
function drupal_form_submit($form_arg, &$form_state) {

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

Dries Buytaert
committed
/**
* Retrieves the structured array that defines a given form.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::formBuilder()->retrieveForm().
*
* @see \Drupal\Core\Form\FormBuilderInterface::retrieveForm().

Dries Buytaert
committed
*/

Dries Buytaert
committed
function drupal_retrieve_form($form_id, &$form_state) {

Alex Pott
committed
return \Drupal::formBuilder()->retrieveForm($form_id, $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, &$form_state) {

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

Dries Buytaert
committed
}
/**

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

Dries Buytaert
committed
*/

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

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

Dries Buytaert
committed
/**

Dries Buytaert
committed
* Validates user-submitted form data in the $form_state array.

Dries Buytaert
committed
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::formBuilder()->validateForm().
*

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

Dries Buytaert
committed
*/
function drupal_validate_form($form_id, &$form, &$form_state) {

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

Dries Buytaert
committed
/**

Dries Buytaert
committed
* Redirects the user to a URL after a form has been processed.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::service('form_submitter')->redirectForm().
*
* @see \Drupal\Core\Form\FormSubmitterInterface::redirectForm().

Dries Buytaert
committed
*/

Dries Buytaert
committed
function drupal_redirect_form($form_state) {
return \Drupal::service('form_submitter')->redirectForm($form_state);

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
*/
function form_execute_handlers($type, &$form, &$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 \Drupal::formBuilder()->setErrorByName().
*

Alex Pott
committed
* @see \Drupal\Core\Form\FormErrorInterface::setErrorByName().
*/

Angie Byron
committed
function form_set_error($name, array &$form_state, $message = '') {
\Drupal::formBuilder()->setErrorByName($name, $form_state, $message);
}

Dries Buytaert
committed
/**

Dries Buytaert
committed
* Clears all errors against all form elements made by form_set_error().

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

Dries Buytaert
committed
*/

Angie Byron
committed
function form_clear_error(array &$form_state) {
\Drupal::formBuilder()->clearErrors($form_state);

Dries Buytaert
committed
}
/**

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 \Drupal::formBuilder()->getErrors().
*

Alex Pott
committed
* @see \Drupal\Core\Form\FormErrorInterface::getErrors()
*/

Angie Byron
committed
function form_get_errors(array &$form_state) {
return \Drupal::formBuilder()->getErrors($form_state);
}
/**

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 \Drupal::formBuilder()->getError().
*

Alex Pott
committed
* @see \Drupal\Core\Form\FormErrorInterface::getError().
*/

Angie Byron
committed
function form_get_error($element, array &$form_state) {
return \Drupal::formBuilder()->getError($element, $form_state);
}

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 \Drupal::formBuilder()->setError().
*

Alex Pott
committed
* @see \Drupal\Core\Form\FormErrorInterface::setError().

Angie Byron
committed
function form_error(&$element, array &$form_state, $message = '') {
\Drupal::formBuilder()->setError($element, $form_state, $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, &$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
* A keyed array containing the current state of the form, including
* submitted form values; altered by reference.
*/
function form_state_values_clean(&$form_state) {
// Remove internal Form API values.
unset($form_state['values']['form_id'], $form_state['values']['form_token'], $form_state['values']['form_build_id'], $form_state['values']['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
// each parent in $form_state['values']. For example, if #parents is:
// array('foo', 'bar', 'baz')
// then the corresponding $form_state['values'] 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
// reference to $form_state['values'], but in the iteration we move the
// reference to $form_state['values']['foo'], and finally to
// $form_state['values']['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;

Dries Buytaert
committed
$values = &NestedArray::getValue($form_state['values'], $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
* A keyed array containing the current state of the form.

Dries Buytaert
committed
*

Gábor Hojtsy
committed
* @return
* The data that will appear in the $form_state['values'] collection
* for this element. Return nothing to use the default.
*/
function form_type_image_button_value($form, $input, $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
* The data that will appear in the $form_state['values'] 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
*

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_textfield_value($element, $input = FALSE) {

Dries Buytaert
committed
if ($input !== FALSE && $input !== NULL) {
// Equate $input to the form value to ensure it's marked for

Dries Buytaert
committed
// validation.
return str_replace(array("\r", "\n"), '', $input);

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.
* Use \Drupal::formBuilder()->setValue().
*
* @see \Drupal\Core\Form\FormBuilderInterface::setValue().
function form_set_value($element, $value, &$form_state) {

Alex Pott
committed
\Drupal::formBuilder()->setValue($element, $value, $form_state);
/**
* 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) {
$options .= '<optgroup label="' . $key . '">';

Dries Buytaert
committed
$options .= form_select_options($element, $choice);
$options .= '</optgroup>';

Neil Drumm
committed
elseif (is_object($choice)) {
$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>';