2005-10-07 06:11:12 +00:00
< ? php
2005-11-01 09:58:01 +00:00
2012-02-19 03:41:24 +00:00
/**
* @ file
* Functions for form and batch generation and processing .
*/
2013-05-02 04:46:53 +00:00
use Drupal\Component\Utility\Crypt ;
2012-12-17 21:54:13 +00:00
use Drupal\Component\Utility\NestedArray ;
2013-02-13 17:17:55 +00:00
use Drupal\Core\Form\FormInterface ;
2013-04-29 23:46:14 +00:00
use Drupal\Core\Form\BaseFormIdInterface ;
2013-01-15 15:35:31 +00:00
use Drupal\Core\Database\Database ;
2013-05-25 20:12:45 +00:00
use Drupal\Core\Language\Language ;
2012-09-04 13:32:47 +00:00
use Drupal\Core\Template\Attribute ;
2013-02-16 23:29:53 +00:00
use Drupal\Core\Datetime\DrupalDateTime ;
2013-01-15 15:35:31 +00:00
use Drupal\Core\Utility\Color ;
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
use Symfony\Component\HttpFoundation\Response ;
use Symfony\Component\HttpFoundation\RedirectResponse ;
use Symfony\Component\HttpKernel\KernelEvents ;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent ;
2012-07-07 20:21:18 +00:00
2005-10-07 06:11:12 +00:00
/**
2008-01-21 15:17:01 +00:00
* @ 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
2008-02-06 19:38:28 +00:00
* functions using \ @ see . Conversely , validate and submit functions should
2008-01-21 15:17:01 +00:00
* reference the form builder function using \ @ see . For examples , of this see
2008-02-06 19:38:28 +00:00
* system_modules_uninstall () or user_pass (), the latter of which has the
2008-01-21 15:17:01 +00:00
* following in its doxygen documentation :
2013-06-06 16:21:59 +00:00
* - \ @ ingroup forms
* - \ @ see user_pass_validate ()
* - \ @ see user_pass_submit ()
2008-01-21 15:17:01 +00:00
*
2013-06-06 16:21:59 +00:00
* @ }
2008-01-21 15:17:01 +00:00
*/
/**
* @ defgroup form_api Form generation
2005-10-07 06:11:12 +00:00
* @ {
2006-08-18 18:58:47 +00:00
* Functions to enable the processing and display of HTML forms .
2005-10-07 06:11:12 +00:00
*
2006-08-18 18:58:47 +00:00
* Drupal uses these functions to achieve consistency in its form processing and
* presentation , while simplifying code and reducing the amount of HTML that
* must be explicitly generated by modules .
*
2010-06-15 15:52:04 +00:00
* The primary function used with forms is drupal_get_form (), which is
* used for forms presented interactively to a user . Forms can also be built and
* submitted programmatically without any user input using the
* drupal_form_submit () function .
2006-08-18 18:58:47 +00:00
*
2010-06-15 15:52:04 +00:00
* drupal_get_form () handles retrieving , processing , and displaying a rendered
* HTML form for modules automatically .
*
* Here is an example of how to use drupal_get_form () and a form builder
* function :
2007-11-09 07:40:55 +00:00
* @ code
2010-06-15 15:52:04 +00:00
* $form = drupal_get_form ( 'my_module_example_form' );
* ...
* function my_module_example_form ( $form , & $form_state ) {
* $form [ 'submit' ] = array (
* '#type' => 'submit' ,
* '#value' => t ( 'Submit' ),
* );
2010-06-16 05:06:15 +00:00
* return $form ;
2010-06-15 15:52:04 +00:00
* }
* function my_module_example_form_validate ( $form , & $form_state ) {
* // Validation logic.
* }
* function my_module_example_form_submit ( $form , & $form_state ) {
* // Submission logic.
* }
2007-11-09 07:40:55 +00:00
* @ endcode
2006-08-18 18:58:47 +00:00
*
2010-06-15 15:52:04 +00:00
* Or with any number of additional arguments :
* @ code
* $extra = " extra " ;
* $form = drupal_get_form ( 'my_module_example_form' , $extra );
* ...
* function my_module_example_form ( $form , & $form_state , $extra ) {
* $form [ 'submit' ] = array (
* '#type' => 'submit' ,
* '#value' => $extra ,
* );
2010-06-16 05:06:15 +00:00
* return $form ;
2010-06-15 15:52:04 +00:00
* }
* @ endcode
2006-08-18 18:58:47 +00:00
*
2010-06-15 15:52:04 +00:00
* 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
2012-05-04 20:07:43 +00:00
* @ link forms_api_reference . html Form API reference @ endlink
2010-06-15 15:52:04 +00:00
* and the
2011-09-27 17:39:34 +00:00
* @ link http :// drupal . org / node / 37775 Form API documentation section . @ endlink
2010-06-15 15:52:04 +00:00
* 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 .
*
* In the form builder , validation , submission , and other form functions ,
* $form_state is the primary influence on the processing of the form and is
* passed by reference to most functions , so they use it to communicate with
* the form system and each other .
*
2011-09-27 17:39:34 +00:00
* See drupal_build_form () for documentation of $form_state keys .
2005-10-07 06:11:12 +00:00
*/
2013-02-27 22:30:50 +00:00
/**
* Determines the form ID .
*
* @ param \Drupal\Core\Form\FormInterface | string $form_arg
* A form object to use to build the form , or the unique string identifying
* the desired form . If $form_arg is a string and a function with that
* name exists , it is called to build the form array .
* @ param array $form_state
* An associative array containing the current state of the form .
*
* @ return string
* The unique string identifying the desired form .
*/
function _drupal_form_id ( $form_arg , & $form_state ) {
// If the $form_arg implements \Drupal\Core\Form\FormInterface, add that as
// the callback object and determine the form ID.
if ( is_object ( $form_arg ) && $form_arg instanceof FormInterface ) {
$form_state [ 'build_info' ][ 'callback_object' ] = $form_arg ;
2013-04-29 23:46:14 +00:00
if ( $form_arg instanceof BaseFormIdInterface ) {
$form_state [ 'build_info' ][ 'base_form_id' ] = $form_arg -> getBaseFormID ();
}
2013-02-27 22:30:50 +00:00
return $form_arg -> getFormID ();
}
// Otherwise, the $form_arg is the form ID.
return $form_arg ;
}
2005-10-07 06:11:12 +00:00
/**
2012-02-19 03:41:24 +00:00
* Returns a renderable form array for a given form ID .
*
* This function should be used instead of drupal_build_form () when $form_state
* is not needed ( i . e . , when initially rendering the form ) and is often
* used as a menu callback .
2005-10-07 06:11:12 +00:00
*
2013-02-13 17:17:55 +00:00
* @ param \Drupal\Core\Form\FormInterface | string $form_arg
* A form object to use to build the form , or the unique string identifying
* the desired form . If $form_arg is a string and a function with that
2009-03-14 20:13:27 +00:00
* name exists , it is called to build the form array . Modules that need to
* generate the same form ( or very similar forms ) using different $form_ids
* can implement hook_forms (), which maps different $form_id values to the
* proper form constructor function . Examples may be found in node_forms (),
2012-01-24 22:53:40 +00:00
* and search_forms () .
2006-08-18 18:58:47 +00:00
* @ param ...
2007-10-31 15:10:33 +00:00
* Any additional arguments are passed on to the functions called by
2009-03-14 20:13:27 +00:00
* drupal_get_form (), including the unique form constructor function . For
* example , the node_edit form requires that a node object is passed in here
2010-11-27 19:12:56 +00:00
* when it is called . These are available to implementations of
2010-10-20 00:53:27 +00:00
* hook_form_alter () and hook_form_FORM_ID_alter () as the array
* $form_state [ 'build_info' ][ 'args' ] .
2010-03-26 17:14:46 +00:00
*
2006-08-18 18:58:47 +00:00
* @ return
2009-05-12 08:37:45 +00:00
* The form array .
2009-03-14 20:13:27 +00:00
*
* @ see drupal_build_form ()
2006-08-18 18:58:47 +00:00
*/
2013-02-13 17:17:55 +00:00
function drupal_get_form ( $form_arg ) {
2009-03-14 20:13:27 +00:00
$form_state = array ();
2007-05-14 13:43:38 +00:00
$args = func_get_args ();
2013-02-13 17:17:55 +00:00
// Remove $form_arg from the arguments.
2009-03-14 20:13:27 +00:00
array_shift ( $args );
2009-11-04 04:56:54 +00:00
$form_state [ 'build_info' ][ 'args' ] = $args ;
2009-03-14 20:13:27 +00:00
2013-02-27 22:30:50 +00:00
$form_id = _drupal_form_id ( $form_arg , $form_state );
2013-02-06 14:00:25 +00:00
return drupal_build_form ( $form_id , $form_state );
}
2009-03-14 20:13:27 +00:00
/**
2012-02-19 03:41:24 +00:00
* Builds and processes a form for a given form ID .
2009-03-14 20:13:27 +00:00
*
* The form may also be retrieved from the cache if the form was built in a
* previous page - load . The form is then passed on for processing , validation
2009-05-12 08:37:45 +00:00
* and submission if there is proper input .
2009-03-14 20:13:27 +00:00
*
* @ param $form_id
* The unique string identifying the desired form . If a function with that
* name exists , it is called to build the form array . Modules that need to
* generate the same form ( or very similar forms ) using different $form_ids
* can implement hook_forms (), which maps different $form_id values to the
* proper form constructor function . Examples may be found in node_forms (),
2012-01-24 22:53:40 +00:00
* and search_forms () .
2011-05-08 19:50:38 +00:00
* @ param $form_state
2009-03-14 20:13:27 +00:00
* An array which stores information about the form . This is passed as a
2009-11-04 04:56:54 +00:00
* reference so that the caller can use it to examine what in the form changed
2009-12-05 14:33:55 +00:00
* when the form submission process is complete . Furthermore , it may be used
* to store information related to the processed data in the form , which will
* persist across page requests when the 'cache' or 'rebuild' flag is set .
2009-03-14 20:13:27 +00:00
* The following parameters may be set in $form_state to affect how the form
* is rendered :
2011-09-27 17:39:34 +00:00
* - build_info : Internal . An associative array of information stored by Form
* API that is necessary to build and rebuild the form from cache when the
* original context may no longer be available :
2012-08-16 11:30:43 +00:00
* - callback : The actual callback to be used to retrieve the form array . If
* none is provided $form_id is used instead . Can be any callable type .
2011-09-27 17:39:34 +00:00
* - args : A list of arguments to pass to the form constructor .
2010-07-17 18:52:39 +00:00
* - files : An optional array defining include files that need to be loaded
* for building the form . Each array entry may be the path to a file or
* another array containing values for the parameters 'type' , 'module' and
* 'name' as needed by module_load_include () . The files listed here are
2010-11-27 19:12:56 +00:00
* automatically loaded by form_get_cache () . By default the current menu
2011-09-27 17:39:34 +00:00
* router item 's ' file ' definition is added , if any . Use
* form_load_include () to add include files from a form constructor .
2012-07-21 19:28:46 +00:00
* - form_id : Identification of the primary form being constructed and
* processed .
2011-12-14 15:04:55 +00:00
* - base_form_id : Identification for a base form , as declared in a
* hook_forms () implementation .
2011-09-27 17:39:34 +00:00
* - rebuild_info : Internal . Similar to 'build_info' , but pertaining to
* drupal_rebuild_form () .
2009-11-21 14:06:46 +00:00
* - rebuild : Normally , after the entire form processing is completed and
2011-09-27 17:39:34 +00:00
* submit handlers have run , a form is considered to be done and
2009-11-21 14:06:46 +00:00
* drupal_redirect_form () will redirect the user to a new page using a GET
* request ( so a browser refresh does not re - submit the form ) . However , if
* 'rebuild' has been set to TRUE , then a new copy of the form is
2011-09-27 17:39:34 +00:00
* immediately built and sent to the browser , instead of a redirect . This is
2010-09-27 00:53:56 +00:00
* used for multi - step forms , such as wizards and confirmation forms .
* Normally , $form_state [ 'rebuild' ] is set by a submit handler , since it is
* usually logic within a submit handler that determines whether a form is
* done or requires another step . However , a validation handler may already
* set $form_state [ 'rebuild' ] to cause the form processing to bypass submit
* handlers and rebuild the form instead , even if there are no validation
* errors .
2011-09-27 17:39:34 +00:00
* - redirect : Used to redirect the form on submission . It may either be a
* string containing the destination URL , or an array of arguments
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
* compatible with url () . See url () for complete information .
* - no_redirect : If set to TRUE the form will NOT perform a redirect ,
2011-09-27 17:39:34 +00:00
* even if 'redirect' is set .
2009-03-14 20:13:27 +00:00
* - method : The HTTP form method to use for finding the input for this form .
* May be 'post' or 'get' . Defaults to 'post' . Note that 'get' method
* forms do not use form ids so are always considered to be submitted , which
* can have unexpected effects . The 'get' method should only be used on
2011-09-27 17:39:34 +00:00
* forms that do not change data , as that is exclusively the domain of
* 'post.'
2009-11-18 18:51:11 +00:00
* - cache : If set to TRUE the original , unprocessed form structure will be
2011-09-27 17:39:34 +00:00
* cached , which allows the entire form to be rebuilt from cache . A typical
* form workflow involves two page requests ; first , a form is built and
* rendered for the user to fill in . Then , the user fills the form in and
* submits it , triggering a second page request in which the form must be
* built and processed . By default , $form and $form_state are built from
* scratch during each of these page requests . Often , it is necessary or
* desired to persist the $form and $form_state variables from the initial
* page request to the one that processes the submission . 'cache' can be set
* to TRUE to do this . A prominent example is an Ajax - enabled form , in which
* ajax_process_form () enables form caching for all forms that include an
* element with the #ajax property. (The Ajax handler has no way to build
* the form itself , so must rely on the cached version . ) Note that the
* persistence of $form and $form_state happens automatically for
* ( multi - step ) forms having the 'rebuild' flag set , regardless of the value
* for 'cache' .
2009-11-18 18:51:11 +00:00
* - no_cache : If set to TRUE the form will NOT be cached , even if 'cache' is
* set .
2011-09-27 17:39:34 +00:00
* - values : An associative array of values submitted to the form . The
* validation functions and submit functions use this array for nearly all
2012-05-04 20:07:43 +00:00
* their decision making . ( Note that #tree determines whether the values
* are a flat array or an array whose structure parallels the $form array .
* See the @ link forms_api_reference . html Form API reference @ endlink for
* more information . )
2011-09-27 17:39:34 +00:00
* - input : The array of values as they were submitted by the user . These are
* raw and unvalidated , so should not be used without a thorough
* understanding of security implications . In almost all cases , code should
* use the data in the 'values' array exclusively . The most common use of
* this key is for multi - step forms that need to clear some of the user
* input when setting 'rebuild' . The values correspond to $_POST or $_GET ,
* depending on the 'method' chosen .
2009-03-14 20:13:27 +00:00
* - always_process : If TRUE and the method is GET , a form_id is not
* necessary . This should only be used on RESTful GET forms that do NOT
* write data , as this could lead to security issues . It is useful so that
* searches do not need to have a form_id in their query arguments to
* trigger the search .
2011-09-27 17:39:34 +00:00
* - must_validate : Ordinarily , a form is only validated once , but there are
2009-03-14 20:13:27 +00:00
* times when a form is resubmitted internally and should be validated
* again . Setting this to TRUE will force that to happen . This is most
2011-09-27 17:39:34 +00:00
* likely to occur during Ajax operations .
* - programmed : If TRUE , the form was submitted programmatically , usually
* invoked via drupal_form_submit () . Defaults to FALSE .
* - process_input : Boolean flag . TRUE signifies correct form submission .
* This is always TRUE for programmed forms coming from drupal_form_submit ()
* ( see 'programmed' key ), or if the form_id coming from the $_POST data is
* set and matches the current form_id .
* - submitted : If TRUE , the form has been submitted . Defaults to FALSE .
* - executed : If TRUE , the form was submitted and has been processed and
* executed . Defaults to FALSE .
* - triggering_element : ( read - only ) The form element that triggered
2012-10-15 21:11:54 +00:00
* submission , which may or may not be a button ( in the case of Ajax forms ) .
* This key is often used to distinguish between various buttons in a submit
* handler , and is also used in Ajax handlers .
2011-09-27 17:39:34 +00:00
* - has_file_element : Internal . If TRUE , there is a file element and Form API
* will set the appropriate 'enctype' HTML attribute on the form .
2012-11-27 07:06:47 +00:00
* - groups : Internal . An array containing references to details elements to
* render them within vertical tabs .
2011-09-27 17:39:34 +00:00
* - storage : $form_state [ 'storage' ] is not a special key , and no specific
* support is provided for it in the Form API . By tradition it was
* the location where application - specific data was stored for communication
* between the submit , validation , and form builder functions , especially
* in a multi - step - style form . Form implementations may use any key ( s )
* within $form_state ( other than the keys listed here and other reserved
* ones used by Form API internals ) for this kind of storage . The
* recommended way to ensure that the chosen key doesn ' t conflict with ones
* used by the Form API or other modules is to use the module name as the
2012-08-16 11:30:43 +00:00
* key name or a prefix for the key name . For example , the entity form
2013-04-29 23:46:14 +00:00
* controller classes use $this -> entity in entity forms , or
* $form_state [ 'controller' ] -> getEntity () outside the controller , to store
2012-08-16 11:30:43 +00:00
* information about the entity being edited , and this information stays
* available across successive clicks of the " Preview " button ( if available )
* as well as when the " Save " button is finally clicked .
2011-09-27 17:39:34 +00:00
* - buttons : A list containing copies of all submit and button elements in
* the form .
* - complete_form : A reference to the $form variable containing the complete
* form structure . #process, #after_build, #element_validate, and other
* handlers being invoked on a form element may use this reference to access
* other information in the form the element is contained in .
2009-12-05 14:33:55 +00:00
* - temporary : An array holding temporary data accessible during the current
2011-09-27 17:39:34 +00:00
* page request only . All $form_state properties that are not reserved keys
* ( see form_state_keys_no_cache ()) persist throughout a multistep form
* sequence . Form API provides this key for modules to communicate
* information across form - related functions during a single page request .
* It may be used to temporarily save data that does not need to or should
* not be cached during the whole form workflow ; e . g . , data that needs to be
* accessed during the current form build process only . There is no use - case
* for this functionality in Drupal core .
2009-09-18 00:12:48 +00:00
* - wrapper_callback : Modules that wish to pre - populate certain forms with
* common elements , such as back / next / save buttons in multi - step form
* wizards , may define a form builder function name that returns a form
* structure , which is passed on to the actual form builder function .
2009-11-04 05:39:14 +00:00
* Such implementations may either define the 'wrapper_callback' via
* hook_forms () or have to invoke drupal_build_form () ( instead of
* drupal_get_form ()) on their own in a custom menu callback to prepare
* $form_state accordingly .
2011-09-27 17:39:34 +00:00
* Information on how certain $form_state properties control redirection
* behavior after form submission may be found in drupal_redirect_form () .
2009-09-21 06:44:14 +00:00
*
2009-03-14 20:13:27 +00:00
* @ return
2011-07-08 00:15:30 +00:00
* The rendered form . This function may also perform a redirect and hence may
* not return at all , depending upon the $form_state flags that were set .
2009-09-21 06:44:14 +00:00
*
* @ see drupal_redirect_form ()
2009-03-14 20:13:27 +00:00
*/
function drupal_build_form ( $form_id , & $form_state ) {
// Ensure some defaults; if already set they will not be overridden.
$form_state += form_state_defaults ();
if ( ! isset ( $form_state [ 'input' ])) {
$form_state [ 'input' ] = $form_state [ 'method' ] == 'get' ? $_GET : $_POST ;
}
2007-05-14 13:43:38 +00:00
if ( isset ( $_SESSION [ 'batch_form_state' ])) {
2010-09-13 01:09:26 +00:00
// We've been redirected here after a batch processing. The form has
// already been processed, but needs to be rebuilt. See _batch_finished().
2007-05-14 13:43:38 +00:00
$form_state = $_SESSION [ 'batch_form_state' ];
unset ( $_SESSION [ 'batch_form_state' ]);
2010-09-13 01:09:26 +00:00
return drupal_rebuild_form ( $form_id , $form_state );
2006-08-25 08:15:24 +00:00
}
2010-07-29 00:46:09 +00:00
2010-09-13 01:09:26 +00:00
// If the incoming input contains a form_build_id, we'll check the cache for a
// copy of the form in question. If it's there, we don't have to rebuild the
// form to proceed. In addition, if there is stored form_state data from a
// previous step, we'll retrieve it so it can be passed on to the form
// processing code.
$check_cache = isset ( $form_state [ 'input' ][ 'form_id' ]) && $form_state [ 'input' ][ 'form_id' ] == $form_id && ! empty ( $form_state [ 'input' ][ 'form_build_id' ]);
if ( $check_cache ) {
$form = form_get_cache ( $form_state [ 'input' ][ 'form_build_id' ], $form_state );
2007-05-14 13:43:38 +00:00
}
2010-09-13 01:09:26 +00:00
// If the previous bit of code didn't result in a populated $form object, we
// are hitting the form for the first time and we need to build it from
// scratch.
if ( ! isset ( $form )) {
// If we attempted to serve the form from cache, uncacheable $form_state
// keys need to be removed after retrieving and preparing the form, except
// any that were already set prior to retrieving the form.
if ( $check_cache ) {
$form_state_before_retrieval = $form_state ;
2009-12-01 16:28:57 +00:00
}
2009-05-24 17:39:35 +00:00
2010-09-13 01:09:26 +00:00
$form = drupal_retrieve_form ( $form_id , $form_state );
drupal_prepare_form ( $form_id , $form , $form_state );
// form_set_cache() removes uncacheable $form_state keys defined in
// form_state_keys_no_cache() in order for multi-step forms to work
// properly. This means that form processing logic for single-step forms
// using $form_state['cache'] may depend on data stored in those keys
// during drupal_retrieve_form()/drupal_prepare_form(), but form
// processing should not depend on whether the form is cached or not, so
// $form_state is adjusted to match what it would be after a
// form_set_cache()/form_get_cache() sequence. These exceptions are
// allowed to survive here:
// - always_process: Does not make sense in conjunction with form caching
// in the first place, since passing form_build_id as a GET parameter is
// not desired.
// - temporary: Any assigned data is expected to survives within the same
// page request.
if ( $check_cache ) {
$uncacheable_keys = array_flip ( array_diff ( form_state_keys_no_cache (), array ( 'always_process' , 'temporary' )));
$form_state = array_diff_key ( $form_state , $uncacheable_keys );
$form_state += $form_state_before_retrieval ;
2010-09-09 23:01:48 +00:00
}
2009-05-12 08:37:45 +00:00
}
2006-12-06 15:49:45 +00:00
2010-09-13 01:09:26 +00:00
// Now that we have a constructed form, process it. This is where:
// - Element #process functions get called to further refine $form.
// - User input, if any, gets incorporated in the #value property of the
// corresponding elements and into $form_state['values'].
// - Validation and submission handlers are called.
// - If this submission is part of a multistep workflow, the form is rebuilt
// to contain the information of the next step.
// - If necessary, the form and form state are cached or re-cached, so that
// appropriate information persists to the next page request.
// All of the handlers in the pipeline receive $form_state by reference and
// can use it to know or update information about the state of the form.
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
$response = drupal_process_form ( $form_id , $form , $form_state );
// If the form returns some kind of response, deliver it.
if ( $response instanceof Response ) {
_drupal_form_send_response ( $response );
}
2010-09-13 01:09:26 +00:00
// If this was a successful submission of a single-step form or the last step
// of a multi-step form, then drupal_process_form() issued a redirect to
// another page, or back to this page, but as a new request. Therefore, if
// we're here, it means that this is either a form being viewed initially
// before any user input, or there was a validation error requiring the form
// to be re-displayed, or we're in a multi-step workflow and need to display
// the form's next step. In any case, we have what we need in $form, and can
// return it for rendering.
2009-05-12 08:37:45 +00:00
return $form ;
2007-05-14 13:43:38 +00:00
}
2006-12-06 15:49:45 +00:00
2009-03-14 20:13:27 +00:00
/**
2012-02-19 03:41:24 +00:00
* Retrieves default values for the $form_state array .
2009-03-14 20:13:27 +00:00
*/
function form_state_defaults () {
return array (
2009-07-06 13:31:47 +00:00
'rebuild' => FALSE ,
2010-09-13 01:09:26 +00:00
'rebuild_info' => array (),
2009-07-06 13:31:47 +00:00
'redirect' => NULL ,
2010-11-27 19:12:56 +00:00
// @todo 'args' is usually set, so no other default 'build_info' keys are
// appended via += form_state_defaults().
'build_info' => array (
'args' => array (),
'files' => array (),
),
2009-12-05 14:33:55 +00:00
'temporary' => array (),
2009-03-14 20:13:27 +00:00
'submitted' => FALSE ,
2010-09-27 00:53:56 +00:00
'executed' => FALSE ,
2009-03-14 20:13:27 +00:00
'programmed' => FALSE ,
2009-07-06 13:31:47 +00:00
'cache' => FALSE ,
'method' => 'post' ,
2009-04-11 22:19:46 +00:00
'groups' => array (),
2010-03-26 18:58:12 +00:00
'buttons' => array (),
2009-03-14 20:13:27 +00:00
);
}
2007-11-19 19:23:28 +00:00
/**
2010-09-13 01:09:26 +00:00
* Constructs a new $form from the information in $form_state .
*
* This is the key function for making multi - step forms advance from step to
* step . It is called by drupal_process_form () when all user input processing ,
* including calling validation and submission handlers , for the request is
* finished . If a validate or submit handler set $form_state [ 'rebuild' ] to TRUE ,
* and if other conditions don ' t preempt a rebuild from happening , then this
* function is called to generate a new $form , the next step in the form
* workflow , to be returned for rendering .
2007-11-19 19:23:28 +00:00
*
2011-02-19 00:09:11 +00:00
* Ajax form submissions are almost always multi - step workflows , so that is one
2010-09-13 01:09:26 +00:00
* common use - case during which form rebuilding occurs . See ajax_form_callback ()
2011-02-19 00:09:11 +00:00
* for more information about creating Ajax - enabled forms .
2007-11-19 19:23:28 +00:00
*
* @ param $form_id
* The unique string identifying the desired form . If a function
* with that name exists , it is called to build the form array .
* Modules that need to generate the same form ( or very similar forms )
* using different $form_ids can implement hook_forms (), which maps
* different $form_id values to the proper form constructor function . Examples
2012-01-24 22:53:40 +00:00
* may be found in node_forms () and search_forms () .
2007-11-19 19:23:28 +00:00
* @ param $form_state
2009-12-05 14:33:55 +00:00
* A keyed array containing the current state of the form .
2010-03-31 19:34:56 +00:00
* @ param $old_form
* ( optional ) A previously built $form . Used to retain the #build_id and
2011-02-19 00:09:11 +00:00
* #action properties in Ajax callbacks and similar partial form rebuilds. The
2010-09-13 01:09:26 +00:00
* only properties copied from $old_form are the ones which both exist in
* $old_form and for which $form_state [ 'rebuild_info' ][ 'copy' ][ PROPERTY ] is
* TRUE . If $old_form is not passed , the entire $form is rebuilt freshly .
* 'rebuild_info' needs to be a separate top - level property next to
* 'build_info' , since the contained data must not be cached .
2010-03-31 19:34:56 +00:00
*
2007-11-19 19:23:28 +00:00
* @ return
* The newly built form .
2010-09-13 01:09:26 +00:00
*
* @ see drupal_process_form ()
* @ see ajax_form_callback ()
2007-11-19 19:23:28 +00:00
*/
2010-03-31 19:34:56 +00:00
function drupal_rebuild_form ( $form_id , & $form_state , $old_form = NULL ) {
2009-03-14 20:13:27 +00:00
$form = drupal_retrieve_form ( $form_id , $form_state );
2007-11-19 19:23:28 +00:00
2011-02-19 00:09:11 +00:00
// If only parts of the form will be returned to the browser (e.g., Ajax or
2010-03-31 19:34:56 +00:00
// RIA clients), re-use the old #build_id to not require client-side code to
// manually update the hidden 'build_id' input element.
// Otherwise, a new #build_id is generated, to not clobber the previous
// build's data in the form cache; also allowing the user to go back to an
// earlier build, make changes, and re-submit.
2010-09-13 01:09:26 +00:00
// @see drupal_prepare_form()
if ( isset ( $old_form [ '#build_id' ]) && ! empty ( $form_state [ 'rebuild_info' ][ 'copy' ][ '#build_id' ])) {
$form [ '#build_id' ] = $old_form [ '#build_id' ];
}
else {
2013-05-02 04:46:53 +00:00
$form [ '#build_id' ] = 'form-' . Crypt :: hashBase64 ( uniqid ( mt_rand (), TRUE ) . mt_rand ());
2010-09-13 01:09:26 +00:00
}
2010-03-31 19:34:56 +00:00
2011-02-19 00:09:11 +00:00
// #action defaults to request_uri(), but in case of Ajax and other partial
2010-03-31 19:34:56 +00:00
// rebuilds, the form is submitted to an alternate URL, and the original
// #action needs to be retained.
2010-09-13 01:09:26 +00:00
if ( isset ( $old_form [ '#action' ]) && ! empty ( $form_state [ 'rebuild_info' ][ 'copy' ][ '#action' ])) {
2010-03-31 19:34:56 +00:00
$form [ '#action' ] = $old_form [ '#action' ];
2007-11-19 19:23:28 +00:00
}
2010-03-31 19:34:56 +00:00
2007-11-19 19:23:28 +00:00
drupal_prepare_form ( $form_id , $form , $form_state );
2010-09-13 01:09:26 +00:00
// Caching is normally done in drupal_process_form(), but what needs to be
// cached is the $form structure before it passes through form_builder(),
// so we need to do it here.
// @todo For Drupal 8, find a way to avoid this code duplication.
2009-11-18 18:51:11 +00:00
if ( empty ( $form_state [ 'no_cache' ])) {
2010-03-31 19:34:56 +00:00
form_set_cache ( $form [ '#build_id' ], $form , $form_state );
2009-03-14 20:13:27 +00:00
}
2007-11-19 19:23:28 +00:00
2009-12-01 03:07:34 +00:00
// Clear out all group associations as these might be different when
// re-rendering the form.
2009-04-11 22:19:46 +00:00
$form_state [ 'groups' ] = array ();
2010-09-13 01:09:26 +00:00
// Return a fully built form that is ready for rendering.
return form_builder ( $form_id , $form , $form_state );
2007-11-19 19:23:28 +00:00
}
2007-07-29 17:28:23 +00:00
/**
2012-02-19 03:41:24 +00:00
* Fetches a form from the cache .
2007-07-29 17:28:23 +00:00
*/
function form_get_cache ( $form_build_id , & $form_state ) {
Issue #512026 by Berdir, effulgentsia, sun, marcingy, Eric_A, xjm, katbailey, Damien Tournoud, alexpott, beejeebus, pillarsdotnet: Move () storage from cache to new state/key-value system.
2013-03-27 18:57:12 +00:00
if ( $form = Drupal :: keyValueExpirable ( 'form' ) -> get ( $form_build_id )) {
2008-10-11 04:06:29 +00:00
global $user ;
2013-07-11 17:29:02 +00:00
if (( isset ( $form [ '#cache_token' ]) && drupal_valid_token ( $form [ '#cache_token' ])) || ( ! isset ( $form [ '#cache_token' ]) && $user -> isAnonymous ())) {
Issue #512026 by Berdir, effulgentsia, sun, marcingy, Eric_A, xjm, katbailey, Damien Tournoud, alexpott, beejeebus, pillarsdotnet: Move () storage from cache to new state/key-value system.
2013-03-27 18:57:12 +00:00
if ( $stored_form_state = Drupal :: keyValueExpirable ( 'form_state' ) -> get ( $form_build_id )) {
2009-11-04 04:56:54 +00:00
// Re-populate $form_state for subsequent rebuilds.
Issue #512026 by Berdir, effulgentsia, sun, marcingy, Eric_A, xjm, katbailey, Damien Tournoud, alexpott, beejeebus, pillarsdotnet: Move () storage from cache to new state/key-value system.
2013-03-27 18:57:12 +00:00
$form_state = $stored_form_state + $form_state ;
2009-11-04 04:56:54 +00:00
2010-07-17 18:52:39 +00:00
// If the original form is contained in include files, load the files.
2010-11-27 19:12:56 +00:00
// @see form_load_include()
2010-07-17 18:52:39 +00:00
$form_state [ 'build_info' ] += array ( 'files' => array ());
foreach ( $form_state [ 'build_info' ][ 'files' ] as $file ) {
if ( is_array ( $file )) {
$file += array ( 'type' => 'inc' , 'name' => $file [ 'module' ]);
module_load_include ( $file [ 'type' ], $file [ 'module' ], $file [ 'name' ]);
}
elseif ( file_exists ( $file )) {
require_once DRUPAL_ROOT . '/' . $file ;
}
2009-11-04 04:56:54 +00:00
}
2008-10-11 04:06:29 +00:00
}
return $form ;
2007-07-29 17:28:23 +00:00
}
}
}
/**
2012-02-19 03:41:24 +00:00
* Stores a form in the cache .
2007-07-29 17:28:23 +00:00
*/
function form_set_cache ( $form_build_id , $form , $form_state ) {
2008-05-05 21:28:49 +00:00
// 6 hours cache life time for forms should be plenty.
$expire = 21600 ;
2009-11-27 15:14:58 +00:00
// Cache form structure.
if ( isset ( $form )) {
2013-07-11 17:29:02 +00:00
if ( $GLOBALS [ 'user' ] -> isAuthenticated ()) {
2009-11-27 15:14:58 +00:00
$form [ '#cache_token' ] = drupal_get_token ();
}
Issue #512026 by Berdir, effulgentsia, sun, marcingy, Eric_A, xjm, katbailey, Damien Tournoud, alexpott, beejeebus, pillarsdotnet: Move () storage from cache to new state/key-value system.
2013-03-27 18:57:12 +00:00
Drupal :: keyValueExpirable ( 'form' ) -> setWithExpire ( $form_build_id , $form , $expire );
2008-10-11 04:06:29 +00:00
}
2009-11-27 15:14:58 +00:00
// Cache form state.
2009-12-05 14:33:55 +00:00
if ( $data = array_diff_key ( $form_state , array_flip ( form_state_keys_no_cache ()))) {
Issue #512026 by Berdir, effulgentsia, sun, marcingy, Eric_A, xjm, katbailey, Damien Tournoud, alexpott, beejeebus, pillarsdotnet: Move () storage from cache to new state/key-value system.
2013-03-27 18:57:12 +00:00
Drupal :: keyValueExpirable ( 'form_state' ) -> setWithExpire ( $form_build_id , $data , $expire );
2007-07-29 17:28:23 +00:00
}
}
2009-12-05 14:33:55 +00:00
/**
* Returns an array of $form_state keys that shouldn ' t be cached .
*/
function form_state_keys_no_cache () {
return array (
// Public properties defined by form constructors and form handlers.
'always_process' ,
'must_validate' ,
'rebuild' ,
2010-09-13 01:09:26 +00:00
'rebuild_info' ,
2009-12-05 14:33:55 +00:00
'redirect' ,
'no_redirect' ,
'temporary' ,
// Internal properties defined by form processing.
'buttons' ,
2010-03-26 18:58:12 +00:00
'triggering_element' ,
2011-08-01 03:41:25 +00:00
'complete_form' ,
2009-12-05 14:33:55 +00:00
'groups' ,
'input' ,
'method' ,
'submit_handlers' ,
'submitted' ,
2010-09-27 00:53:56 +00:00
'executed' ,
2009-12-05 14:33:55 +00:00
'validate_handlers' ,
'values' ,
);
}
2010-11-27 19:12:56 +00:00
/**
2012-07-30 18:31:47 +00:00
* Ensures an include file is loaded whenever the form is processed .
2010-11-27 19:12:56 +00:00
*
* 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 ;
}
2006-08-31 14:59:28 +00:00
/**
2010-03-04 09:07:27 +00:00
* Retrieves , populates , and processes a form .
*
* This function allows you to supply values for form elements and submit a
* form for processing . Compare to drupal_get_form (), which also builds and
* processes a form , but does not allow you to supply values .
*
* There is no return value , but you can check to see if there are errors
* by calling form_get_errors () .
2006-08-31 14:59:28 +00:00
*
2013-02-27 22:30:50 +00:00
* @ param \Drupal\Core\Form\FormInterface | string $form_arg
* A form object to use to build the form , or the unique string identifying
* the desired form . If $form_arg is a string and a function with that
* name exists , it is called to build the form array . Modules that need to
* generate the same form ( or very similar forms ) using different $form_ids
* can implement hook_forms (), which maps different $form_id values to the
* proper form constructor function . Examples may be found in node_forms ()
* and search_forms () .
2007-05-14 13:43:38 +00:00
* @ param $form_state
2009-12-01 03:07:34 +00:00
* A keyed array containing the current state of the form . Most important is
* the $form_state [ 'values' ] collection , a tree of data used to simulate the
* incoming $_POST information from a user ' s form submission . If a key is not
* filled in $form_state [ 'values' ], then the default value of the respective
* element is used . To submit an unchecked checkbox or other control that
* browsers submit by not having a $_POST entry , include the key , but set the
* value to NULL .
2006-08-31 14:59:28 +00:00
* @ param ...
2007-10-31 15:10:33 +00:00
* Any additional arguments are passed on to the functions called by
2009-04-29 07:18:04 +00:00
* drupal_form_submit (), including the unique form constructor function .
2007-10-31 15:10:33 +00:00
* For example , the node_edit form requires that a node object be passed
2010-04-23 07:50:28 +00:00
* in here when it is called . Arguments that need to be passed by reference
* should not be included here , but rather placed directly in the $form_state
* build info array so that the reference can be preserved . For example , a
* form builder function with the following signature :
* @ code
* function mymodule_form ( $form , & $form_state , & $object ) {
* }
* @ endcode
* would be called via drupal_form_submit () as follows :
* @ code
* $form_state [ 'values' ] = $my_form_values ;
* $form_state [ 'build_info' ][ 'args' ] = array ( & $object );
* drupal_form_submit ( 'mymodule_form' , $form_state );
* @ endcode
2006-11-26 22:35:20 +00:00
* For example :
2009-06-18 15:48:13 +00:00
* @ code
2006-11-26 22:35:20 +00:00
* // register a new user
2007-05-14 13:43:38 +00:00
* $form_state = array ();
* $form_state [ 'values' ][ 'name' ] = 'robo-user' ;
* $form_state [ 'values' ][ 'mail' ] = 'robouser@example.com' ;
2009-11-23 22:37:37 +00:00
* $form_state [ 'values' ][ 'pass' ][ 'pass1' ] = 'password' ;
* $form_state [ 'values' ][ 'pass' ][ 'pass2' ] = 'password' ;
2007-09-25 15:14:37 +00:00
* $form_state [ 'values' ][ 'op' ] = t ( 'Create new account' );
2009-10-10 16:48:39 +00:00
* drupal_form_submit ( 'user_register_form' , $form_state );
2009-06-18 15:48:13 +00:00
* @ endcode
2006-08-31 14:59:28 +00:00
*/
2013-02-27 22:30:50 +00:00
function drupal_form_submit ( $form_arg , & $form_state ) {
2009-11-04 04:56:54 +00:00
if ( ! isset ( $form_state [ 'build_info' ][ 'args' ])) {
2009-03-14 20:13:27 +00:00
$args = func_get_args ();
array_shift ( $args );
array_shift ( $args );
2009-11-04 04:56:54 +00:00
$form_state [ 'build_info' ][ 'args' ] = $args ;
2009-03-14 20:13:27 +00:00
}
2009-12-05 14:33:55 +00:00
// Merge in default values.
$form_state += form_state_defaults ();
2009-01-26 14:08:44 +00:00
2010-12-21 21:05:22 +00:00
// Populate $form_state['input'] with the submitted values before retrieving
// the form, to be consistent with what drupal_build_form() does for
// non-programmatic submissions (form builder functions may expect it to be
// there).
2009-03-14 20:13:27 +00:00
$form_state [ 'input' ] = $form_state [ 'values' ];
2010-12-21 21:05:22 +00:00
2009-03-14 20:13:27 +00:00
$form_state [ 'programmed' ] = TRUE ;
2013-02-27 22:30:50 +00:00
$form_id = _drupal_form_id ( $form_arg , $form_state );
2010-12-21 21:05:22 +00:00
$form = drupal_retrieve_form ( $form_id , $form_state );
2009-07-06 13:31:47 +00:00
// Programmed forms are always submitted.
$form_state [ 'submitted' ] = TRUE ;
2009-01-22 12:46:07 +00:00
2010-03-07 07:49:26 +00:00
// Reset form validation.
$form_state [ 'must_validate' ] = TRUE ;
form_clear_error ();
2007-05-14 13:43:38 +00:00
drupal_prepare_form ( $form_id , $form , $form_state );
drupal_process_form ( $form_id , $form , $form_state );
2006-08-31 14:59:28 +00:00
}
2006-08-18 18:58:47 +00:00
/**
* Retrieves the structured array that defines a given form .
*
* @ param $form_id
* The unique string identifying the desired form . If a function
* with that name exists , it is called to build the form array .
* Modules that need to generate the same form ( or very similar forms )
* using different $form_ids can implement hook_forms (), which maps
2007-05-14 13:43:38 +00:00
* different $form_id values to the proper form constructor function .
2007-06-04 07:22:23 +00:00
* @ param $form_state
2010-03-06 12:43:45 +00:00
* A keyed array containing the current state of the form , including the
* additional arguments to drupal_get_form () or drupal_form_submit () in the
* 'args' component of the array .
2006-08-18 18:58:47 +00:00
*/
2007-06-04 07:22:23 +00:00
function drupal_retrieve_form ( $form_id , & $form_state ) {
2009-06-02 13:47:26 +00:00
$forms = & drupal_static ( __FUNCTION__ );
2006-08-18 18:58:47 +00:00
2012-06-27 21:31:52 +00:00
// Record the $form_id.
$form_state [ 'build_info' ][ 'form_id' ] = $form_id ;
2010-09-13 01:09:26 +00:00
// Record the filepath of the include file containing the original form, so
// the form builder callbacks can be loaded when the form is being rebuilt
// from cache on a different path (such as 'system/ajax'). See
2013-05-08 13:42:56 +00:00
// form_get_cache(). Don't do this in maintenance mode as Drupal may not be
// fully bootstrapped (i.e. during installation) in which case
// menu_get_item() is not available.
2010-09-13 01:09:26 +00:00
if ( ! isset ( $form_state [ 'build_info' ][ 'files' ][ 'menu' ]) && ! defined ( 'MAINTENANCE_MODE' )) {
$item = menu_get_item ();
if ( ! empty ( $item [ 'include_file' ])) {
2010-11-27 19:12:56 +00:00
// Do not use form_load_include() here, as the file is already loaded.
// Anyway, form_get_cache() is able to handle filepaths too.
2010-09-13 01:09:26 +00:00
$form_state [ 'build_info' ][ 'files' ][ 'menu' ] = $item [ 'include_file' ];
}
}
2006-10-12 20:36:51 +00:00
// We save two copies of the incoming arguments: one for modules to use
2007-05-14 13:43:38 +00:00
// when mapping form ids to constructor functions, and another to pass to
2009-03-14 20:13:27 +00:00
// the constructor function itself.
2009-11-04 04:56:54 +00:00
$args = $form_state [ 'build_info' ][ 'args' ];
2006-10-12 20:36:51 +00:00
2012-08-16 11:30:43 +00:00
// If an explicit form builder callback is defined we just use it, otherwise
// we look for a function named after the $form_id.
2013-02-13 17:17:55 +00:00
$callback = $form_id ;
if ( ! empty ( $form_state [ 'build_info' ][ 'callback' ])) {
$callback = $form_state [ 'build_info' ][ 'callback' ];
}
elseif ( ! empty ( $form_state [ 'build_info' ][ 'callback_object' ])) {
2013-02-18 17:30:09 +00:00
$callback = array ( $form_state [ 'build_info' ][ 'callback_object' ], 'buildForm' );
2013-02-13 17:17:55 +00:00
}
2012-08-16 11:30:43 +00:00
// We first check to see if there is a valid form builder callback defined.
2006-10-12 20:36:51 +00:00
// If there is, we simply pass the arguments on to it to get the form.
2012-08-16 11:30:43 +00:00
if ( ! is_callable ( $callback )) {
2007-05-14 13:43:38 +00:00
// In cases where many form_ids need to share a central constructor function,
2006-10-12 20:36:51 +00:00
// such as the node editing form, modules can implement hook_forms(). It
2007-05-14 13:43:38 +00:00
// maps one or more form_ids to the correct constructor functions.
2006-10-12 20:36:51 +00:00
//
// We cache the results of that hook to save time, but that only works
// for modules that know all their form_ids in advance. (A module that
// adds a small 'rate this comment' form to each comment in a list
// would need a unique form_id for each one, for example.)
//
// So, we call the hook if $forms isn't yet populated, OR if it doesn't
// yet have an entry for the requested form_id.
if ( ! isset ( $forms ) || ! isset ( $forms [ $form_id ])) {
2007-05-14 13:43:38 +00:00
$forms = module_invoke_all ( 'forms' , $form_id , $args );
2006-08-18 18:58:47 +00:00
}
$form_definition = $forms [ $form_id ];
if ( isset ( $form_definition [ 'callback arguments' ])) {
$args = array_merge ( $form_definition [ 'callback arguments' ], $args );
}
if ( isset ( $form_definition [ 'callback' ])) {
$callback = $form_definition [ 'callback' ];
2010-09-09 23:01:48 +00:00
$form_state [ 'build_info' ][ 'base_form_id' ] = $callback ;
2006-08-18 18:58:47 +00:00
}
2009-11-04 05:39:14 +00:00
// In case $form_state['wrapper_callback'] is not defined already, we also
// allow hook_forms() to define one.
if ( ! isset ( $form_state [ 'wrapper_callback' ]) && isset ( $form_definition [ 'wrapper_callback' ])) {
$form_state [ 'wrapper_callback' ] = $form_definition [ 'wrapper_callback' ];
}
2006-08-18 18:58:47 +00:00
}
2007-06-04 07:22:23 +00:00
2009-11-04 05:39:14 +00:00
$form = array ();
2011-10-21 03:58:07 +00:00
// Assign a default CSS class name based on $form_id.
// This happens here and not in drupal_prepare_form() in order to allow the
// form constructor function to override or remove the default class.
$form [ '#attributes' ][ 'class' ][] = drupal_html_class ( $form_id );
// Same for the base form ID, if any.
if ( isset ( $form_state [ 'build_info' ][ 'base_form_id' ])) {
$form [ '#attributes' ][ 'class' ][] = drupal_html_class ( $form_state [ 'build_info' ][ 'base_form_id' ]);
}
2009-11-04 05:39:14 +00:00
// We need to pass $form_state by reference in order for forms to modify it,
// since call_user_func_array() requires that referenced variables are passed
// explicitly.
$args = array_merge ( array ( $form , & $form_state ), $args );
2009-09-18 00:12:48 +00:00
// When the passed $form_state (not using drupal_get_form()) defines a
// 'wrapper_callback', then it requests to invoke a separate (wrapping) form
// builder function to pre-populate the $form array with form elements, which
// the actual form builder function ($callback) expects. This allows for
// pre-populating a form with common elements for certain forms, such as
2010-03-26 17:14:46 +00:00
// back/next/save buttons in multi-step form wizards. See drupal_build_form().
2011-12-15 17:33:38 +00:00
if ( isset ( $form_state [ 'wrapper_callback' ])) {
2009-11-04 05:39:14 +00:00
$form = call_user_func_array ( $form_state [ 'wrapper_callback' ], $args );
// Put the prepopulated $form into $args.
$args [ 0 ] = $form ;
2009-09-18 00:12:48 +00:00
}
2007-06-04 07:22:23 +00:00
2006-10-12 20:36:51 +00:00
// If $callback was returned by a hook_forms() implementation, call it.
// Otherwise, call the function named after the form id.
2012-08-16 11:30:43 +00:00
$form = call_user_func_array ( $callback , $args );
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
// If the form returns some kind of response, deliver it.
if ( $form instanceof Response ) {
_drupal_form_send_response ( $form );
}
2009-03-14 20:13:27 +00:00
$form [ '#form_id' ] = $form_id ;
2009-08-27 04:40:12 +00:00
2006-08-31 14:59:28 +00:00
return $form ;
2006-08-18 18:58:47 +00:00
}
/**
2009-09-21 06:44:14 +00:00
* Processes a form submission .
*
2006-08-18 18:58:47 +00:00
* This function is the heart of form API . The form gets built , validated and in
2010-09-13 01:09:26 +00:00
* appropriate cases , submitted and rebuilt .
2006-08-18 18:58:47 +00:00
*
* @ param $form_id
* The unique string identifying the current form .
2005-10-07 06:11:12 +00:00
* @ param $form
* An associative array containing the structure of the form .
2007-05-14 13:43:38 +00:00
* @ param $form_state
* A keyed array containing the current state of the form . This
2007-07-02 14:41:37 +00:00
* includes the current persistent storage data for the form , and
2007-05-14 13:43:38 +00:00
* any data passed along by earlier steps when displaying a
* multi - step form . Additional information , like the sanitized $_POST
* data , is also accumulated here .
2005-10-07 06:11:12 +00:00
*/
2007-05-14 13:43:38 +00:00
function drupal_process_form ( $form_id , & $form , & $form_state ) {
$form_state [ 'values' ] = array ();
2009-03-14 20:13:27 +00:00
// With $_GET, these forms are always submitted if requested.
if ( $form_state [ 'method' ] == 'get' && ! empty ( $form_state [ 'always_process' ])) {
if ( ! isset ( $form_state [ 'input' ][ 'form_build_id' ])) {
$form_state [ 'input' ][ 'form_build_id' ] = $form [ '#build_id' ];
}
if ( ! isset ( $form_state [ 'input' ][ 'form_id' ])) {
$form_state [ 'input' ][ 'form_id' ] = $form_id ;
}
if ( ! isset ( $form_state [ 'input' ][ 'form_token' ]) && isset ( $form [ '#token' ])) {
$form_state [ 'input' ][ 'form_token' ] = drupal_get_token ( $form [ '#token' ]);
}
}
2010-09-13 01:09:26 +00:00
// form_builder() finishes building the form by calling element #process
// functions and mapping user input, if any, to #value properties, and also
// storing the values in $form_state['values']. We need to retain the
// unprocessed $form in case it needs to be cached.
$unprocessed_form = $form ;
2007-05-14 13:43:38 +00:00
$form = form_builder ( $form_id , $form , $form_state );
2009-07-06 13:31:47 +00:00
// Only process the input if we have a correct form submission.
if ( $form_state [ 'process_input' ]) {
2007-05-14 13:43:38 +00:00
drupal_validate_form ( $form_id , $form , $form_state );
2009-10-05 01:18:26 +00:00
// drupal_html_id() maintains a cache of element IDs it has seen,
2007-10-24 13:35:26 +00:00
// so it can prevent duplicates. We want to be sure we reset that
2008-12-30 16:43:20 +00:00
// cache when a form is processed, so scenarios that result in
2007-10-24 13:35:26 +00:00
// the form being built behind the scenes and again for the
// browser don't increment all the element IDs needlessly.
2012-08-28 15:57:02 +00:00
if ( ! form_get_errors ()) {
// In case of errors, do not break HTML IDs of other forms.
drupal_static_reset ( 'drupal_html_id' );
}
2007-10-24 13:35:26 +00:00
2009-07-06 13:31:47 +00:00
if ( $form_state [ 'submitted' ] && ! form_get_errors () && ! $form_state [ 'rebuild' ]) {
// Execute form submit handlers.
2007-05-14 13:43:38 +00:00
form_execute_handlers ( 'submit' , $form , $form_state );
// If batches were set in the submit handlers, we process them now,
2007-09-14 10:40:55 +00:00
// possibly ending execution. We make sure we do not react to the batch
// that is already being processed (if a batch operation performs a
2009-04-29 07:18:04 +00:00
// drupal_form_submit).
2007-09-14 10:40:55 +00:00
if ( $batch =& batch_get () && ! isset ( $batch [ 'current_set' ])) {
2010-01-08 06:36:34 +00:00
// Store $form_state information in the batch definition.
// We need the full $form_state when either:
// - Some submit handlers were saved to be called during batch
// processing. See form_execute_handlers().
// - The form is multistep.
// In other cases, we only need the information expected by
// drupal_redirect_form().
2010-04-04 13:22:51 +00:00
if ( $batch [ 'has_form_submits' ] || ! empty ( $form_state [ 'rebuild' ])) {
2010-01-08 06:36:34 +00:00
$batch [ 'form_state' ] = $form_state ;
}
else {
$batch [ 'form_state' ] = array_intersect_key ( $form_state , array_flip ( array ( 'programmed' , 'rebuild' , 'storage' , 'no_redirect' , 'redirect' )));
}
2009-03-14 20:13:27 +00:00
$batch [ 'progressive' ] = ! $form_state [ 'programmed' ];
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
$response = batch_process ();
if ( $batch [ 'progressive' ]) {
return $response ;
}
2010-01-08 06:36:34 +00:00
2007-05-14 13:43:38 +00:00
// Execution continues only for programmatic forms.
// For 'regular' forms, we get redirected to the batch processing
// page. Form redirection will be handled in _batch_finished(),
// after the batch is processed.
2007-05-04 09:41:37 +00:00
}
2007-05-14 13:43:38 +00:00
2009-07-06 13:31:47 +00:00
// Set a flag to indicate the the form has been processed and executed.
$form_state [ 'executed' ] = TRUE ;
2009-09-21 06:44:14 +00:00
// Redirect the form based on values in $form_state.
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
$redirect = drupal_redirect_form ( $form_state );
if ( is_object ( $redirect )) {
return $redirect ;
}
2006-07-22 19:26:58 +00:00
}
2010-09-13 01:09:26 +00:00
// Don't rebuild or cache form submissions invoked via drupal_form_submit().
if ( ! empty ( $form_state [ 'programmed' ])) {
return ;
}
2010-09-27 00:53:56 +00:00
// If $form_state['rebuild'] has been set and input has been processed
// without validation errors, we are in a multi-step workflow that is not
// yet complete. A new $form needs to be constructed based on the changes
// made to $form_state during this request. Normally, a submit handler sets
// $form_state['rebuild'] if a fully executed form requires another step.
2011-02-19 00:09:11 +00:00
// However, for forms that have not been fully executed (e.g., Ajax
2010-09-27 00:53:56 +00:00
// submissions triggered by non-buttons), there is no submit handler to set
// $form_state['rebuild']. It would not make sense to redisplay the
// identical form without an error for the user to correct, so we also
// rebuild error-free non-executed forms, regardless of
// $form_state['rebuild'].
2011-02-19 00:09:11 +00:00
// @todo D8: Simplify this logic; considering Ajax and non-HTML front-ends,
2010-09-27 00:53:56 +00:00
// along with element-level #submit properties, it makes no sense to have
// divergent form execution based on whether the triggering element has
// #executes_submit_callback set to TRUE.
if (( $form_state [ 'rebuild' ] || ! $form_state [ 'executed' ]) && ! form_get_errors ()) {
// Form building functions (e.g., _form_builder_handle_input_element())
// may use $form_state['rebuild'] to determine if they are running in the
// context of a rebuild, so ensure it is set.
$form_state [ 'rebuild' ] = TRUE ;
$form = drupal_rebuild_form ( $form_id , $form_state , $form );
}
2010-09-13 01:09:26 +00:00
}
2010-09-27 00:53:56 +00:00
2010-09-13 01:09:26 +00:00
// After processing the form, the form builder or a #process callback may
// have set $form_state['cache'] to indicate that the form and form state
// shall be cached. But the form may only be cached if the 'no_cache' property
// is not set to TRUE. Only cache $form as it was prior to form_builder(),
2011-09-24 20:44:06 +00:00
// because form_builder() must run for each request to accommodate new user
2010-09-27 00:53:56 +00:00
// input. Rebuilt forms are not cached here, because drupal_rebuild_form()
// already takes care of that.
if ( ! $form_state [ 'rebuild' ] && $form_state [ 'cache' ] && empty ( $form_state [ 'no_cache' ])) {
2010-09-13 01:09:26 +00:00
form_set_cache ( $form [ '#build_id' ], $unprocessed_form , $form_state );
2006-07-22 19:26:58 +00:00
}
}
/**
2012-02-19 03:41:24 +00:00
* Prepares a structured form array .
*
* Adds required elements , executes any hook_form_alter functions , and
* optionally inserts a validation token to prevent tampering .
2006-07-22 19:26:58 +00:00
*
* @ param $form_id
* A unique string identifying the form for validation , submission ,
* theming , and hook_form_alter functions .
* @ param $form
* An associative array containing the structure of the form .
2007-05-14 13:43:38 +00:00
* @ param $form_state
* A keyed array containing the current state of the form . Passed
* in here so that hook_form_alter () calls can use it , as well .
2006-07-22 19:26:58 +00:00
*/
2007-05-14 13:43:38 +00:00
function drupal_prepare_form ( $form_id , & $form , & $form_state ) {
2006-10-31 08:06:18 +00:00
global $user ;
2005-10-11 19:44:35 +00:00
$form [ '#type' ] = 'form' ;
2009-03-14 20:13:27 +00:00
$form_state [ 'programmed' ] = isset ( $form_state [ 'programmed' ]) ? $form_state [ 'programmed' ] : FALSE ;
2006-08-18 18:58:47 +00:00
2010-09-13 01:09:26 +00:00
// Fix the form method, if it is 'get' in $form_state, but not in $form.
if ( $form_state [ 'method' ] == 'get' && ! isset ( $form [ '#method' ])) {
$form [ '#method' ] = 'get' ;
2006-08-25 08:15:24 +00:00
}
2010-09-13 01:09:26 +00:00
// Generate a new #build_id for this form, if none has been set already. The
// form_build_id is used as key to cache a particular build of the form. For
// multi-step forms, this allows the user to go back to an earlier build, make
// changes, and re-submit.
// @see drupal_build_form()
// @see drupal_rebuild_form()
if ( ! isset ( $form [ '#build_id' ])) {
2013-05-02 04:46:53 +00:00
$form [ '#build_id' ] = 'form-' . Crypt :: hashBase64 ( uniqid ( mt_rand (), TRUE ) . mt_rand ());
2010-09-13 01:09:26 +00:00
}
$form [ 'form_build_id' ] = array (
'#type' => 'hidden' ,
'#value' => $form [ '#build_id' ],
'#id' => $form [ '#build_id' ],
'#name' => 'form_build_id' ,
2012-05-26 23:28:37 +00:00
// Form processing and validation requires this value, so ensure the
// submitted form value appears literally, regardless of custom #tree
// and #parents being set elsewhere.
'#parents' => array ( 'form_build_id' ),
2010-09-13 01:09:26 +00:00
);
2007-01-23 19:17:55 +00:00
// Add a token, based on either #token or form_id, to any form displayed to
// authenticated users. This ensures that any submitted form was actually
// requested previously by the user and protects against cross site request
// forgeries.
2009-12-14 13:51:57 +00:00
// This does not apply to programmatically submitted forms. Furthermore, since
// tokens are session-bound and forms displayed to anonymous users are very
// likely cached, we cannot assign a token for them.
// During installation, there is no $user yet.
2013-07-11 17:29:02 +00:00
if ( $user && $user -> isAuthenticated () && ! $form_state [ 'programmed' ]) {
2009-12-14 13:51:57 +00:00
// Form constructors may explicitly set #token to FALSE when cross site
// request forgery is irrelevant to the form, such as search forms.
if ( isset ( $form [ '#token' ]) && $form [ '#token' ] === FALSE ) {
2006-03-20 16:28:10 +00:00
unset ( $form [ '#token' ]);
2005-11-06 11:38:56 +00:00
}
2009-12-14 13:51:57 +00:00
// Otherwise, generate a public token based on the form id.
2006-03-20 16:28:10 +00:00
else {
2009-12-14 13:51:57 +00:00
$form [ '#token' ] = $form_id ;
$form [ 'form_token' ] = array (
'#id' => drupal_html_id ( 'edit-' . $form_id . '-form-token' ),
'#type' => 'token' ,
'#default_value' => drupal_get_token ( $form [ '#token' ]),
2012-05-26 23:28:37 +00:00
// Form processing and validation requires this value, so ensure the
// submitted form value appears literally, regardless of custom #tree
// and #parents being set elsewhere.
'#parents' => array ( 'form_token' ),
2009-12-14 13:51:57 +00:00
);
2006-03-20 16:28:10 +00:00
}
2005-10-07 06:11:12 +00:00
}
2006-10-31 08:06:18 +00:00
2006-02-27 14:46:49 +00:00
if ( isset ( $form_id )) {
2007-05-14 13:43:38 +00:00
$form [ 'form_id' ] = array (
'#type' => 'hidden' ,
'#value' => $form_id ,
2009-10-05 01:18:26 +00:00
'#id' => drupal_html_id ( " edit- $form_id " ),
2012-05-26 23:28:37 +00:00
// Form processing and validation requires this value, so ensure the
// submitted form value appears literally, regardless of custom #tree
// and #parents being set elsewhere.
'#parents' => array ( 'form_id' ),
2007-05-14 13:43:38 +00:00
);
2006-02-27 14:46:49 +00:00
}
2006-02-02 02:10:31 +00:00
if ( ! isset ( $form [ '#id' ])) {
2009-10-05 01:18:26 +00:00
$form [ '#id' ] = drupal_html_id ( $form_id );
2006-02-02 02:10:31 +00:00
}
2005-10-26 01:24:09 +00:00
2009-02-03 18:55:32 +00:00
$form += element_info ( 'form' );
2008-08-15 07:55:54 +00:00
$form += array ( '#tree' => FALSE , '#parents' => array ());
2005-10-07 06:11:12 +00:00
2005-11-22 21:31:15 +00:00
if ( ! isset ( $form [ '#validate' ])) {
2011-07-04 17:11:33 +00:00
// Ensure that modules can rely on #validate being set.
$form [ '#validate' ] = array ();
2013-02-13 17:17:55 +00:00
if ( isset ( $form_state [ 'build_info' ][ 'callback_object' ])) {
2013-02-18 17:30:09 +00:00
$form [ '#validate' ][] = array ( $form_state [ 'build_info' ][ 'callback_object' ], 'validateForm' );
2013-02-13 17:17:55 +00:00
}
2010-09-09 23:01:48 +00:00
// Check for a handler specific to $form_id.
2013-02-13 17:17:55 +00:00
elseif ( function_exists ( $form_id . '_validate' )) {
2010-09-09 23:01:48 +00:00
$form [ '#validate' ][] = $form_id . '_validate' ;
}
// Otherwise check whether this is a shared form and whether there is a
// handler for the shared $form_id.
elseif ( isset ( $form_state [ 'build_info' ][ 'base_form_id' ]) && function_exists ( $form_state [ 'build_info' ][ 'base_form_id' ] . '_validate' )) {
$form [ '#validate' ][] = $form_state [ 'build_info' ][ 'base_form_id' ] . '_validate' ;
2005-11-22 21:31:15 +00:00
}
}
2005-12-02 15:21:01 +00:00
if ( ! isset ( $form [ '#submit' ])) {
2011-07-04 17:11:33 +00:00
// Ensure that modules can rely on #submit being set.
$form [ '#submit' ] = array ();
2013-02-13 17:17:55 +00:00
if ( isset ( $form_state [ 'build_info' ][ 'callback_object' ])) {
2013-02-18 17:30:09 +00:00
$form [ '#submit' ][] = array ( $form_state [ 'build_info' ][ 'callback_object' ], 'submitForm' );
2013-02-13 17:17:55 +00:00
}
2010-09-09 23:01:48 +00:00
// Check for a handler specific to $form_id.
2013-02-13 17:17:55 +00:00
elseif ( function_exists ( $form_id . '_submit' )) {
2010-09-09 23:01:48 +00:00
$form [ '#submit' ][] = $form_id . '_submit' ;
}
// Otherwise check whether this is a shared form and whether there is a
// handler for the shared $form_id.
elseif ( isset ( $form_state [ 'build_info' ][ 'base_form_id' ]) && function_exists ( $form_state [ 'build_info' ][ 'base_form_id' ] . '_submit' )) {
$form [ '#submit' ][] = $form_state [ 'build_info' ][ 'base_form_id' ] . '_submit' ;
2005-11-22 21:31:15 +00:00
}
}
2010-09-14 21:42:05 +00:00
// If no #theme has been set, automatically apply theme suggestions.
2010-09-13 01:09:26 +00:00
// theme_form() itself is in #theme_wrappers and not #theme. Therefore, the
// #theme function only has to care for rendering the inner form elements,
// not the form itself.
2010-09-14 21:42:05 +00:00
if ( ! isset ( $form [ '#theme' ])) {
$form [ '#theme' ] = array ( $form_id );
if ( isset ( $form_state [ 'build_info' ][ 'base_form_id' ])) {
$form [ '#theme' ][] = $form_state [ 'build_info' ][ 'base_form_id' ];
2010-09-13 01:09:26 +00:00
}
}
2010-09-09 23:01:48 +00:00
// Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
// hook_form_FORM_ID_alter() implementations.
$hooks = array ( 'form' );
if ( isset ( $form_state [ 'build_info' ][ 'base_form_id' ])) {
$hooks [] = 'form_' . $form_state [ 'build_info' ][ 'base_form_id' ];
}
$hooks [] = 'form_' . $form_id ;
drupal_alter ( $hooks , $form , $form_state , $form_id );
2005-10-07 06:11:12 +00:00
}
2006-07-22 19:26:58 +00:00
/**
2012-02-19 03:41:24 +00:00
* Validates user - submitted form data in the $form_state array .
2006-07-22 19:26:58 +00:00
*
* @ param $form_id
* A unique string identifying the form for validation , submission ,
* theming , and hook_form_alter functions .
* @ param $form
2009-11-28 14:39:31 +00:00
* An associative array containing the structure of the form , which is passed
* by reference . Form validation handlers are able to alter the form structure
* ( like #process and #after_build callbacks during form building) in case of
* a validation error . If a validation handler alters the form structure , it
* is responsible for validating the values of changed form elements in
* $form_state [ 'values' ] to prevent form submit handlers from receiving
* unvalidated values .
2007-05-14 13:43:38 +00:00
* @ param $form_state
* A keyed array containing the current state of the form . The current
* user - submitted data is stored in $form_state [ 'values' ], though
* form validation functions are passed an explicit copy of the
2012-05-30 15:49:24 +00:00
* values for the sake of simplicity . Validation handlers can also use
2007-05-14 13:43:38 +00:00
* $form_state to pass information on to submit handlers . For example :
2010-01-25 10:38:35 +00:00
* $form_state [ 'data_for_submission' ] = $data ;
2007-05-14 13:43:38 +00:00
* This technique is useful when validation requires file parsing ,
* web service requests , or other expensive requests that should
* not be repeated in the submission step .
2006-07-22 19:26:58 +00:00
*/
2009-11-28 14:39:31 +00:00
function drupal_validate_form ( $form_id , & $form , & $form_state ) {
2009-06-02 13:47:26 +00:00
$validated_forms = & drupal_static ( __FUNCTION__ , array ());
2006-04-07 13:00:37 +00:00
2009-03-14 20:13:27 +00:00
if ( isset ( $validated_forms [ $form_id ]) && empty ( $form_state [ 'must_validate' ])) {
2006-04-07 13:00:37 +00:00
return ;
}
2005-10-07 06:11:12 +00:00
2006-08-18 18:58:47 +00:00
// If the session token was set by drupal_prepare_form(), ensure that it
2007-01-23 19:17:55 +00:00
// matches the current user's session.
2005-10-11 19:44:35 +00:00
if ( isset ( $form [ '#token' ])) {
2007-05-14 13:43:38 +00:00
if ( ! drupal_valid_token ( $form_state [ 'values' ][ 'form_token' ], $form [ '#token' ])) {
2011-09-11 20:07:49 +00:00
$path = current_path ();
$query = drupal_get_query_parameters ();
$url = url ( $path , array ( 'query' => $query ));
2007-01-23 19:17:55 +00:00
// Setting this error will cause the form to fail validation.
2011-09-11 20:07:49 +00:00
form_set_error ( 'form_token' , t ( 'The form has become outdated. Copy any unsaved work in the form below and then <a href="@link">reload this page</a>.' , array ( '@link' => $url )));
2005-10-07 06:11:12 +00:00
}
}
2007-05-14 13:43:38 +00:00
_form_validate ( $form , $form_state , $form_id );
2006-04-07 13:00:37 +00:00
$validated_forms [ $form_id ] = TRUE ;
2010-08-27 11:54:32 +00:00
// If validation errors are limited then remove any non validated form values,
// so that only values that passed validation are left for submit callbacks.
if ( isset ( $form_state [ 'triggering_element' ][ '#limit_validation_errors' ]) && $form_state [ 'triggering_element' ][ '#limit_validation_errors' ] !== FALSE ) {
$values = array ();
foreach ( $form_state [ 'triggering_element' ][ '#limit_validation_errors' ] as $section ) {
2010-09-10 07:58:40 +00:00
// If the section exists within $form_state['values'], even if the value
// is NULL, copy it to $values.
$section_exists = NULL ;
2012-12-17 21:54:13 +00:00
$value = NestedArray :: getValue ( $form_state [ 'values' ], $section , $section_exists );
2010-09-10 07:58:40 +00:00
if ( $section_exists ) {
2012-12-17 21:54:13 +00:00
NestedArray :: setValue ( $values , $section , $value );
2010-08-27 11:54:32 +00:00
}
}
2010-11-13 14:04:08 +00:00
// A button's #value does not require validation, so for convenience we
// allow the value of the clicked button to be retained in its normal
// $form_state['values'] locations, even if these locations are not included
// in #limit_validation_errors.
2012-11-21 19:37:44 +00:00
if ( ! empty ( $form_state [ 'triggering_element' ][ '#is_button' ])) {
2010-11-13 14:04:08 +00:00
$button_value = $form_state [ 'triggering_element' ][ '#value' ];
// Like all input controls, the button value may be in the location
// dictated by #parents. If it is, copy it to $values, but do not override
// what may already be in $values.
$parents = $form_state [ 'triggering_element' ][ '#parents' ];
2012-12-17 21:54:13 +00:00
if ( ! NestedArray :: keyExists ( $values , $parents ) && NestedArray :: getValue ( $form_state [ 'values' ], $parents ) === $button_value ) {
NestedArray :: setValue ( $values , $parents , $button_value );
2010-11-13 14:04:08 +00:00
}
// Additionally, form_builder() places the button value in
// $form_state['values'][BUTTON_NAME]. If it's still there, after
// validation handlers have run, copy it to $values, but do not override
// what may already be in $values.
$name = $form_state [ 'triggering_element' ][ '#name' ];
if ( ! isset ( $values [ $name ]) && isset ( $form_state [ 'values' ][ $name ]) && $form_state [ 'values' ][ $name ] === $button_value ) {
$values [ $name ] = $button_value ;
}
2010-08-27 11:54:32 +00:00
}
$form_state [ 'values' ] = $values ;
}
2005-10-07 06:11:12 +00:00
}
2006-07-22 19:26:58 +00:00
/**
2009-09-21 06:44:14 +00:00
* Redirects the user to a URL after a form has been processed .
*
2012-08-20 16:50:21 +00:00
* After a form is submitted and processed , normally the user should be
* redirected to a new destination page . This function figures out what that
* destination should be , based on the $form_state array and the 'destination'
* query string in the request URL , and redirects the user there .
2011-06-22 06:11:46 +00:00
*
2012-08-20 16:50:21 +00:00
* Usually ( for exceptions , see below ) $form_state [ 'redirect' ] determines where
* to redirect the user . This can be set either to a string ( the path to
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
* redirect to ), or an array of arguments for url () . If $form_state [ 'redirect' ]
* is missing , the user is usually ( again , see below for exceptions ) redirected
* back to the page they came from , where they should see a fresh , unpopulated
* copy of the form .
2012-08-20 16:50:21 +00:00
*
* Here is an example of how to set up a form to redirect to the path 'node' :
2011-06-22 06:11:46 +00:00
* @ code
* $form_state [ 'redirect' ] = 'node' ;
* @ endcode
2012-08-20 16:50:21 +00:00
* And here is an example of how to redirect to 'node/123?foo=bar#baz' :
2011-06-22 06:11:46 +00:00
* @ code
* $form_state [ 'redirect' ] = array (
* 'node/123' ,
* array (
* 'query' => array (
* 'foo' => 'bar' ,
* ),
* 'fragment' => 'baz' ,
* ),
* );
* @ endcode
2009-09-21 06:44:14 +00:00
*
2012-08-20 16:50:21 +00:00
* There are several exceptions to the " usual " behavior described above :
2009-09-21 06:44:14 +00:00
* - If $form_state [ 'programmed' ] is TRUE , the form submission was usually
* invoked via drupal_form_submit (), so any redirection would break the script
2012-08-20 16:50:21 +00:00
* that invoked drupal_form_submit () and no redirection is done .
* - If $form_state [ 'rebuild' ] is TRUE , the form is being rebuilt , and no
* redirection is done .
* - If $form_state [ 'no_redirect' ] is TRUE , redirection is disabled . This is
* set , for instance , by ajax_get_form () to prevent redirection in Ajax
* callbacks . $form_state [ 'no_redirect' ] should never be set or altered by
* form builder functions or form validation / submit handlers .
* - If $form_state [ 'redirect' ] is set to FALSE , redirection is disabled .
* - If none of the above conditions has prevented redirection , then the
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
* redirect is accomplished by returning a RedirectResponse , passing in the
* value of $form_state [ 'redirect' ] if it is set , or the current path if it is
* not . RedirectResponse preferentially uses the value of $_GET [ 'destination' ]
2012-08-20 16:50:21 +00:00
* ( the 'destination' URL query string ) if it is present , so this will
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
* override any values set by $form_state [ 'redirect' ] .
2006-07-22 19:26:58 +00:00
*
2009-09-21 06:44:14 +00:00
* @ param $form_state
2012-08-20 16:50:21 +00:00
* An associative array containing the current state of the form .
2009-09-21 06:44:14 +00:00
*
* @ see drupal_process_form ()
* @ see drupal_build_form ()
2006-07-22 19:26:58 +00:00
*/
2009-09-21 06:44:14 +00:00
function drupal_redirect_form ( $form_state ) {
// Skip redirection for form submissions invoked via drupal_form_submit().
if ( ! empty ( $form_state [ 'programmed' ])) {
return ;
2006-07-22 19:26:58 +00:00
}
2009-11-21 14:06:46 +00:00
// Skip redirection if rebuild is activated.
if ( ! empty ( $form_state [ 'rebuild' ])) {
2009-09-21 06:44:14 +00:00
return ;
}
// Skip redirection if it was explicitly disallowed.
if ( ! empty ( $form_state [ 'no_redirect' ])) {
return ;
2006-07-22 19:26:58 +00:00
}
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
// Only invoke a redirection if redirect value was not set to FALSE.
2009-09-21 06:44:14 +00:00
if ( ! isset ( $form_state [ 'redirect' ]) || $form_state [ 'redirect' ] !== FALSE ) {
if ( isset ( $form_state [ 'redirect' ])) {
if ( is_array ( $form_state [ 'redirect' ])) {
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
$options = isset ( $form_state [ 'redirect' ][ 1 ]) ? $form_state [ 'redirect' ][ 1 ] : array ();
// Redirections should always use absolute URLs.
$options [ 'absolute' ] = TRUE ;
$status_code = isset ( $form_state [ 'redirect' ][ 2 ]) ? $form_state [ 'redirect' ][ 2 ] : 302 ;
return new RedirectResponse ( url ( $form_state [ 'redirect' ][ 0 ], $options ), $status_code );
2007-07-01 17:41:16 +00:00
}
else {
2009-07-28 12:13:47 +00:00
// This function can be called from the installer, which guarantees
// that $redirect will always be a string, so catch that case here
// and use the appropriate redirect function.
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
if ( drupal_installation_attempted ()) {
install_goto ( $form_state [ 'redirect' ]);
}
else {
return new RedirectResponse ( url ( $form_state [ 'redirect' ], array ( 'absolute' => TRUE )));
}
2007-07-01 17:41:16 +00:00
}
}
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
$url = url ( Drupal :: request () -> attributes -> get ( 'system_path' ), array (
'query' => Drupal :: request () -> query -> all (),
'absolute' => TRUE ,
));
return new RedirectResponse ( $url );
2007-07-01 17:41:16 +00:00
}
2006-07-22 19:26:58 +00:00
}
2007-01-23 19:17:55 +00:00
/**
2012-02-19 03:41:24 +00:00
* Performs validation on form elements .
*
* First ensures required fields are completed , #maxlength is not exceeded, and
* selected options were in the list of options given to the user . Then calls
* user - defined validators .
2007-01-23 19:17:55 +00:00
*
* @ param $elements
* An associative array containing the structure of the form .
2007-05-14 13:43:38 +00:00
* @ param $form_state
* A keyed array containing the current state of the form . The current
* user - submitted data is stored in $form_state [ 'values' ], though
* form validation functions are passed an explicit copy of the
* values for the sake of simplicity . Validation handlers can also
* $form_state to pass information on to submit handlers . For example :
2010-01-25 10:38:35 +00:00
* $form_state [ 'data_for_submission' ] = $data ;
2007-05-14 13:43:38 +00:00
* This technique is useful when validation requires file parsing ,
* web service requests , or other expensive requests that should
* not be repeated in the submission step .
2007-01-23 19:17:55 +00:00
* @ param $form_id
* A unique string identifying the form for validation , submission ,
* theming , and hook_form_alter functions .
*/
2009-11-28 14:39:31 +00:00
function _form_validate ( & $elements , & $form_state , $form_id = NULL ) {
2006-05-16 10:11:19 +00:00
// Recurse through all children.
foreach ( element_children ( $elements ) as $key ) {
if ( isset ( $elements [ $key ]) && $elements [ $key ]) {
2007-05-14 13:43:38 +00:00
_form_validate ( $elements [ $key ], $form_state );
2006-05-16 10:11:19 +00:00
}
}
2010-01-02 23:30:53 +00:00
2007-12-31 08:54:37 +00:00
// Validate the current input.
2006-11-16 09:06:59 +00:00
if ( ! isset ( $elements [ '#validated' ]) || ! $elements [ '#validated' ]) {
2010-01-02 23:30:53 +00:00
// The following errors are always shown.
2006-03-06 14:46:51 +00:00
if ( isset ( $elements [ '#needs_validation' ])) {
2006-12-21 16:16:44 +00:00
// Verify that the value is not longer than #maxlength.
if ( isset ( $elements [ '#maxlength' ]) && drupal_strlen ( $elements [ '#value' ]) > $elements [ '#maxlength' ]) {
2013-06-17 13:35:07 +00:00
form_error ( $elements , t ( '!name cannot be longer than %max characters but is currently %length characters long.' , array ( '!name' => empty ( $elements [ '#title' ]) ? $elements [ '#parents' ][ 0 ] : $elements [ '#title' ], '%max' => $elements [ '#maxlength' ], '%length' => drupal_strlen ( $elements [ '#value' ]))));
2006-12-21 16:16:44 +00:00
}
2007-10-11 09:22:39 +00:00
if ( isset ( $elements [ '#options' ]) && isset ( $elements [ '#value' ])) {
2006-03-05 02:46:55 +00:00
if ( $elements [ '#type' ] == 'select' ) {
$options = form_options_flatten ( $elements [ '#options' ]);
}
else {
$options = $elements [ '#options' ];
}
if ( is_array ( $elements [ '#value' ])) {
2010-10-21 20:46:58 +00:00
$value = in_array ( $elements [ '#type' ], array ( 'checkboxes' , 'tableselect' )) ? array_keys ( $elements [ '#value' ]) : $elements [ '#value' ];
2006-03-05 02:46:55 +00:00
foreach ( $value as $v ) {
if ( ! isset ( $options [ $v ])) {
2013-06-17 13:35:07 +00:00
form_error ( $elements , t ( 'An illegal choice has been detected. Please contact the site administrator.' ));
2011-07-04 16:58:33 +00:00
watchdog ( 'form' , 'Illegal choice %choice in !name element.' , array ( '%choice' => $v , '!name' => empty ( $elements [ '#title' ]) ? $elements [ '#parents' ][ 0 ] : $elements [ '#title' ]), WATCHDOG_ERROR );
2006-03-05 02:46:55 +00:00
}
2005-12-19 14:30:53 +00:00
}
}
2010-09-24 21:36:22 +00:00
// Non-multiple select fields always have a value in HTML. If the user
// does not change the form, it will be the value of the first option.
// Because of this, form validation for the field will almost always
// pass, even if the user did not select anything. To work around this
2010-10-04 18:00:46 +00:00
// browser behavior, required select fields without a #default_value get
// an additional, first empty option. In case the submitted value is
// identical to the empty option's value, we reset the element's value
// to NULL to trigger the regular #required handling below.
2010-09-24 21:36:22 +00:00
// @see form_process_select()
2010-10-04 18:00:46 +00:00
elseif ( $elements [ '#type' ] == 'select' && ! $elements [ '#multiple' ] && $elements [ '#required' ] && ! isset ( $elements [ '#default_value' ]) && $elements [ '#value' ] === $elements [ '#empty_value' ]) {
$elements [ '#value' ] = NULL ;
form_set_value ( $elements , NULL , $form_state );
2010-09-24 21:36:22 +00:00
}
2006-03-05 02:46:55 +00:00
elseif ( ! isset ( $options [ $elements [ '#value' ]])) {
2013-06-17 13:35:07 +00:00
form_error ( $elements , t ( 'An illegal choice has been detected. Please contact the site administrator.' ));
2011-07-04 16:58:33 +00:00
watchdog ( 'form' , 'Illegal choice %choice in %name element.' , array ( '%choice' => $elements [ '#value' ], '%name' => empty ( $elements [ '#title' ]) ? $elements [ '#parents' ][ 0 ] : $elements [ '#title' ]), WATCHDOG_ERROR );
2006-03-05 02:46:55 +00:00
}
2005-12-19 14:30:53 +00:00
}
}
2010-01-02 23:30:53 +00:00
// While this element is being validated, it may be desired that some calls
// to form_set_error() be suppressed and not result in a form error, so
// that a button that implements low-risk functionality (such as "Previous"
// or "Add more") that doesn't require all user input to be valid can still
2010-03-26 18:58:12 +00:00
// have its submit handlers triggered. The triggering element's
2010-01-02 23:30:53 +00:00
// #limit_validation_errors property contains the information for which
// errors are needed, and all other errors are to be suppressed. The
2010-03-26 18:58:12 +00:00
// #limit_validation_errors property is ignored if submit handlers will run,
// but the element doesn't have a #submit property, because it's too large a
// security risk to have any invalid user input when executing form-level
// submit handlers.
if ( isset ( $form_state [ 'triggering_element' ][ '#limit_validation_errors' ]) && ( $form_state [ 'triggering_element' ][ '#limit_validation_errors' ] !== FALSE ) && ! ( $form_state [ 'submitted' ] && ! isset ( $form_state [ 'triggering_element' ][ '#submit' ]))) {
form_set_error ( NULL , '' , $form_state [ 'triggering_element' ][ '#limit_validation_errors' ]);
2010-01-02 23:30:53 +00:00
}
2010-03-26 18:58:12 +00:00
// If submit handlers won't run (due to the submission having been triggered
// by an element whose #executes_submit_callback property isn't TRUE), then
// it's safe to suppress all validation errors, and we do so by default,
2011-02-19 00:09:11 +00:00
// which is particularly useful during an Ajax submission triggered by a
2010-03-26 18:58:12 +00:00
// non-button. An element can override this default by setting the
// #limit_validation_errors property. For button element types,
// #limit_validation_errors defaults to FALSE (via system_element_info()),
// so that full validation is their default behavior.
elseif ( isset ( $form_state [ 'triggering_element' ]) && ! isset ( $form_state [ 'triggering_element' ][ '#limit_validation_errors' ]) && ! $form_state [ 'submitted' ]) {
form_set_error ( NULL , '' , array ());
}
// As an extra security measure, explicitly turn off error suppression if
// one of the above conditions wasn't met. Since this is also done at the
// end of this function, doing it here is only to handle the rare edge case
// where a validate handler invokes form processing of another form.
2010-01-02 23:30:53 +00:00
else {
drupal_static_reset ( 'form_set_error:limit_validation_errors' );
}
2010-03-26 18:58:12 +00:00
2010-01-02 23:30:53 +00:00
// Make sure a value is passed when the field is required.
2010-09-24 21:36:22 +00:00
if ( isset ( $elements [ '#needs_validation' ]) && $elements [ '#required' ]) {
// A simple call to empty() will not cut it here as some fields, like
// checkboxes, can return a valid value of '0'. Instead, check the
// length if it's a string, and the item count if it's an array.
// An unchecked checkbox has a #value of integer 0, different than string
// '0', which could be a valid value.
$is_empty_multiple = ( ! count ( $elements [ '#value' ]));
$is_empty_string = ( is_string ( $elements [ '#value' ]) && drupal_strlen ( trim ( $elements [ '#value' ])) == 0 );
$is_empty_value = ( $elements [ '#value' ] === 0 );
if ( $is_empty_multiple || $is_empty_string || $is_empty_value ) {
2012-10-06 18:12:27 +00:00
// Flag this element as #required_but_empty to allow #element_validate
// handlers to set a custom required error message, but without having
// to re-implement the complex logic to figure out whether the field
// value is empty.
$elements [ '#required_but_empty' ] = TRUE ;
2010-09-24 21:36:22 +00:00
}
2010-01-02 23:30:53 +00:00
}
2009-07-06 13:31:47 +00:00
// Call user-defined form level validators.
2007-05-14 13:43:38 +00:00
if ( isset ( $form_id )) {
form_execute_handlers ( 'validate' , $elements , $form_state );
}
// Call any element-specific validators. These must act on the element
// #value data.
elseif ( isset ( $elements [ '#element_validate' ])) {
2012-10-11 21:50:53 +00:00
foreach ( $elements [ '#element_validate' ] as $callback ) {
call_user_func_array ( $callback , array ( & $elements , & $form_state , & $form_state [ 'complete_form' ]));
2010-02-04 03:34:21 +00:00
}
2005-10-07 06:11:12 +00:00
}
2012-10-06 18:12:27 +00:00
// Ensure that a #required form error is thrown, regardless of whether
// #element_validate handlers changed any properties. If $is_empty_value
// is defined, then above #required validation code ran, so the other
// variables are also known to be defined and we can test them again.
if ( isset ( $is_empty_value ) && ( $is_empty_multiple || $is_empty_string || $is_empty_value )) {
if ( isset ( $elements [ '#required_error' ])) {
form_error ( $elements , $elements [ '#required_error' ]);
}
// A #title is not mandatory for form elements, but without it we cannot
// set a form error message. So when a visible title is undesirable, form
// constructors are encouraged to set #title anyway, and then set
// #title_display to 'invisible'. This improves accessibility.
elseif ( isset ( $elements [ '#title' ])) {
2013-06-17 13:35:07 +00:00
form_error ( $elements , t ( '!name field is required.' , array ( '!name' => $elements [ '#title' ])));
2012-10-06 18:12:27 +00:00
}
else {
form_error ( $elements );
}
}
2005-10-11 19:44:35 +00:00
$elements [ '#validated' ] = TRUE ;
2005-10-07 06:11:12 +00:00
}
2010-01-02 23:30:53 +00:00
// Done validating this element, so turn off error suppression.
// _form_validate() turns it on again when starting on the next element, if
// it's still appropriate to do so.
drupal_static_reset ( 'form_set_error:limit_validation_errors' );
2005-10-07 06:11:12 +00:00
}
2007-05-14 13:43:38 +00:00
/**
2012-02-19 03:41:24 +00:00
* Executes custom validation and submission handlers for a given form .
*
* Button - specific handlers are checked first . If none exist , the function
* falls back to form - level handlers .
2007-05-14 13:43:38 +00:00
*
* @ param $type
* The type of handler to execute . 'validate' or 'submit' are the
* defaults used by Form API .
* @ param $form
* An associative array containing the structure of the form .
* @ param $form_state
* A keyed array containing the current state of the form . If the user
* submitted the form by clicking a button with custom handler functions
* defined , those handlers will be stored here .
*/
function form_execute_handlers ( $type , & $form , & $form_state ) {
2009-12-01 03:07:34 +00:00
// If there was a button pressed, use its handlers.
2008-04-14 17:48:46 +00:00
if ( isset ( $form_state [ $type . '_handlers' ])) {
$handlers = $form_state [ $type . '_handlers' ];
2007-05-14 13:43:38 +00:00
}
2009-12-01 03:07:34 +00:00
// Otherwise, check for a form-level handler.
2008-04-14 17:48:46 +00:00
elseif ( isset ( $form [ '#' . $type ])) {
$handlers = $form [ '#' . $type ];
2007-05-14 13:43:38 +00:00
}
else {
$handlers = array ();
}
foreach ( $handlers as $function ) {
2010-02-04 03:34:21 +00:00
// Check if a previous _submit handler has set a batch, but make sure we
// do not react to a batch that is already being processed (for instance
// if a batch operation performs a drupal_form_submit()).
if ( $type == 'submit' && ( $batch =& batch_get ()) && ! isset ( $batch [ 'id' ])) {
// Some previous submit handler has set a batch. To ensure correct
// execution order, store the call in a special 'control' batch set.
// See _batch_next_set().
2010-02-01 19:26:13 +00:00
$batch [ 'sets' ][] = array ( 'form_submit' => $function );
2010-02-04 03:34:21 +00:00
$batch [ 'has_form_submits' ] = TRUE ;
2010-02-01 19:26:13 +00:00
}
else {
2012-08-16 11:30:43 +00:00
call_user_func_array ( $function , array ( & $form , & $form_state ));
2007-05-14 13:43:38 +00:00
}
}
}
2005-10-13 10:02:31 +00:00
/**
2009-12-04 16:29:15 +00:00
* Files an error against a form element .
2007-12-23 12:33:13 +00:00
*
2010-06-03 13:48:04 +00:00
* When a validation error is detected , the validator calls form_set_error () to
* indicate which element needs to be changed and provide an error message . This
* causes the Form API to not execute the form submit handlers , and instead to
* re - display the form to the user with the corresponding elements rendered with
* an 'error' CSS class ( shown as red by default ) .
*
* The standard form_set_error () behavior can be changed if a button provides
* the #limit_validation_errors property. Multistep forms not wanting to
* validate the whole form can set #limit_validation_errors on buttons to
* limit validation errors to only certain elements . For example , pressing the
* " Previous " button in a multistep form should not fire validation errors just
* because the current step has invalid values . If #limit_validation_errors is
* set on a clicked button , the button must also define a #submit property
* ( may be set to an empty array ) . Any #submit handlers will be executed even if
* there is invalid input , so extreme care should be taken with respect to any
* actions taken by them . This is typically not a problem with buttons like
* " Previous " or " Add more " that do not invoke persistent storage of the
* submitted form values . Do not use the #limit_validation_errors property on
* buttons that trigger saving of form values to the database .
*
* The #limit_validation_errors property is a list of "sections" within
* $form_state [ 'values' ] that must contain valid values . Each " section " is an
* array with the ordered set of keys needed to reach that part of
* $form_state [ 'values' ] ( i . e . , the #parents property of the element).
*
* Example 1 : Allow the " Previous " button to function , regardless of whether any
* user input is valid .
*
* @ code
* $form [ 'actions' ][ 'previous' ] = array (
* '#type' => 'submit' ,
* '#value' => t ( 'Previous' ),
* '#limit_validation_errors' => array (), // No validation.
* '#submit' => array ( 'some_submit_function' ), // #submit required.
* );
* @ endcode
*
* Example 2 : Require some , but not all , user input to be valid to process the
* submission of a " Previous " button .
*
* @ code
* $form [ 'actions' ][ 'previous' ] = array (
* '#type' => 'submit' ,
* '#value' => t ( 'Previous' ),
* '#limit_validation_errors' => array (
* array ( 'step1' ), // Validate $form_state['values']['step1'].
* array ( 'foo' , 'bar' ), // Validate $form_state['values']['foo']['bar'].
* ),
* '#submit' => array ( 'some_submit_function' ), // #submit required.
* );
* @ endcode
*
* This will require $form_state [ 'values' ][ 'step1' ] and everything within it
* ( for example , $form_state [ 'values' ][ 'step1' ][ 'choice' ]) to be valid , so
* calls to form_set_error ( 'step1' , $message ) or
* form_set_error ( 'step1][choice' , $message ) will prevent the submit handlers
* from running , and result in the error message being displayed to the user .
* However , calls to form_set_error ( 'step2' , $message ) and
* form_set_error ( 'step2][groupX][choiceY' , $message ) will be suppressed ,
* resulting in the message not being displayed to the user , and the submit
* handlers will run despite $form_state [ 'values' ][ 'step2' ] and
* $form_state [ 'values' ][ 'step2' ][ 'groupX' ][ 'choiceY' ] containing invalid
* values . Errors for an invalid $form_state [ 'values' ][ 'foo' ] will be
* suppressed , but errors flagging invalid values for
* $form_state [ 'values' ][ 'foo' ][ 'bar' ] and everything within it will be
* flagged and submission prevented .
*
* Partial form validation is implemented by suppressing errors rather than by
* skipping the input processing and validation steps entirely , because some
* forms have button - level submit handlers that call Drupal API functions that
* assume that certain data exists within $form_state [ 'values' ], and while not
* doing anything with that data that requires it to be valid , PHP errors
* would be triggered if the input processing and validation steps were fully
* skipped .
*
2007-12-23 12:33:13 +00:00
* @ param $name
* The name of the form element . If the #parents property of your form
* element is array ( 'foo' , 'bar' , 'baz' ) then you may set an error on 'foo'
* or 'foo][bar][baz' . Setting an error on 'foo' sets an error for every
* element where the #parents array starts with 'foo'.
* @ param $message
* The error message to present to the user .
2010-01-02 23:30:53 +00:00
* @ param $limit_validation_errors
* Internal use only . The #limit_validation_errors property of the clicked
2010-06-03 13:48:04 +00:00
* button , if it exists .
2010-01-02 23:30:53 +00:00
*
2007-12-23 12:33:13 +00:00
* @ return
2010-01-02 23:30:53 +00:00
* Return value is for internal use only . To get a list of errors , use
2009-12-04 16:29:15 +00:00
* form_get_errors () or form_get_error () .
2012-02-19 03:41:24 +00:00
*
* @ see http :// drupal . org / node / 370537
* @ see http :// drupal . org / node / 763376
2005-10-13 10:02:31 +00:00
*/
2010-01-02 23:30:53 +00:00
function form_set_error ( $name = NULL , $message = '' , $limit_validation_errors = NULL ) {
2009-06-02 13:47:26 +00:00
$form = & drupal_static ( __FUNCTION__ , array ());
2010-01-02 23:30:53 +00:00
$sections = & drupal_static ( __FUNCTION__ . ':limit_validation_errors' );
if ( isset ( $limit_validation_errors )) {
$sections = $limit_validation_errors ;
}
2005-10-13 10:02:31 +00:00
if ( isset ( $name ) && ! isset ( $form [ $name ])) {
2010-01-02 23:30:53 +00:00
$record = TRUE ;
if ( isset ( $sections )) {
// #limit_validation_errors is an array of "sections" within which user
// input must be valid. If the element is within one of these sections,
// the error must be recorded. Otherwise, it can be suppressed.
// #limit_validation_errors can be an empty array, in which case all
// errors are suppressed. For example, a "Previous" button might want its
// submit action to be triggered even if none of the submitted values are
// valid.
$record = FALSE ;
foreach ( $sections as $section ) {
// Exploding by '][' reconstructs the element's #parents. If the
// reconstructed #parents begin with the same keys as the specified
// section, then the element's values are within the part of
// $form_state['values'] that the clicked button requires to be valid,
2010-11-15 10:06:32 +00:00
// so errors for this element must be recorded. As the exploded array
// will all be strings, we need to cast every value of the section
// array to string.
2010-11-17 04:10:52 +00:00
if ( array_slice ( explode ( '][' , $name ), 0 , count ( $section )) === array_map ( 'strval' , $section )) {
2010-01-02 23:30:53 +00:00
$record = TRUE ;
break ;
}
}
}
if ( $record ) {
$form [ $name ] = $message ;
if ( $message ) {
drupal_set_message ( $message , 'error' );
}
2006-01-24 10:15:03 +00:00
}
2005-10-13 10:02:31 +00:00
}
2010-01-02 23:30:53 +00:00
2005-10-13 10:02:31 +00:00
return $form ;
}
2009-06-02 13:47:26 +00:00
/**
2012-02-19 03:41:24 +00:00
* Clears all errors against all form elements made by form_set_error () .
2009-06-02 13:47:26 +00:00
*/
function form_clear_error () {
drupal_static_reset ( 'form_set_error' );
}
2005-10-13 10:02:31 +00:00
/**
2012-02-19 03:41:24 +00:00
* Returns an associative array of all errors .
2005-10-13 10:02:31 +00:00
*/
function form_get_errors () {
$form = form_set_error ();
if ( ! empty ( $form )) {
return $form ;
}
}
/**
2010-09-16 20:14:49 +00:00
* Returns the error message filed against the given form element .
*
* Form errors higher up in the form structure override deeper errors as well as
* errors on the element itself .
2005-10-13 10:02:31 +00:00
*/
function form_get_error ( $element ) {
$form = form_set_error ();
2010-09-16 20:14:49 +00:00
$parents = array ();
foreach ( $element [ '#parents' ] as $parent ) {
$parents [] = $parent ;
$key = implode ( '][' , $parents );
if ( isset ( $form [ $key ])) {
return $form [ $key ];
}
2005-10-13 10:02:31 +00:00
}
}
2005-10-07 06:11:12 +00:00
/**
2012-02-19 03:41:24 +00:00
* Flags an element as having an error .
2005-10-07 06:11:12 +00:00
*/
2006-01-24 10:15:03 +00:00
function form_error ( & $element , $message = '' ) {
2005-10-13 10:02:31 +00:00
form_set_error ( implode ( '][' , $element [ '#parents' ]), $message );
2005-10-07 06:11:12 +00:00
}
/**
2012-02-19 03:41:24 +00:00
* Builds and processes all elements in the structured form array .
*
* Adds any required properties to each element , maps the incoming input data
* to the proper elements , and executes any #process handlers attached to a
* specific element .
2010-07-02 12:37:57 +00:00
*
* This is one of the three primary functions that recursively iterates a form
* array . This one does it for completing the form building process . The other
* two are _form_validate () ( invoked via drupal_validate_form () and used to
* invoke validation logic for each element ) and drupal_render () ( for rendering
* each element ) . Each of these three pipelines provides ample opportunity for
* modules to customize what happens . For example , during this function ' s life
* cycle , the following functions get called for each element :
* - $element [ '#value_callback' ] : A function that implements how user input is
* mapped to an element ' s #value property. This defaults to a function named
* 'form_type_TYPE_value' where TYPE is $element [ '#type' ] .
* - $element [ '#process' ] : An array of functions called after user input has
* been mapped to the element ' s #value property. These functions can be used
* to dynamically add child elements : for example , for the 'date' element
2013-02-16 23:29:53 +00:00
* type , one of the functions in this array is form_process_datetime (), which adds
* the individual 'date' , and 'time' . child elements . These functions
2010-07-02 12:37:57 +00:00
* can also be used to set additional properties or implement special logic
2012-11-27 07:06:47 +00:00
* other than adding child elements : for example , for the 'details' element
* type , one of the functions in this array is form_process_details (), which
2013-02-01 21:48:05 +00:00
* adds the attributes and JavaScript needed to make the details work in older
* browsers . The #process functions are called in preorder traversal, meaning
* they are called for the parent element first , then for the child elements .
2013-05-22 00:22:44 +00:00
* - $element [ '#after_build' ] : An array of callables called after form_builder ()
2010-07-02 12:37:57 +00:00
* is done with its processing of the element . These are called in postorder
* traversal , meaning they are called for the child elements first , then for
* the parent element .
* There are similar properties containing callback functions invoked by
* _form_validate () and drupal_render (), appropriate for those operations .
*
* Developers are strongly encouraged to integrate the functionality needed by
* their form or module within one of these three pipelines , using the
* appropriate callback property , rather than implementing their own recursive
* traversal of a form array . This facilitates proper integration between
* multiple modules . For example , module developers are familiar with the
* relative order in which hook_form_alter () implementations and #process
* functions run . A custom traversal function that affects the building of a
* form is likely to not integrate with hook_form_alter () and #process in the
* expected way . Also , deep recursion within PHP is both slow and memory
* intensive , so it is best to minimize how often it ' s done .
*
* As stated above , each element ' s #process functions are executed after its
* #value has been set. This enables those functions to execute conditional
* logic based on the current value . However , all of form_builder () runs before
* drupal_validate_form () is called , so during #process function execution, the
* element ' s #value has not yet been validated, so any code that requires
* validated values must reside within a submit handler .
*
* As a security measure , user input is used for an element ' s #value only if the
* element exists within $form , is not disabled ( as per the #disabled property),
* and can be accessed ( as per the #access property, except that forms submitted
* using drupal_form_submit () bypass #access restrictions). When user input is
* ignored due to #disabled and #access restrictions, the element's default
* value is used .
*
* Because of the preorder traversal , where #process functions of an element run
* before user input for its child elements is processed , and because of the
* Form API security of user input processing with respect to #access and
* #disabled described above, this generally means that #process functions
* should not use an element ' s ( unvalidated ) #value to affect the #disabled or
* #access of child elements. Use-cases where a developer may be tempted to
* implement such conditional logic usually fall into one of two categories :
* - Where user input from the current submission must affect the structure of a
* form , including properties like #access and #disabled that affect how the
* next submission needs to be processed , a multi - step workflow is needed .
* This is most commonly implemented with a submit handler setting persistent
* data within $form_state based on * validated * values in
* $form_state [ 'values' ] and setting $form_state [ 'rebuild' ] . The form building
2010-12-18 06:29:46 +00:00
* functions must then be implemented to use the $form_state data to rebuild
2010-07-02 12:37:57 +00:00
* the form with the structure appropriate for the new state .
* - Where user input must affect the rendering of the form without affecting
* its structure , the necessary conditional rendering logic should reside
* within functions that run during the rendering phase ( #pre_render, #theme,
* #theme_wrappers, and #post_render).
2006-02-17 10:51:57 +00:00
*
* @ param $form_id
2006-07-22 19:26:58 +00:00
* A unique string identifying the form for validation , submission ,
* theming , and hook_form_alter functions .
2009-07-06 13:31:47 +00:00
* @ param $element
* An associative array containing the structure of the current element .
2007-05-14 13:43:38 +00:00
* @ param $form_state
* A keyed array containing the current state of the form . In this
* context , it is used to accumulate information about which button
* was clicked when the form was submitted , as well as the sanitized
* $_POST data .
2005-10-07 06:11:12 +00:00
*/
2010-10-13 13:43:21 +00:00
function form_builder ( $form_id , & $element , & $form_state ) {
2006-06-23 08:31:23 +00:00
// Initialize as unprocessed.
2009-07-06 13:31:47 +00:00
$element [ '#processed' ] = FALSE ;
2006-06-23 08:31:23 +00:00
2007-12-31 08:54:37 +00:00
// Use element defaults.
2010-04-24 14:49:14 +00:00
if ( isset ( $element [ '#type' ]) && empty ( $element [ '#defaults_loaded' ]) && ( $info = element_info ( $element [ '#type' ]))) {
2009-07-06 13:31:47 +00:00
// Overlay $info onto $element, retaining preexisting keys in $element.
$element += $info ;
$element [ '#defaults_loaded' ] = TRUE ;
}
2009-11-16 05:11:01 +00:00
// Assign basic defaults common for all form elements.
$element += array (
'#required' => FALSE ,
'#attributes' => array (),
2009-12-02 15:09:16 +00:00
'#title_display' => 'before' ,
2009-11-16 05:11:01 +00:00
);
2009-07-06 13:31:47 +00:00
// Special handling if we're on the top level form element.
if ( isset ( $element [ '#type' ]) && $element [ '#type' ] == 'form' ) {
2013-04-09 20:38:06 +00:00
if ( ! empty ( $element [ '#https' ]) && settings () -> get ( 'mixed_mode_sessions' , FALSE ) &&
2009-11-01 23:02:13 +00:00
! url_is_external ( $element [ '#action' ])) {
2009-09-05 13:05:31 +00:00
global $base_root ;
// Not an external URL so ensure that it is secure.
$element [ '#action' ] = str_replace ( 'http://' , 'https://' , $base_root ) . $element [ '#action' ];
}
2010-10-13 13:43:21 +00:00
// Store a reference to the complete form in $form_state prior to building
// the form. This allows advanced #process and #after_build callbacks to
// perform changes elsewhere in the form.
2011-08-01 03:41:25 +00:00
$form_state [ 'complete_form' ] = & $element ;
2010-10-13 13:43:21 +00:00
2009-07-06 13:31:47 +00:00
// Set a flag if we have a correct form submission. This is always TRUE for
// programmed forms coming from drupal_form_submit(), or if the form_id coming
// from the POST data is set and matches the current form_id.
if ( $form_state [ 'programmed' ] || ( ! empty ( $form_state [ 'input' ]) && ( isset ( $form_state [ 'input' ][ 'form_id' ]) && ( $form_state [ 'input' ][ 'form_id' ] == $form_id )))) {
$form_state [ 'process_input' ] = TRUE ;
}
else {
$form_state [ 'process_input' ] = FALSE ;
2007-10-16 14:06:23 +00:00
}
2010-12-08 06:55:01 +00:00
// All form elements should have an #array_parents property.
$element [ '#array_parents' ] = array ();
2007-06-17 12:07:51 +00:00
}
2009-07-06 13:31:47 +00:00
if ( ! isset ( $element [ '#id' ])) {
2009-10-05 01:18:26 +00:00
$element [ '#id' ] = drupal_html_id ( 'edit-' . implode ( '-' , $element [ '#parents' ]));
2009-04-11 22:19:46 +00:00
}
2012-04-05 07:09:10 +00:00
// Add the aria-describedby attribute to associate the form control with its
// description.
if ( ! empty ( $element [ '#description' ])) {
$element [ '#attributes' ][ 'aria-describedby' ] = $element [ '#id' ] . '--description' ;
}
2009-07-06 13:31:47 +00:00
// Handle input elements.
if ( ! empty ( $element [ '#input' ])) {
_form_builder_handle_input_element ( $form_id , $element , $form_state );
2005-12-19 14:43:42 +00:00
}
2009-04-11 22:19:46 +00:00
// Allow for elements to expand to multiple elements, e.g., radios,
// checkboxes and files.
2009-07-06 13:31:47 +00:00
if ( isset ( $element [ '#process' ]) && ! $element [ '#processed' ]) {
foreach ( $element [ '#process' ] as $process ) {
2012-08-16 11:30:43 +00:00
$element = call_user_func_array ( $process , array ( & $element , & $form_state , & $form_state [ 'complete_form' ]));
2009-04-11 22:19:46 +00:00
}
2009-07-06 13:31:47 +00:00
$element [ '#processed' ] = TRUE ;
2009-04-11 22:19:46 +00:00
}
2005-12-19 14:43:42 +00:00
2007-01-10 23:30:07 +00:00
// We start off assuming all form elements are in the correct order.
2009-07-06 13:31:47 +00:00
$element [ '#sorted' ] = TRUE ;
2007-01-10 23:30:07 +00:00
2005-10-07 06:11:12 +00:00
// Recurse through all child elements.
2007-01-10 23:30:07 +00:00
$count = 0 ;
2009-07-06 13:31:47 +00:00
foreach ( element_children ( $element ) as $key ) {
2010-05-17 21:23:32 +00:00
// Prior to checking properties of child elements, their default properties
// need to be loaded.
if ( isset ( $element [ $key ][ '#type' ]) && empty ( $element [ $key ][ '#defaults_loaded' ]) && ( $info = element_info ( $element [ $key ][ '#type' ]))) {
$element [ $key ] += $info ;
$element [ $key ][ '#defaults_loaded' ] = TRUE ;
}
2007-01-23 19:17:55 +00:00
// Don't squash an existing tree value.
2009-07-06 13:31:47 +00:00
if ( ! isset ( $element [ $key ][ '#tree' ])) {
$element [ $key ][ '#tree' ] = $element [ '#tree' ];
2005-11-18 13:48:09 +00:00
}
2005-10-26 01:24:09 +00:00
2007-01-23 19:17:55 +00:00
// Deny access to child elements if parent is denied.
2009-07-06 13:31:47 +00:00
if ( isset ( $element [ '#access' ]) && ! $element [ '#access' ]) {
$element [ $key ][ '#access' ] = FALSE ;
2006-08-22 11:13:04 +00:00
}
2010-07-10 00:03:37 +00:00
// Make child elements inherit their parent's #disabled and #allow_focus
// values unless they specify their own.
foreach ( array ( '#disabled' , '#allow_focus' ) as $property ) {
if ( isset ( $element [ $property ]) && ! isset ( $element [ $key ][ $property ])) {
$element [ $key ][ $property ] = $element [ $property ];
}
}
2007-01-23 19:17:55 +00:00
// Don't squash existing parents value.
2009-07-06 13:31:47 +00:00
if ( ! isset ( $element [ $key ][ '#parents' ])) {
2007-01-23 19:17:55 +00:00
// Check to see if a tree of child elements is present. If so,
// continue down the tree if required.
2009-07-06 13:31:47 +00:00
$element [ $key ][ '#parents' ] = $element [ $key ][ '#tree' ] && $element [ '#tree' ] ? array_merge ( $element [ '#parents' ], array ( $key )) : array ( $key );
2005-10-26 01:24:09 +00:00
}
2009-11-22 07:32:47 +00:00
// Ensure #array_parents follows the actual form structure.
2010-12-08 06:55:01 +00:00
$array_parents = $element [ '#array_parents' ];
2009-11-22 07:32:47 +00:00
$array_parents [] = $key ;
$element [ $key ][ '#array_parents' ] = $array_parents ;
2005-10-26 01:24:09 +00:00
2007-01-23 19:17:55 +00:00
// Assign a decimal placeholder weight to preserve original array order.
2009-07-06 13:31:47 +00:00
if ( ! isset ( $element [ $key ][ '#weight' ])) {
$element [ $key ][ '#weight' ] = $count / 1000 ;
2005-11-18 13:48:09 +00:00
}
2007-01-10 23:30:07 +00:00
else {
2007-01-23 19:17:55 +00:00
// If one of the child elements has a weight then we will need to sort
// later.
2009-07-06 13:31:47 +00:00
unset ( $element [ '#sorted' ]);
2007-01-10 23:30:07 +00:00
}
2009-07-06 13:31:47 +00:00
$element [ $key ] = form_builder ( $form_id , $element [ $key ], $form_state );
2005-10-07 06:11:12 +00:00
$count ++ ;
}
2007-05-14 13:43:38 +00:00
// The #after_build flag allows any piece of a form to be altered
// after normal input parsing has been completed.
2009-07-06 13:31:47 +00:00
if ( isset ( $element [ '#after_build' ]) && ! isset ( $element [ '#after_build_done' ])) {
2013-05-22 00:22:44 +00:00
foreach ( $element [ '#after_build' ] as $callable ) {
$element = call_user_func_array ( $callable , array ( $element , & $form_state ));
2007-05-14 13:43:38 +00:00
}
2010-01-02 10:41:56 +00:00
$element [ '#after_build_done' ] = TRUE ;
2007-05-14 13:43:38 +00:00
}
2009-07-06 13:31:47 +00:00
// If there is a file element, we need to flip a flag so later the
2008-11-15 13:01:11 +00:00
// form encoding can be set.
2009-07-06 13:31:47 +00:00
if ( isset ( $element [ '#type' ]) && $element [ '#type' ] == 'file' ) {
$form_state [ 'has_file_element' ] = TRUE ;
2008-11-15 13:01:11 +00:00
}
2009-07-06 13:31:47 +00:00
2010-03-26 18:58:12 +00:00
// Final tasks for the form element after form_builder() has run for all other
// elements.
2009-07-06 13:31:47 +00:00
if ( isset ( $element [ '#type' ]) && $element [ '#type' ] == 'form' ) {
2008-11-24 00:40:45 +00:00
// If there is a file element, we set the form encoding.
2009-07-06 13:31:47 +00:00
if ( isset ( $form_state [ 'has_file_element' ])) {
$element [ '#attributes' ][ 'enctype' ] = 'multipart/form-data' ;
2008-11-24 00:40:45 +00:00
}
2010-03-26 18:58:12 +00:00
// If a form contains a single textfield, and the ENTER key is pressed
// within it, Internet Explorer submits the form with no POST data
// identifying any submit button. Other browsers submit POST data as though
// the user clicked the first button. Therefore, to be as consistent as we
// can be across browsers, if no 'triggering_element' has been identified
// yet, default it to the first button.
if ( ! $form_state [ 'programmed' ] && ! isset ( $form_state [ 'triggering_element' ]) && ! empty ( $form_state [ 'buttons' ])) {
$form_state [ 'triggering_element' ] = $form_state [ 'buttons' ][ 0 ];
}
// If the triggering element specifies "button-level" validation and submit
// handlers to run instead of the default form-level ones, then add those to
// the form state.
foreach ( array ( 'validate' , 'submit' ) as $type ) {
if ( isset ( $form_state [ 'triggering_element' ][ '#' . $type ])) {
$form_state [ $type . '_handlers' ] = $form_state [ 'triggering_element' ][ '#' . $type ];
}
}
// If the triggering element executes submit handlers, then set the form
// state key that's needed for those handlers to run.
if ( ! empty ( $form_state [ 'triggering_element' ][ '#executes_submit_callback' ])) {
$form_state [ 'submitted' ] = TRUE ;
}
// Special processing if the triggering element is a button.
2012-11-21 19:37:44 +00:00
if ( ! empty ( $form_state [ 'triggering_element' ][ '#is_button' ])) {
2010-03-26 18:58:12 +00:00
// Because there are several ways in which the triggering element could
// have been determined (including from input variables set by JavaScript
// or fallback behavior implemented for IE), and because buttons often
// have their #name property not derived from their #parents property, we
// can't assume that input processing that's happened up until here has
// resulted in $form_state['values'][BUTTON_NAME] being set. But it's
// common for forms to have several buttons named 'op' and switch on
// $form_state['values']['op'] during submit handler execution.
$form_state [ 'values' ][ $form_state [ 'triggering_element' ][ '#name' ]] = $form_state [ 'triggering_element' ][ '#value' ];
}
2007-11-19 19:23:28 +00:00
}
2009-07-06 13:31:47 +00:00
return $element ;
2007-05-14 13:43:38 +00:00
}
/**
2012-02-19 03:41:24 +00:00
* Adds the #name and #value properties of an input element before rendering.
2007-05-14 13:43:38 +00:00
*/
2009-07-06 13:31:47 +00:00
function _form_builder_handle_input_element ( $form_id , & $element , & $form_state ) {
if ( ! isset ( $element [ '#name' ])) {
$name = array_shift ( $element [ '#parents' ]);
$element [ '#name' ] = $name ;
if ( $element [ '#type' ] == 'file' ) {
2007-05-14 13:43:38 +00:00
// To make it easier to handle $_FILES in file.inc, we place all
// file fields in the 'files' array. Also, we do not support
// nested file names.
2009-07-06 13:31:47 +00:00
$element [ '#name' ] = 'files[' . $element [ '#name' ] . ']' ;
2007-05-14 13:43:38 +00:00
}
2009-07-06 13:31:47 +00:00
elseif ( count ( $element [ '#parents' ])) {
$element [ '#name' ] .= '[' . implode ( '][' , $element [ '#parents' ]) . ']' ;
2007-05-14 13:43:38 +00:00
}
2009-07-06 13:31:47 +00:00
array_unshift ( $element [ '#parents' ], $name );
2007-05-14 13:43:38 +00:00
}
2010-04-11 19:00:27 +00:00
// Setting #disabled to TRUE results in user input being ignored, regardless
// of how the element is themed or whether JavaScript is used to change the
// control's attributes. However, it's good UI to let the user know that input
// is not wanted for the control. HTML supports two attributes for this:
// http://www.w3.org/TR/html401/interact/forms.html#h-17.12. If a form wants
// to start a control off with one of these attributes for UI purposes only,
// but still allow input to be processed if it's sumitted, it can set the
// desired attribute in #attributes directly rather than using #disabled.
// However, developers should think carefully about the accessibility
// implications of doing so: if the form expects input to be enterable under
// some condition triggered by JavaScript, how would someone who has
// JavaScript disabled trigger that condition? Instead, developers should
// consider whether a multi-step form would be more appropriate (#disabled can
// be changed from step to step). If one still decides to use JavaScript to
// affect when a control is enabled, then it is best for accessibility for the
// control to be enabled in the HTML, and disabled by JavaScript on document
// ready.
2009-07-06 13:31:47 +00:00
if ( ! empty ( $element [ '#disabled' ])) {
2010-04-11 19:00:27 +00:00
if ( ! empty ( $element [ '#allow_focus' ])) {
$element [ '#attributes' ][ 'readonly' ] = 'readonly' ;
}
else {
$element [ '#attributes' ][ 'disabled' ] = 'disabled' ;
}
2007-05-14 13:43:38 +00:00
}
2010-07-02 12:37:57 +00:00
// With JavaScript or other easy hacking, input can be submitted even for
// elements with #access=FALSE or #disabled=TRUE. For security, these must
// not be processed. Forms that set #disabled=TRUE on an element do not
// expect input for the element, and even forms submitted with
// drupal_form_submit() must not be able to get around this. Forms that set
// #access=FALSE on an element usually allow access for some users, so forms
// submitted with drupal_form_submit() may bypass access restriction and be
2011-09-24 20:44:06 +00:00
// treated as high-privilege users instead.
2010-07-02 12:37:57 +00:00
$process_input = empty ( $element [ '#disabled' ]) && ( $form_state [ 'programmed' ] || ( $form_state [ 'process_input' ] && ( ! isset ( $element [ '#access' ]) || $element [ '#access' ])));
2009-07-06 13:31:47 +00:00
// Set the element's #value property.
if ( ! isset ( $element [ '#value' ]) && ! array_key_exists ( '#value' , $element )) {
$value_callback = ! empty ( $element [ '#value_callback' ]) ? $element [ '#value_callback' ] : 'form_type_' . $element [ '#type' ] . '_value' ;
2010-07-02 12:37:57 +00:00
if ( $process_input ) {
2009-12-01 03:07:34 +00:00
// Get the input for the current element. NULL values in the input need to
// be explicitly distinguished from missing input. (see below)
2010-09-10 07:58:40 +00:00
$input_exists = NULL ;
2012-12-17 21:54:13 +00:00
$input = NestedArray :: getValue ( $form_state [ 'input' ], $element [ '#parents' ], $input_exists );
2009-12-01 03:07:34 +00:00
// For browser-submitted forms, the submitted values do not contain values
// for certain elements (empty multiple select, unchecked checkbox).
// During initial form processing, we add explicit NULL values for such
// elements in $form_state['input']. When rebuilding the form, we can
// distinguish elements having NULL input from elements that were not part
// of the initially submitted form and can therefore use default values
// for the latter, if required. Programmatically submitted forms can
// submit explicit NULL values when calling drupal_form_submit(), so we do
// not modify $form_state['input'] for them.
if ( ! $input_exists && ! $form_state [ 'rebuild' ] && ! $form_state [ 'programmed' ]) {
2010-08-27 11:54:32 +00:00
// Add the necessary parent keys to $form_state['input'] and sets the
// element's input value to NULL.
2012-12-17 21:54:13 +00:00
NestedArray :: setValue ( $form_state [ 'input' ], $element [ '#parents' ], NULL );
2009-12-01 03:07:34 +00:00
$input_exists = TRUE ;
2007-05-14 13:43:38 +00:00
}
2009-12-01 03:07:34 +00:00
// If we have input for the current element, assign it to the #value
// property, optionally filtered through $value_callback.
if ( $input_exists ) {
2009-08-24 00:14:23 +00:00
if ( function_exists ( $value_callback )) {
2009-07-06 13:31:47 +00:00
$element [ '#value' ] = $value_callback ( $element , $input , $form_state );
2007-05-14 13:43:38 +00:00
}
2009-07-06 13:31:47 +00:00
if ( ! isset ( $element [ '#value' ]) && isset ( $input )) {
$element [ '#value' ] = $input ;
2007-05-14 13:43:38 +00:00
}
}
2007-07-04 15:45:37 +00:00
// Mark all posted values for validation.
2009-07-06 13:31:47 +00:00
if ( isset ( $element [ '#value' ]) || ( ! empty ( $element [ '#required' ]))) {
$element [ '#needs_validation' ] = TRUE ;
2007-07-04 15:45:37 +00:00
}
2007-05-14 13:43:38 +00:00
}
2007-07-04 15:45:37 +00:00
// Load defaults.
2009-07-06 13:31:47 +00:00
if ( ! isset ( $element [ '#value' ])) {
2007-07-04 15:45:37 +00:00
// Call #type_value without a second argument to request default_value handling.
2009-08-24 00:14:23 +00:00
if ( function_exists ( $value_callback )) {
2009-08-12 11:45:14 +00:00
$element [ '#value' ] = $value_callback ( $element , FALSE , $form_state );
2007-05-14 13:43:38 +00:00
}
2007-07-04 15:45:37 +00:00
// Final catch. If we haven't set a value yet, use the explicit default value.
2008-01-28 00:15:34 +00:00
// Avoid image buttons (which come with garbage value), so we only get value
// for the button actually clicked.
2009-07-06 13:31:47 +00:00
if ( ! isset ( $element [ '#value' ]) && empty ( $element [ '#has_garbage_value' ])) {
$element [ '#value' ] = isset ( $element [ '#default_value' ]) ? $element [ '#default_value' ] : '' ;
2006-04-20 07:11:37 +00:00
}
}
2005-10-26 01:24:09 +00:00
}
2006-04-06 15:30:19 +00:00
2010-03-26 18:58:12 +00:00
// Determine which element (if any) triggered the submission of the form and
2010-07-02 12:37:57 +00:00
// keep track of all the clickable buttons in the form for
// form_state_values_clean(). Enforce the same input processing restrictions
// as above.
if ( $process_input ) {
2011-02-19 00:09:11 +00:00
// Detect if the element triggered the submission via Ajax.
2010-03-26 18:58:12 +00:00
if ( _form_element_triggered_scripted_submission ( $element , $form_state )) {
$form_state [ 'triggering_element' ] = $element ;
}
2011-02-19 00:09:11 +00:00
// If the form was submitted by the browser rather than via Ajax, then it
2010-03-26 18:58:12 +00:00
// can only have been triggered by a button, and we need to determine which
// button within the constraints of how browsers provide this information.
2012-11-21 19:37:44 +00:00
if ( ! empty ( $element [ '#is_button' ])) {
2010-03-26 18:58:12 +00:00
// All buttons in the form need to be tracked for
// form_state_values_clean() and for the form_builder() code that handles
// a form submission containing no button information in $_POST.
2010-07-02 12:37:57 +00:00
$form_state [ 'buttons' ][] = $element ;
2010-03-26 18:58:12 +00:00
if ( _form_button_was_clicked ( $element , $form_state )) {
$form_state [ 'triggering_element' ] = $element ;
2007-05-14 13:43:38 +00:00
}
}
}
2010-03-26 18:58:12 +00:00
2010-03-07 23:14:20 +00:00
// Set the element's value in $form_state['values'], but only, if its key
// does not exist yet (a #value_callback may have already populated it).
2012-12-17 21:54:13 +00:00
if ( ! NestedArray :: keyExists ( $form_state [ 'values' ], $element [ '#parents' ])) {
2010-03-07 23:14:20 +00:00
form_set_value ( $element , $element [ '#value' ], $form_state );
}
2007-05-14 13:43:38 +00:00
}
2007-07-29 17:28:23 +00:00
/**
2012-02-19 03:41:24 +00:00
* Detects if an element triggered the form submission via Ajax .
2007-07-29 17:28:23 +00:00
*
2010-03-26 18:58:12 +00:00
* This detects button or non - button controls that trigger a form submission via
2011-02-19 00:09:11 +00:00
* Ajax or some other scriptable environment . These environments can set the
2010-03-26 18:58:12 +00:00
* special input key '_triggering_element_name' to identify the triggering
* element . If the name alone doesn ' t identify the element uniquely , the input
* key '_triggering_element_value' may also be set to require a match on element
* value . An example where this is needed is if there are several buttons all
* named 'op' , and only differing in their value .
2007-07-29 17:28:23 +00:00
*/
2010-03-26 18:58:12 +00:00
function _form_element_triggered_scripted_submission ( $element , & $form_state ) {
if ( ! empty ( $form_state [ 'input' ][ '_triggering_element_name' ]) && $element [ '#name' ] == $form_state [ 'input' ][ '_triggering_element_name' ]) {
if ( empty ( $form_state [ 'input' ][ '_triggering_element_value' ]) || $form_state [ 'input' ][ '_triggering_element_value' ] == $element [ '#value' ]) {
return TRUE ;
}
}
return FALSE ;
}
/**
2012-02-19 03:41:24 +00:00
* Determines if a given button triggered the form submission .
2010-03-26 18:58:12 +00:00
*
* This detects button controls that trigger a form submission by being clicked
* and having the click processed by the browser rather than being captured by
* JavaScript . Essentially , it detects if the button ' s name and value are part
* of the POST data , but with extra code to deal with the convoluted way in
* which browsers submit data for image button clicks .
*
2011-02-19 00:09:11 +00:00
* This does not detect button clicks processed by Ajax ( that is done in
2010-03-26 18:58:12 +00:00
* _form_element_triggered_scripted_submission ()) and it does not detect form
* submissions from Internet Explorer in response to an ENTER key pressed in a
* textfield ( form_builder () has extra code for that ) .
*
* Because this function contains only part of the logic needed to determine
* $form_state [ 'triggering_element' ], it should not be called from anywhere
* other than within the Form API . Form validation and submit handlers needing
* to know which button was clicked should get that information from
* $form_state [ 'triggering_element' ] .
*/
function _form_button_was_clicked ( $element , & $form_state ) {
2007-07-29 17:28:23 +00:00
// First detect normal 'vanilla' button clicks. Traditionally, all
// standard buttons on a form share the same name (usually 'op'),
// and the specific return value is used to determine which was
// clicked. This ONLY works as long as $form['#name'] puts the
// value at the top level of the tree of $_POST data.
2010-03-26 18:58:12 +00:00
if ( isset ( $form_state [ 'input' ][ $element [ '#name' ]]) && $form_state [ 'input' ][ $element [ '#name' ]] == $element [ '#value' ]) {
2007-07-29 17:28:23 +00:00
return TRUE ;
}
// When image buttons are clicked, browsers do NOT pass the form element
// value in $_POST. Instead they pass an integer representing the
// coordinates of the click on the button image. This means that image
// buttons MUST have unique $form['#name'] values, but the details of
// their $_POST data should be ignored.
2010-03-26 18:58:12 +00:00
elseif ( ! empty ( $element [ '#has_garbage_value' ]) && isset ( $element [ '#value' ]) && $element [ '#value' ] !== '' ) {
2007-07-29 17:28:23 +00:00
return TRUE ;
}
return FALSE ;
}
2009-10-15 11:47:25 +00:00
/**
* 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
*
2011-05-08 19:50:38 +00:00
* @ param $form_state
2009-10-15 11:47:25 +00:00
* 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.
2010-03-26 18:58:12 +00:00
// 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 );
2011-11-03 07:32:28 +00:00
$key_exists = NULL ;
2012-12-17 21:54:13 +00:00
$values = & NestedArray :: getValue ( $form_state [ 'values' ], $parents , $key_exists );
2011-11-03 07:32:28 +00:00
if ( $key_exists && is_array ( $values )) {
unset ( $values [ $last_parent ]);
2009-10-15 11:47:25 +00:00
}
}
}
2007-08-10 10:51:17 +00:00
/**
2012-02-19 03:41:24 +00:00
* Determines the value for an image button form element .
2007-08-10 10:51:17 +00:00
*
* @ param $form
* The form element whose value is being populated .
2009-07-06 13:31:47 +00:00
* @ param $input
* The incoming input to populate the form element . If this is FALSE ,
2007-08-10 10:51:17 +00:00
* the element ' s default value should be returned .
2009-06-29 17:27:58 +00:00
* @ param $form_state
2009-03-14 20:13:27 +00:00
* A keyed array containing the current state of the form .
2012-02-19 03:41:24 +00:00
*
2007-08-10 10:51:17 +00:00
* @ return
* The data that will appear in the $form_state [ 'values' ] collection
* for this element . Return nothing to use the default .
*/
2009-07-06 13:31:47 +00:00
function form_type_image_button_value ( $form , $input , $form_state ) {
if ( $input !== FALSE ) {
if ( ! empty ( $input )) {
2007-08-10 10:51:17 +00:00
// 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'.
2009-07-06 13:31:47 +00:00
$input = $form_state [ 'input' ];
2009-06-29 17:27:58 +00:00
foreach ( explode ( '[' , $form [ '#name' ]) as $element_name ) {
2007-08-10 10:51:17 +00:00
// chop off the ] that may exist.
if ( substr ( $element_name , - 1 ) == ']' ) {
$element_name = substr ( $element_name , 0 , - 1 );
}
2009-07-06 13:31:47 +00:00
if ( ! isset ( $input [ $element_name ])) {
if ( isset ( $input [ $element_name . '_x' ])) {
2007-08-10 10:51:17 +00:00
return $form [ '#return_value' ];
}
return NULL ;
}
2009-07-06 13:31:47 +00:00
$input = $input [ $element_name ];
2007-08-10 10:51:17 +00:00
}
return $form [ '#return_value' ];
}
}
}
2007-07-04 15:45:37 +00:00
/**
2012-02-19 03:41:24 +00:00
* Determines the value for a checkbox form element .
2007-07-04 15:45:37 +00:00
*
* @ param $form
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2012-02-19 03:41:24 +00:00
* @ param $input
2009-07-06 13:31:47 +00:00
* The incoming input to populate the form element . If this is FALSE ,
2007-07-14 15:23:29 +00:00
* the element ' s default value should be returned .
2012-02-19 03:41:24 +00:00
*
2007-07-04 15:45:37 +00:00
* @ return
2009-07-06 13:31:47 +00:00
* The data that will appear in the $element_state [ 'values' ] collection
2007-07-14 15:23:29 +00:00
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2009-07-06 13:31:47 +00:00
function form_type_checkbox_value ( $element , $input = FALSE ) {
2010-10-28 02:20:14 +00:00
if ( $input === FALSE ) {
// Use #default_value as the default value of a checkbox, except change
// NULL to 0, because _form_builder_handle_input_element() would otherwise
// replace NULL with empty string, but an empty string is a potentially
// valid value for a checked checkbox.
return isset ( $element [ '#default_value' ]) ? $element [ '#default_value' ] : 0 ;
}
else {
// Checked checkboxes are submitted with a value (possibly '0' or ''):
// http://www.w3.org/TR/html401/interact/forms.html#successful-controls.
// For checked checkboxes, browsers submit the string version of
// #return_value, but we return the original #return_value. For unchecked
// checkboxes, browsers submit nothing at all, but
// _form_builder_handle_input_element() detects this, and calls this
// function with $input=NULL. Returning NULL from a value callback means to
// use the default value, which is not what is wanted when an unchecked
// checkbox is submitted, so we use integer 0 as the value indicating an
// unchecked checkbox. Therefore, modules must not use integer 0 as a
// #return_value, as doing so results in the checkbox always being treated
// as unchecked. The string '0' is allowed for #return_value. The most
// common use-case for setting #return_value to either 0 or '0' is for the
// first option within a 0-indexed array of checkboxes, and for this,
// form_process_checkboxes() uses the string rather than the integer.
2010-04-11 19:00:27 +00:00
return isset ( $input ) ? $element [ '#return_value' ] : 0 ;
2007-07-04 15:45:37 +00:00
}
}
/**
2012-02-19 03:41:24 +00:00
* Determines the value for a checkboxes form element .
2007-07-04 15:45:37 +00:00
*
2009-07-06 13:31:47 +00:00
* @ param $element
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2009-07-06 13:31:47 +00:00
* @ param $input
* The incoming input to populate the form element . If this is FALSE ,
2007-07-14 15:23:29 +00:00
* the element ' s default value should be returned .
2012-02-19 03:41:24 +00:00
*
2007-07-04 15:45:37 +00:00
* @ return
2009-07-06 13:31:47 +00:00
* The data that will appear in the $element_state [ 'values' ] collection
2007-07-14 15:23:29 +00:00
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2009-07-06 13:31:47 +00:00
function form_type_checkboxes_value ( $element , $input = FALSE ) {
if ( $input === FALSE ) {
2007-07-04 15:45:37 +00:00
$value = array ();
2009-07-06 13:31:47 +00:00
$element += array ( '#default_value' => array ());
foreach ( $element [ '#default_value' ] as $key ) {
2010-03-13 22:33:05 +00:00
$value [ $key ] = $key ;
2007-07-04 15:45:37 +00:00
}
return $value ;
}
2010-07-26 13:07:59 +00:00
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
2010-10-28 02:20:14 +00:00
// form submissions, since all values in $_POST are strings.
2010-07-26 13:07:59 +00:00
foreach ( $input as $key => $value ) {
2010-09-26 23:31:36 +00:00
if ( ! isset ( $value )) {
2010-07-26 13:07:59 +00:00
unset ( $input [ $key ]);
}
}
return drupal_map_assoc ( $input );
}
2010-06-05 13:18:09 +00:00
else {
2010-07-26 13:07:59 +00:00
return array ();
2007-09-25 11:29:10 +00:00
}
2007-07-04 15:45:37 +00:00
}
2012-12-26 18:15:32 +00:00
/**
* 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 ());
return drupal_map_assoc ( array_keys ( array_filter ( $element [ '#default_value' ])));
}
else {
return is_array ( $input ) ? drupal_map_assoc ( $input ) : array ();
}
}
}
2012-03-10 21:01:23 +00:00
/**
* 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 ) {
2012-07-26 11:56:58 +00:00
// When there's user input (including NULL), return it as the value.
// However, if NULL is submitted, _form_builder_handle_input_element() will
// 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' ])) {
2012-03-10 21:01:23 +00:00
$element [ '#needs_validation' ] = TRUE ;
}
return $input ;
}
2012-07-26 11:56:58 +00:00
else {
// For default value handling, simply return #default_value. Additionally,
// for a NULL default value, set #has_garbage_value to prevent
// _form_builder_handle_input_element() converting the NULL to an empty
// 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 ;
2012-03-10 21:01:23 +00:00
}
}
2010-10-21 20:46:58 +00:00
/**
2012-02-19 03:41:24 +00:00
* Determines the value for a tableselect form element .
2010-10-21 20:46:58 +00:00
*
* @ 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 .
2012-02-19 03:41:24 +00:00
*
2010-10-21 20:46:58 +00:00
* @ 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 {
return is_array ( $input ) ? drupal_map_assoc ( $input ) : array ();
}
}
}
2007-07-04 15:45:37 +00:00
/**
2012-02-19 03:41:24 +00:00
* Determines the value for a password_confirm form element .
2007-07-04 15:45:37 +00:00
*
2009-07-06 13:31:47 +00:00
* @ param $element
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2009-07-06 13:31:47 +00:00
* @ param $input
* The incoming input to populate the form element . If this is FALSE ,
2007-07-14 15:23:29 +00:00
* the element ' s default value should be returned .
2012-02-19 03:41:24 +00:00
*
2007-07-04 15:45:37 +00:00
* @ return
2009-07-06 13:31:47 +00:00
* The data that will appear in the $element_state [ 'values' ] collection
2007-07-14 15:23:29 +00:00
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2009-07-06 13:31:47 +00:00
function form_type_password_confirm_value ( $element , $input = FALSE ) {
if ( $input === FALSE ) {
$element += array ( '#default_value' => array ());
return $element [ '#default_value' ] + array ( 'pass1' => '' , 'pass2' => '' );
2007-07-04 15:45:37 +00:00
}
}
/**
2012-02-19 03:41:24 +00:00
* Determines the value for a select form element .
2007-07-04 15:45:37 +00:00
*
2009-07-06 13:31:47 +00:00
* @ param $element
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2009-07-06 13:31:47 +00:00
* @ param $input
* The incoming input to populate the form element . If this is FALSE ,
2007-07-14 15:23:29 +00:00
* the element ' s default value should be returned .
2012-02-19 03:41:24 +00:00
*
2007-07-04 15:45:37 +00:00
* @ return
2009-07-06 13:31:47 +00:00
* The data that will appear in the $element_state [ 'values' ] collection
2007-07-14 15:23:29 +00:00
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2009-07-06 13:31:47 +00:00
function form_type_select_value ( $element , $input = FALSE ) {
if ( $input !== FALSE ) {
if ( isset ( $element [ '#multiple' ]) && $element [ '#multiple' ]) {
2010-03-12 14:38:37 +00:00
// 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' ])) {
return ( is_array ( $input )) ? drupal_map_assoc ( $input ) : array ();
}
else {
return ( isset ( $element [ '#default_value' ]) && is_array ( $element [ '#default_value' ])) ? $element [ '#default_value' ] : array ();
}
2007-07-04 15:45:37 +00:00
}
2010-10-04 18:00:46 +00:00
// 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' ];
}
2007-07-04 15:45:37 +00:00
else {
2009-07-06 13:31:47 +00:00
return $input ;
2007-07-04 15:45:37 +00:00
}
}
}
/**
2012-02-19 03:41:24 +00:00
* Determines the value for a textfield form element .
2007-07-04 15:45:37 +00:00
*
2009-07-06 13:31:47 +00:00
* @ param $element
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2009-07-06 13:31:47 +00:00
* @ param $input
* The incoming input to populate the form element . If this is FALSE ,
2007-07-14 15:23:29 +00:00
* the element ' s default value should be returned .
2012-02-19 03:41:24 +00:00
*
2007-07-04 15:45:37 +00:00
* @ return
2009-07-06 13:31:47 +00:00
* The data that will appear in the $element_state [ 'values' ] collection
2007-07-14 15:23:29 +00:00
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2009-07-06 13:31:47 +00:00
function form_type_textfield_value ( $element , $input = FALSE ) {
2010-03-12 14:38:37 +00:00
if ( $input !== FALSE && $input !== NULL ) {
2009-07-06 13:31:47 +00:00
// Equate $input to the form value to ensure it's marked for
2007-07-04 15:45:37 +00:00
// validation.
2009-07-06 13:31:47 +00:00
return str_replace ( array ( " \r " , " \n " ), '' , $input );
2007-07-04 15:45:37 +00:00
}
}
/**
2012-02-19 03:41:24 +00:00
* Determines the value for form ' s token value .
2007-07-04 15:45:37 +00:00
*
2009-07-06 13:31:47 +00:00
* @ param $element
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2009-07-06 13:31:47 +00:00
* @ param $input
* The incoming input to populate the form element . If this is FALSE ,
2007-07-14 15:23:29 +00:00
* the element ' s default value should be returned .
2012-02-19 03:41:24 +00:00
*
2007-07-04 15:45:37 +00:00
* @ return
2009-07-06 13:31:47 +00:00
* The data that will appear in the $element_state [ 'values' ] collection
2007-07-14 15:23:29 +00:00
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2009-07-06 13:31:47 +00:00
function form_type_token_value ( $element , $input = FALSE ) {
if ( $input !== FALSE ) {
2010-05-06 05:59:31 +00:00
return ( string ) $input ;
2007-07-04 15:45:37 +00:00
}
}
2006-04-15 21:52:44 +00:00
/**
2012-02-19 03:41:24 +00:00
* Changes submitted form values during form validation .
2006-04-15 21:52:44 +00:00
*
2009-12-05 16:03:51 +00:00
* Use this function to change the submitted value of a form element in a form
* validation function , so that the changed value persists in $form_state
* through to the submission handlers .
2006-04-15 21:52:44 +00:00
*
2009-12-05 16:03:51 +00:00
* Note that form validation functions are specified in the '#validate'
* component of the form array ( the value of $form [ '#validate' ] is an array of
* validation function names ) . If the form does not originate in your module ,
* you can implement hook_form_FORM_ID_alter () to add a validation function
* to $form [ '#validate' ] .
2008-04-14 17:51:38 +00:00
*
2009-07-06 13:31:47 +00:00
* @ param $element
2009-12-05 16:03:51 +00:00
* The form element that should have its value updated ; in most cases you can
* just pass in the element from the $form array , although the only component
* that is actually used is '#parents' . If constructing yourself , set
* $element [ '#parents' ] to be an array giving the path through the form
* array ' s keys to the element whose value you want to update . For instance ,
* if you want to update the value of $form [ 'elem1' ][ 'elem2' ], which should be
* stored in $form_state [ 'values' ][ 'elem1' ][ 'elem2' ], you would set
* $element [ '#parents' ] = array ( 'elem1' , 'elem2' ) .
2006-04-15 21:52:44 +00:00
* @ param $value
2009-12-05 16:03:51 +00:00
* The new value for the form element .
2008-04-14 17:51:38 +00:00
* @ param $form_state
2009-12-05 16:03:51 +00:00
* Form state array where the value change should be recorded .
2006-04-15 21:52:44 +00:00
*/
2009-07-06 13:31:47 +00:00
function form_set_value ( $element , $value , & $form_state ) {
2012-12-17 21:54:13 +00:00
NestedArray :: setValue ( $form_state [ 'values' ], $element [ '#parents' ], $value , TRUE );
2006-04-15 21:52:44 +00:00
}
2010-06-26 18:11:27 +00:00
/**
* 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 .
*/
2009-12-29 20:16:09 +00:00
function form_options_flatten ( $array ) {
// Always reset static var when first entering the recursion.
drupal_static_reset ( '_form_options_flatten' );
return _form_options_flatten ( $array );
}
2006-01-06 07:15:47 +00:00
2010-06-26 18:11:27 +00:00
/**
2012-02-19 03:41:24 +00:00
* Iterates over an array and returns a flat array with duplicate keys removed .
2010-06-26 18:11:27 +00:00
*
2012-02-19 03:41:24 +00:00
* This function also handles cases where objects are passed as array values .
2010-06-26 18:11:27 +00:00
*/
2009-12-29 20:16:09 +00:00
function _form_options_flatten ( $array ) {
$return = & drupal_static ( __FUNCTION__ );
2006-01-06 07:15:47 +00:00
foreach ( $array as $key => $value ) {
2006-10-31 07:37:25 +00:00
if ( is_object ( $value )) {
2009-12-29 20:16:09 +00:00
_form_options_flatten ( $value -> option );
2006-10-31 07:37:25 +00:00
}
2008-10-12 04:30:09 +00:00
elseif ( is_array ( $value )) {
2009-12-29 20:16:09 +00:00
_form_options_flatten ( $value );
2006-01-06 07:15:47 +00:00
}
else {
$return [ $key ] = 1 ;
}
}
return $return ;
}
2010-09-24 21:36:22 +00:00
/**
* 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)
2010-10-04 18:00:46 +00:00
* or not ( FALSE ) . Defaults to FALSE .
2010-09-24 21:36:22 +00:00
* - #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
2010-10-04 18:00:46 +00:00
* 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 .
2010-09-24 21:36:22 +00:00
*
* @ 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 {
2013-03-24 18:54:56 +00:00
// If the element is set to #required through #states, override the
// element's #required setting.
$required = isset ( $element [ '#states' ][ 'required' ]) ? TRUE : $element [ '#required' ];
2010-10-04 18:00:46 +00:00
// 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' ])) {
2010-09-24 21:36:22 +00:00
$element += array (
'#empty_value' => '' ,
2011-01-18 18:23:40 +00:00
'#empty_option' => $required ? t ( '- Select -' ) : t ( '- None -' ),
2010-09-24 21:36:22 +00:00
);
// 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 ;
}
2005-10-07 06:11:12 +00:00
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a select form element .
*
* 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 .
2005-10-07 06:11:12 +00:00
*
2009-10-09 01:00:08 +00:00
* @ 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.
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_select ( $variables ) {
$element = $variables [ 'element' ];
2010-09-16 20:14:49 +00:00
element_set_attributes ( $element , array ( 'id' , 'name' , 'size' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-select' ));
2010-09-16 20:14:49 +00:00
2012-09-04 13:32:47 +00:00
return '<select' . new Attribute ( $element [ '#attributes' ]) . '>' . form_select_options ( $element ) . '</select>' ;
2006-01-19 09:22:16 +00:00
}
2009-08-19 08:15:36 +00:00
/**
2012-02-19 03:41:24 +00:00
* Converts a select form element ' s options array into HTML .
2009-08-19 08:15:36 +00:00
*
* @ 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 .
2012-02-19 03:41:24 +00:00
*
2009-08-19 08:15:36 +00:00
* @ return
* An HTML string of options for the select form element .
*/
2006-01-19 09:22:16 +00:00
function form_select_options ( $element , $choices = NULL ) {
if ( ! isset ( $choices )) {
2013-02-06 12:03:07 +00:00
if ( empty ( $element [ '#options' ])) {
return '' ;
}
2006-01-19 09:22:16 +00:00
$choices = $element [ '#options' ];
}
2006-04-11 11:33:15 +00:00
// array_key_exists() accommodates the rare event where $element['#value'] is NULL.
2006-01-13 07:44:59 +00:00
// isset() fails in this situation.
$value_valid = isset ( $element [ '#value' ]) || array_key_exists ( '#value' , $element );
2010-09-16 20:14:49 +00:00
$value_is_array = $value_valid && is_array ( $element [ '#value' ]);
2006-01-19 09:22:16 +00:00
$options = '' ;
foreach ( $choices as $key => $choice ) {
2005-10-07 06:11:12 +00:00
if ( is_array ( $choice )) {
2008-04-14 17:48:46 +00:00
$options .= '<optgroup label="' . $key . '">' ;
2006-01-19 09:22:16 +00:00
$options .= form_select_options ( $element , $choice );
$options .= '</optgroup>' ;
2005-10-07 06:11:12 +00:00
}
2006-10-20 20:55:03 +00:00
elseif ( is_object ( $choice )) {
$options .= form_select_options ( $element , $choice -> option );
}
2005-10-07 06:11:12 +00:00
else {
2010-05-06 05:59:31 +00:00
$key = ( string ) $key ;
if ( $value_valid && ( ! $value_is_array && ( string ) $element [ '#value' ] === $key || ( $value_is_array && in_array ( $key , $element [ '#value' ])))) {
2006-01-06 07:15:47 +00:00
$selected = ' selected="selected"' ;
}
else {
$selected = '' ;
}
2008-04-14 17:48:46 +00:00
$options .= '<option value="' . check_plain ( $key ) . '"' . $selected . '>' . check_plain ( $choice ) . '</option>' ;
2005-10-07 06:11:12 +00:00
}
}
2006-01-19 09:22:16 +00:00
return $options ;
2005-10-07 06:11:12 +00:00
}
2006-12-29 00:19:58 +00:00
/**
2012-02-19 03:41:24 +00:00
* Returns the indexes of a select element ' s options matching a given key .
2007-01-05 19:08:30 +00:00
*
* This function is useful if you need to modify the options that are
2007-01-23 19:17:55 +00:00
* already in a form element ; for example , to remove choices which are
2007-01-05 19:08:30 +00:00
* 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 .
2006-12-29 00:19:58 +00:00
*
* @ param $element
2007-01-05 19:08:30 +00:00
* The select element to search .
2006-12-29 00:19:58 +00:00
* @ param $key
* The key to look for .
2012-02-19 03:41:24 +00:00
*
2006-12-29 00:19:58 +00:00
* @ return
2007-01-05 19:08:30 +00:00
* An array of indexes that match the given $key . Array will be
* empty if no elements were found . FALSE if optgroups were found .
2006-12-29 00:19:58 +00:00
*/
2007-01-05 19:08:30 +00:00
function form_get_options ( $element , $key ) {
$keys = array ();
foreach ( $element [ '#options' ] as $index => $choice ) {
if ( is_array ( $choice )) {
return FALSE ;
}
2008-10-12 04:30:09 +00:00
elseif ( is_object ( $choice )) {
2007-01-05 19:08:30 +00:00
if ( isset ( $choice -> option [ $key ])) {
$keys [] = $index ;
}
}
2008-10-12 04:30:09 +00:00
elseif ( $index == $key ) {
2007-01-05 19:08:30 +00:00
$keys [] = $index ;
2006-12-29 00:19:58 +00:00
}
}
2007-01-05 19:08:30 +00:00
return $keys ;
2006-12-29 00:19:58 +00:00
}
2005-10-07 06:11:12 +00:00
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a fieldset form element and its children .
2005-10-07 06:11:12 +00:00
*
2009-10-09 01:00:08 +00:00
* @ param $variables
* An associative array containing :
* - element : An associative array containing the properties of the element .
2013-05-29 21:08:39 +00:00
* Properties used : #attributes, #children, #collapsed, #description, #id,
* #title, #value.
2009-10-09 01:00:08 +00:00
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_fieldset ( $variables ) {
$element = $variables [ 'element' ];
2010-09-16 20:14:49 +00:00
element_set_attributes ( $element , array ( 'id' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-wrapper' ));
2009-11-16 05:11:01 +00:00
2012-11-29 07:16:36 +00:00
if ( ! empty ( $element [ '#description' ])) {
$description_id = $element [ '#attributes' ][ 'id' ] . '--description' ;
$element [ '#attributes' ][ 'aria-describedby' ] = $description_id ;
}
2013-01-17 20:43:29 +00:00
$legend_attributes = array ();
if ( isset ( $element [ '#title_display' ]) && $element [ '#title_display' ] == 'invisible' ) {
2013-06-17 19:58:27 +00:00
$legend_attributes [ 'class' ][] = 'visually-hidden' ;
2013-01-17 20:43:29 +00:00
}
2012-09-04 13:32:47 +00:00
$output = '<fieldset' . new Attribute ( $element [ '#attributes' ]) . '>' ;
2009-11-16 05:11:01 +00:00
if ( ! empty ( $element [ '#title' ])) {
2010-03-03 19:46:26 +00:00
// Always wrap fieldset legends in a SPAN for CSS positioning.
2013-01-17 20:43:29 +00:00
$output .= '<legend' . new Attribute ( $legend_attributes ) . '><span class="fieldset-legend">' . $element [ '#title' ] . '</span></legend>' ;
2009-11-16 05:11:01 +00:00
}
2010-03-03 19:46:26 +00:00
$output .= '<div class="fieldset-wrapper">' ;
2009-11-16 05:11:01 +00:00
if ( ! empty ( $element [ '#description' ])) {
2012-11-29 07:16:36 +00:00
$attributes = array ( 'class' => 'fieldset-description' , 'id' => $description_id );
$output .= '<div' . new Attribute ( $attributes ) . '>' . $element [ '#description' ] . '</div>' ;
2009-11-16 05:11:01 +00:00
}
$output .= $element [ '#children' ];
if ( isset ( $element [ '#value' ])) {
$output .= $element [ '#value' ];
}
2010-03-03 19:46:26 +00:00
$output .= '</div>' ;
2009-11-16 05:11:01 +00:00
$output .= " </fieldset> \n " ;
return $output ;
2005-10-07 06:11:12 +00:00
}
2012-11-27 07:06:47 +00:00
/**
* Returns HTML for a details form element and its children .
*
* @ param $variables
* An associative array containing :
* - element : An associative array containing the properties of the element .
2013-05-29 21:08:39 +00:00
* Properties used : #attributes, #children, #collapsed, #description, #id,
* #title, #value.
2012-11-27 07:06:47 +00:00
*
* @ ingroup themeable
*/
function theme_details ( $variables ) {
$element = $variables [ 'element' ];
element_set_attributes ( $element , array ( 'id' ));
_form_set_attributes ( $element , array ( 'form-wrapper' ));
$output = '<details' . new Attribute ( $element [ '#attributes' ]) . '>' ;
if ( ! empty ( $element [ '#title' ])) {
2013-01-03 01:58:48 +00:00
$summary_attributes = new Attribute ( array (
'role' => 'button' ,
));
if ( ! empty ( $element [ '#attributes' ][ 'id' ])) {
$summary_attributes [ 'aria-controls' ] = $element [ '#attributes' ][ 'id' ];
}
2013-05-29 21:08:39 +00:00
$summary_attributes [ 'aria-expanded' ] = empty ( $element [ '#attributes' ][ 'open' ]) ? FALSE : TRUE ;
2013-01-03 01:58:48 +00:00
$summary_attributes [ 'aria-pressed' ] = $summary_attributes [ 'aria-expanded' ];
$output .= '<summary' . $summary_attributes . '>' . $element [ '#title' ] . '</summary>' ;
2012-11-27 07:06:47 +00:00
}
$output .= '<div class="details-wrapper">' ;
if ( ! empty ( $element [ '#description' ])) {
$output .= '<div class="details-description">' . $element [ '#description' ] . '</div>' ;
}
$output .= $element [ '#children' ];
if ( isset ( $element [ '#value' ])) {
$output .= $element [ '#value' ];
}
$output .= '</div>' ;
$output .= " </details> \n " ;
return $output ;
}
2005-10-07 06:11:12 +00:00
/**
2013-01-10 05:45:55 +00:00
* 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.
2005-10-07 06:11:12 +00:00
*
2010-12-30 22:52:24 +00:00
* Note : The input " name " attribute needs to be sanitized before output , which
2012-09-04 13:32:47 +00:00
* is currently done by initializing Drupal\Core\Template\Attribute with
* all the attributes .
2010-12-30 22:52:24 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2005-10-07 06:11:12 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_radio ( $element ) {
2010-07-31 04:01:51 +00:00
$element [ '#attributes' ][ 'type' ] = 'radio' ;
2010-09-16 20:14:49 +00:00
element_set_attributes ( $element , array ( 'id' , 'name' , '#return_value' => 'value' ));
2010-12-30 22:52:24 +00:00
if ( isset ( $element [ '#return_value' ]) && $element [ '#value' ] !== FALSE && $element [ '#value' ] == $element [ '#return_value' ]) {
2010-07-31 04:01:51 +00:00
$element [ '#attributes' ][ 'checked' ] = 'checked' ;
}
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-radio' ));
2009-11-16 05:11:01 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2005-10-07 06:11:12 +00:00
}
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a set of radio button form elements .
2005-10-07 06:11:12 +00:00
*
2009-10-09 01:00:08 +00:00
* @ param $variables
* An associative array containing :
* - element : An associative array containing the properties of the element .
* Properties used : #title, #value, #options, #description, #required,
* #attributes, #children.
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_radios ( $variables ) {
$element = $variables [ 'element' ];
2010-03-26 12:16:18 +00:00
$attributes = array ();
2010-09-16 20:14:49 +00:00
if ( isset ( $element [ '#id' ])) {
2010-03-26 12:16:18 +00:00
$attributes [ 'id' ] = $element [ '#id' ];
}
$attributes [ 'class' ] = 'form-radios' ;
2009-08-22 14:34:23 +00:00
if ( ! empty ( $element [ '#attributes' ][ 'class' ])) {
2010-03-26 12:16:18 +00:00
$attributes [ 'class' ] .= ' ' . implode ( ' ' , $element [ '#attributes' ][ 'class' ]);
2006-12-12 10:01:38 +00:00
}
2012-04-12 07:29:31 +00:00
if ( isset ( $element [ '#attributes' ][ 'title' ])) {
$attributes [ 'title' ] = $element [ '#attributes' ][ 'title' ];
}
2012-09-04 13:32:47 +00:00
return '<div' . new Attribute ( $attributes ) . '>' . ( ! empty ( $element [ '#children' ]) ? $element [ '#children' ] : '' ) . '</div>' ;
2006-01-02 08:35:59 +00:00
}
2007-12-31 08:54:37 +00:00
/**
2006-04-20 16:35:29 +00:00
* Expand a password_confirm field into two text boxes .
*/
2008-07-18 07:06:24 +00:00
function form_process_password_confirm ( $element ) {
2006-08-03 14:08:30 +00:00
$element [ 'pass1' ] = array (
'#type' => 'password' ,
'#title' => t ( 'Password' ),
2007-05-14 13:43:38 +00:00
'#value' => empty ( $element [ '#value' ]) ? NULL : $element [ '#value' ][ 'pass1' ],
2007-05-31 12:36:18 +00:00
'#required' => $element [ '#required' ],
2009-08-22 14:34:23 +00:00
'#attributes' => array ( 'class' => array ( 'password-field' )),
2006-08-03 14:08:30 +00:00
);
$element [ 'pass2' ] = array (
'#type' => 'password' ,
'#title' => t ( 'Confirm password' ),
2007-05-14 13:43:38 +00:00
'#value' => empty ( $element [ '#value' ]) ? NULL : $element [ '#value' ][ 'pass2' ],
2007-05-31 12:36:18 +00:00
'#required' => $element [ '#required' ],
2009-08-22 14:34:23 +00:00
'#attributes' => array ( 'class' => array ( 'password-confirm' )),
2006-08-03 14:08:30 +00:00
);
2007-05-14 13:43:38 +00:00
$element [ '#element_validate' ] = array ( 'password_confirm_validate' );
2006-04-20 16:35:29 +00:00
$element [ '#tree' ] = TRUE ;
2007-01-11 03:24:42 +00:00
if ( isset ( $element [ '#size' ])) {
$element [ 'pass1' ][ '#size' ] = $element [ 'pass2' ][ '#size' ] = $element [ '#size' ];
}
2006-04-20 16:35:29 +00:00
return $element ;
}
2006-01-02 08:35:59 +00:00
/**
2012-02-19 03:41:24 +00:00
* Validates a password_confirm element .
2006-01-02 08:35:59 +00:00
*/
2009-07-06 13:31:47 +00:00
function password_confirm_validate ( $element , & $element_state ) {
$pass1 = trim ( $element [ 'pass1' ][ '#value' ]);
2010-05-07 14:48:10 +00:00
$pass2 = trim ( $element [ 'pass2' ][ '#value' ]);
if ( ! empty ( $pass1 ) || ! empty ( $pass2 )) {
2009-05-22 07:39:09 +00:00
if ( strcmp ( $pass1 , $pass2 )) {
2009-07-06 13:31:47 +00:00
form_error ( $element , t ( 'The specified passwords do not match.' ));
2006-01-02 08:35:59 +00:00
}
2006-01-24 10:15:03 +00:00
}
2009-07-06 13:31:47 +00:00
elseif ( $element [ '#required' ] && ! empty ( $element_state [ 'input' ])) {
form_error ( $element , t ( 'Password field is required.' ));
2006-01-02 08:35:59 +00:00
}
2006-04-20 16:35:29 +00:00
2006-04-25 20:44:00 +00:00
// Password field must be converted from a two-element array into a single
// string regardless of validation results.
2009-07-06 13:31:47 +00:00
form_set_value ( $element [ 'pass1' ], NULL , $element_state );
form_set_value ( $element [ 'pass2' ], NULL , $element_state );
form_set_value ( $element , $pass1 , $element_state );
2006-04-25 20:44:00 +00:00
2009-07-06 13:31:47 +00:00
return $element ;
2007-05-28 06:08:47 +00:00
2006-01-02 08:35:59 +00:00
}
2005-10-07 06:11:12 +00:00
/**
2013-02-16 23:29:53 +00:00
* Returns HTML for an #date form element.
2005-10-07 06:11:12 +00:00
*
2013-02-16 23:29:53 +00:00
* 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 $variables
2009-10-09 01:00:08 +00:00
* An associative array containing :
* - element : An associative array containing the properties of the element .
* Properties used : #title, #value, #options, #description, #required,
2013-02-16 23:29:53 +00:00
* #attributes, #id, #name, #type, #min, #max, #step, #value, #size.
2009-10-09 01:00:08 +00:00
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_date ( $variables ) {
$element = $variables [ 'element' ];
2013-02-16 23:29:53 +00:00
if ( empty ( $element [ 'attribute' ][ 'type' ])) {
$element [ 'attribute' ][ 'type' ] = 'date' ;
2011-09-05 19:08:11 +00:00
}
2013-02-16 23:29:53 +00:00
element_set_attributes ( $element , array ( 'id' , 'name' , 'type' , 'min' , 'max' , 'step' , 'value' , 'size' ));
_form_set_attributes ( $element , array ( 'form-' . $element [ 'attribute' ][ 'type' ]));
2011-09-05 19:08:11 +00:00
2013-02-16 23:29:53 +00:00
return '<input' . new Attribute ( $element [ '#attributes' ]) . ' />' ;
2005-11-21 09:42:14 +00:00
}
2005-10-07 06:11:12 +00:00
2006-01-26 13:39:48 +00:00
/**
2012-02-19 03:41:24 +00:00
* Sets the value for a weight element , with zero as a default .
2006-01-26 13:39:48 +00:00
*/
function weight_value ( & $form ) {
if ( isset ( $form [ '#default_value' ])) {
$form [ '#value' ] = $form [ '#default_value' ];
}
else {
$form [ '#value' ] = 0 ;
}
}
2005-10-07 06:11:12 +00:00
/**
2012-02-19 03:41:24 +00:00
* Expands a radios element into individual radio elements .
2005-10-07 06:11:12 +00:00
*/
2008-07-18 07:06:24 +00:00
function form_process_radios ( $element ) {
2005-10-11 19:44:35 +00:00
if ( count ( $element [ '#options' ]) > 0 ) {
2010-11-07 21:46:09 +00:00
$weight = 0 ;
2005-10-11 19:44:35 +00:00
foreach ( $element [ '#options' ] as $key => $choice ) {
2010-11-07 21:46:09 +00:00
// Maintain order of options as defined in #options, in case the element
// defines custom option sub-elements, but does not define all option
// sub-elements.
$weight += 0.001 ;
$element += array ( $key => array ());
// Generate the parents as the autogenerator does, so we will have a
// unique id for each radio button.
$parents_for_id = array_merge ( $element [ '#parents' ], array ( $key ));
$element [ $key ] += array (
'#type' => 'radio' ,
'#title' => $choice ,
2012-09-04 13:32:47 +00:00
// The key is sanitized in Drupal\Core\Template\Attribute during output
// from the theme function.
2010-12-30 22:52:24 +00:00
'#return_value' => $key ,
2012-03-10 21:01:23 +00:00
// Use default or FALSE. A value of FALSE means that the radio button is
// not 'checked'.
'#default_value' => isset ( $element [ '#default_value' ]) ? $element [ '#default_value' ] : FALSE ,
2010-11-07 21:46:09 +00:00
'#attributes' => $element [ '#attributes' ],
'#parents' => $element [ '#parents' ],
'#id' => drupal_html_id ( 'edit-' . implode ( '-' , $parents_for_id )),
'#ajax' => isset ( $element [ '#ajax' ]) ? $element [ '#ajax' ] : NULL ,
'#weight' => $weight ,
);
2005-10-07 06:11:12 +00:00
}
}
return $element ;
}
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'checkbox' render element for theme_input().
2005-10-07 06:11:12 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #title, #value, #return_value, #description, #required,
* #attributes, #checked.
2009-10-09 01:00:08 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2005-10-07 06:11:12 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_checkbox ( $element ) {
2010-07-31 04:01:51 +00:00
$element [ '#attributes' ][ 'type' ] = 'checkbox' ;
2010-09-16 20:14:49 +00:00
element_set_attributes ( $element , array ( 'id' , 'name' , '#return_value' => 'value' ));
2010-04-11 19:00:27 +00:00
// Unchecked checkbox has #value of integer 0.
2010-10-28 02:20:14 +00:00
if ( ! empty ( $element [ '#checked' ])) {
2010-07-31 04:01:51 +00:00
$element [ '#attributes' ][ 'checked' ] = 'checked' ;
2009-11-15 21:36:06 +00:00
}
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-checkbox' ));
2005-10-11 19:44:35 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2005-10-07 06:11:12 +00:00
}
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a set of checkbox form elements .
2005-10-07 06:11:12 +00:00
*
2009-10-09 01:00:08 +00:00
* @ param $variables
* An associative array containing :
* - element : An associative array containing the properties of the element .
* Properties used : #children, #attributes.
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_checkboxes ( $variables ) {
$element = $variables [ 'element' ];
2010-03-26 12:16:18 +00:00
$attributes = array ();
2010-09-16 20:14:49 +00:00
if ( isset ( $element [ '#id' ])) {
2010-03-26 12:16:18 +00:00
$attributes [ 'id' ] = $element [ '#id' ];
}
2010-09-16 20:14:49 +00:00
$attributes [ 'class' ][] = 'form-checkboxes' ;
2009-08-22 14:34:23 +00:00
if ( ! empty ( $element [ '#attributes' ][ 'class' ])) {
2010-09-16 20:14:49 +00:00
$attributes [ 'class' ] = array_merge ( $attributes [ 'class' ], $element [ '#attributes' ][ 'class' ]);
2006-12-12 10:01:38 +00:00
}
2012-04-12 07:29:31 +00:00
if ( isset ( $element [ '#attributes' ][ 'title' ])) {
$attributes [ 'title' ] = $element [ '#attributes' ][ 'title' ];
}
2012-09-04 13:32:47 +00:00
return '<div' . new Attribute ( $attributes ) . '>' . ( ! empty ( $element [ '#children' ]) ? $element [ '#children' ] : '' ) . '</div>' ;
2009-02-03 18:55:32 +00:00
}
/**
2012-02-19 03:41:24 +00:00
* Adds form element theming to an element if its title or description is set .
2009-02-03 18:55:32 +00:00
*
* This is used as a pre render function for checkboxes and radios .
*/
function form_pre_render_conditional_form_element ( $element ) {
2009-12-02 15:09:16 +00:00
// Set the element's title attribute to show #title as a tooltip, if needed.
if ( isset ( $element [ '#title' ]) && $element [ '#title_display' ] == 'attribute' ) {
$element [ '#attributes' ][ 'title' ] = $element [ '#title' ];
if ( ! empty ( $element [ '#required' ])) {
// Append an indication that this field is required.
2013-06-17 13:35:07 +00:00
$element [ '#attributes' ][ 'title' ] .= ' (' . t ( 'Required' ) . ')' ;
2009-12-02 15:09:16 +00:00
}
}
2009-11-16 05:11:01 +00:00
if ( isset ( $element [ '#title' ]) || isset ( $element [ '#description' ])) {
2009-08-04 06:38:57 +00:00
$element [ '#theme_wrappers' ][] = 'form_element' ;
2005-10-07 06:11:12 +00:00
}
2009-02-03 18:55:32 +00:00
return $element ;
2005-10-07 06:11:12 +00:00
}
2012-03-27 06:23:35 +00:00
/**
* Processes a form button element .
*/
function form_process_button ( $element , $form_state ) {
// If this is a button intentionally allowing incomplete form submission
// (e.g., a "Previous" or "Add another item" button), then also skip
// client-side validation.
if ( isset ( $element [ '#limit_validation_errors' ]) && $element [ '#limit_validation_errors' ] !== FALSE ) {
$element [ '#attributes' ][ 'formnovalidate' ] = 'formnovalidate' ;
}
return $element ;
}
2010-10-28 02:20:14 +00:00
/**
* Sets the #checked property of a checkbox element.
*/
function form_process_checkbox ( $element , $form_state ) {
$value = $element [ '#value' ];
$return_value = $element [ '#return_value' ];
// On form submission, the #value of an available and enabled checked
// checkbox is #return_value, and the #value of an available and enabled
// unchecked checkbox is integer 0. On not submitted forms, and for
// checkboxes with #access=FALSE or #disabled=TRUE, the #value is
// #default_value (integer 0 if #default_value is NULL). Most of the time,
// a string comparison of #value and #return_value is sufficient for
// determining the "checked" state, but a value of TRUE always means checked
// (even if #return_value is 'foo'), and a value of FALSE or integer 0 always
// means unchecked (even if #return_value is '' or '0').
if ( $value === TRUE || $value === FALSE || $value === 0 ) {
$element [ '#checked' ] = ( bool ) $value ;
}
else {
// Compare as strings, so that 15 is not considered equal to '15foo', but 1
// is considered equal to '1'. This cast does not imply that either #value
// or #return_value is expected to be a string.
$element [ '#checked' ] = (( string ) $value === ( string ) $return_value );
}
return $element ;
}
2012-02-19 03:41:24 +00:00
/**
* Processes a checkboxes form element .
*/
2008-07-18 07:06:24 +00:00
function form_process_checkboxes ( $element ) {
2005-10-11 19:44:35 +00:00
$value = is_array ( $element [ '#value' ]) ? $element [ '#value' ] : array ();
2005-10-26 01:24:09 +00:00
$element [ '#tree' ] = TRUE ;
2005-10-11 19:44:35 +00:00
if ( count ( $element [ '#options' ]) > 0 ) {
if ( ! isset ( $element [ '#default_value' ]) || $element [ '#default_value' ] == 0 ) {
$element [ '#default_value' ] = array ();
2005-10-07 06:11:12 +00:00
}
2010-11-07 21:46:09 +00:00
$weight = 0 ;
2005-10-11 19:44:35 +00:00
foreach ( $element [ '#options' ] as $key => $choice ) {
2010-11-07 21:46:09 +00:00
// Integer 0 is not a valid #return_value, so use '0' instead.
// @see form_type_checkbox_value().
// @todo For Drupal 8, cast all integer keys to strings for consistency
// with form_process_radios().
if ( $key === 0 ) {
$key = '0' ;
2005-10-07 06:11:12 +00:00
}
2010-11-07 21:46:09 +00:00
// Maintain order of options as defined in #options, in case the element
// defines custom option sub-elements, but does not define all option
// sub-elements.
$weight += 0.001 ;
$element += array ( $key => array ());
$element [ $key ] += array (
'#type' => 'checkbox' ,
'#title' => $choice ,
'#return_value' => $key ,
'#default_value' => isset ( $value [ $key ]) ? $key : NULL ,
'#attributes' => $element [ '#attributes' ],
'#ajax' => isset ( $element [ '#ajax' ]) ? $element [ '#ajax' ] : NULL ,
'#weight' => $weight ,
);
2005-10-07 06:11:12 +00:00
}
}
return $element ;
}
2010-04-24 14:49:14 +00:00
/**
* Processes a form actions container element .
*
* @ param $element
* An associative array containing the properties and children of the
* form actions container .
* @ param $form_state
* The $form_state array for the form this element belongs to .
*
* @ return
* The processed element .
*/
function form_process_actions ( $element , & $form_state ) {
$element [ '#attributes' ][ 'class' ][] = 'form-actions' ;
return $element ;
}
2013-01-30 03:30:40 +00:00
/**
* #pre_render callback for #type 'actions'.
*
* This callback iterates over all child elements of the #type 'actions'
* container to look for elements with a #dropbutton property, so as to group
* those elements into dropbuttons . As such , it works similar to #group, but is
* specialized for dropbuttons .
*
* The value of #dropbutton denotes the dropbutton to group the child element
* into . For example , two different values of 'foo' and 'bar' on child elements
* would generate two separate dropbuttons , which each contain the corresponding
* buttons .
*
* @ param array $element
* The #type 'actions' element to process.
*
* @ return array
* The processed #type 'actions' element, including individual buttons grouped
* into new #type 'dropbutton' elements.
*/
function form_pre_render_actions_dropbutton ( array $element ) {
$dropbuttons = array ();
foreach ( element_children ( $element , TRUE ) as $key ) {
if ( isset ( $element [ $key ][ '#dropbutton' ])) {
$dropbutton = $element [ $key ][ '#dropbutton' ];
// If there is no dropbutton for this button group yet, create one.
if ( ! isset ( $dropbuttons [ $dropbutton ])) {
$dropbuttons [ $dropbutton ] = array (
'#type' => 'dropbutton' ,
);
}
// Add this button to the corresponding dropbutton.
// @todo Change #type 'dropbutton' to be based on theme_item_list()
// instead of theme_links() to avoid this preemptive rendering.
$button = drupal_render ( $element [ $key ]);
$dropbuttons [ $dropbutton ][ '#links' ][ $key ] = array (
'title' => $button ,
'html' => TRUE ,
);
}
}
// @todo For now, all dropbuttons appear first. Consider to invent a more
// fancy sorting/injection algorithm here.
return $dropbuttons + $element ;
}
2012-05-08 03:00:06 +00:00
/**
* #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 ()
*/
function form_process_pattern ( $element , & $form_state ) {
if ( isset ( $element [ '#pattern' ]) && ! isset ( $element [ '#attributes' ][ 'pattern' ])) {
$element [ '#attributes' ][ 'pattern' ] = $element [ '#pattern' ];
$element [ '#element_validate' ][] = 'form_validate_pattern' ;
}
return $element ;
}
/**
* #element_validate callback for #pattern form element property.
*
* @ param $element
* An associative array containing the properties and children of the
* generic form element .
* @ param $form_state
* The $form_state array for the form this element belongs to .
*
* @ see form_process_pattern ()
*/
function form_validate_pattern ( $element , & $form_state ) {
if ( $element [ '#value' ] !== '' ) {
// The pattern must match the entire string and should have the same
// behavior as the RegExp object in ECMA 262.
// - Use bracket-style delimiters to avoid introducing a special delimiter
// character like '/' that would have to be escaped.
// - Put in brackets so that the pattern can't interfere with what's
// prepended and appended.
$pattern = '{^(?:' . $element [ '#pattern' ] . ')$}' ;
if ( ! preg_match ( $pattern , $element [ '#value' ])) {
form_error ( $element , t ( '%name field is not in the right format.' , array ( '%name' => $element [ '#title' ])));
}
}
}
2009-10-16 19:20:34 +00:00
/**
* Processes a container element .
*
* @ param $element
* An associative array containing the properties and children of the
* container .
* @ param $form_state
* The $form_state array for the form this element belongs to .
2012-02-19 03:41:24 +00:00
*
2009-10-16 19:20:34 +00:00
* @ return
* The processed element .
*/
function form_process_container ( $element , & $form_state ) {
2010-01-03 21:01:04 +00:00
// Generate the ID of the element if it's not explicitly given.
if ( ! isset ( $element [ '#id' ])) {
$element [ '#id' ] = drupal_html_id ( implode ( '-' , $element [ '#parents' ]) . '-wrapper' );
}
2009-10-16 19:20:34 +00:00
return $element ;
}
2009-01-28 07:43:26 +00:00
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a table with radio buttons or checkboxes .
2009-01-28 07:43:26 +00:00
*
2010-04-13 15:23:03 +00:00
* @ param $variables
* An associative array containing :
* - element : An associative array containing the properties and children of
* the tableselect element . Properties used : #header, #options, #empty,
* and #js_select. The #options property is an array of selection options;
* each array element of #options is an array of properties. These
* properties can include #attributes, which is added to the
2012-08-29 18:02:40 +00:00
* table row ' s HTML attributes ; see theme_table () . An example of per - row
* options :
* @ code
* $options = array (
* array (
* 'title' => 'How to Learn Drupal' ,
* 'content_type' => 'Article' ,
* 'status' => 'published' ,
* '#attributes' => array ( 'class' => array ( 'article-row' )),
* ),
* array (
* 'title' => 'Privacy Policy' ,
* 'content_type' => 'Page' ,
* 'status' => 'published' ,
* '#attributes' => array ( 'class' => array ( 'page-row' )),
* ),
* );
* $header = array (
* 'title' => t ( 'Title' ),
* 'content_type' => t ( 'Content type' ),
* 'status' => t ( 'Status' ),
* );
* $form [ 'table' ] = array (
* '#type' => 'tableselect' ,
* '#header' => $header ,
* '#options' => $options ,
* '#empty' => t ( 'No content available.' ),
* );
* @ endcode
2010-01-30 07:54:01 +00:00
*
2009-01-28 07:43:26 +00:00
* @ ingroup themeable
*/
2009-10-09 01:00:08 +00:00
function theme_tableselect ( $variables ) {
$element = $variables [ 'element' ];
2009-01-28 07:43:26 +00:00
$rows = array ();
2009-12-02 14:56:32 +00:00
$header = $element [ '#header' ];
2009-01-28 07:43:26 +00:00
if ( ! empty ( $element [ '#options' ])) {
// Generate a table row for each selectable item in #options.
2010-10-07 17:31:39 +00:00
foreach ( element_children ( $element ) as $key ) {
2009-01-28 07:43:26 +00:00
$row = array ();
2009-07-07 22:32:17 +00:00
$row [ 'data' ] = array ();
2010-10-07 17:31:39 +00:00
if ( isset ( $element [ '#options' ][ $key ][ '#attributes' ])) {
$row += $element [ '#options' ][ $key ][ '#attributes' ];
2009-07-07 22:32:17 +00:00
}
2009-01-28 07:43:26 +00:00
// Render the checkbox / radio element.
2009-07-07 22:32:17 +00:00
$row [ 'data' ][] = drupal_render ( $element [ $key ]);
2009-01-28 07:43:26 +00:00
// As theme_table only maps header and row columns by order, create the
// correct order by iterating over the header fields.
foreach ( $element [ '#header' ] as $fieldname => $title ) {
2012-04-06 05:38:32 +00:00
// A row cell can span over multiple headers, which means less row cells
// than headers could be present.
if ( isset ( $element [ '#options' ][ $key ][ $fieldname ])) {
// A header can span over multiple cells and in this case the cells
// are passed in an array. The order of this array determines the
// order in which they are added.
if ( ! isset ( $element [ '#options' ][ $key ][ $fieldname ][ 'data' ]) && is_array ( $element [ '#options' ][ $key ][ $fieldname ])) {
foreach ( $element [ '#options' ][ $key ][ $fieldname ] as $cell ) {
$row [ 'data' ][] = $cell ;
}
}
else {
$row [ 'data' ][] = $element [ '#options' ][ $key ][ $fieldname ];
}
}
2009-01-28 07:43:26 +00:00
}
$rows [] = $row ;
}
// Add an empty header or a "Select all" checkbox to provide room for the
// checkboxes/radios in the first table column.
2010-05-19 19:22:24 +00:00
if ( $element [ '#js_select' ]) {
2010-10-03 01:04:51 +00:00
// Add a "Select all" checkbox.
2012-08-30 19:24:38 +00:00
drupal_add_library ( 'system' , 'drupal.tableselect' );
2010-05-19 19:22:24 +00:00
array_unshift ( $header , array ( 'class' => array ( 'select-all' )));
}
2010-08-30 17:07:49 +00:00
else {
2010-10-03 01:04:51 +00:00
// Add an empty header when radio buttons are displayed or a "Select all"
// checkbox is not desired.
2010-08-30 17:07:49 +00:00
array_unshift ( $header , '' );
}
2009-01-28 07:43:26 +00:00
}
2009-12-13 14:18:31 +00:00
return theme ( 'table' , array ( 'header' => $header , 'rows' => $rows , 'empty' => $element [ '#empty' ], 'attributes' => $element [ '#attributes' ]));
2009-01-28 07:43:26 +00:00
}
/**
2012-02-19 03:41:24 +00:00
* Creates checkbox or radio elements to populate a tableselect table .
2009-01-28 07:43:26 +00:00
*
* @ param $element
* An associative array containing the properties and children of the
* tableselect element .
2012-02-19 03:41:24 +00:00
*
2009-01-28 07:43:26 +00:00
* @ return
* The processed element .
*/
function form_process_tableselect ( $element ) {
if ( $element [ '#multiple' ]) {
$value = is_array ( $element [ '#value' ]) ? $element [ '#value' ] : array ();
}
else {
2012-04-20 19:51:35 +00:00
// Advanced selection behavior makes no sense for radios.
2009-01-28 07:43:26 +00:00
$element [ '#js_select' ] = FALSE ;
}
$element [ '#tree' ] = TRUE ;
if ( count ( $element [ '#options' ]) > 0 ) {
if ( ! isset ( $element [ '#default_value' ]) || $element [ '#default_value' ] === 0 ) {
$element [ '#default_value' ] = array ();
}
// Create a checkbox or radio for each item in #options in such a way that
// the value of the tableselect element behaves as if it had been of type
// checkboxes or radios.
foreach ( $element [ '#options' ] as $key => $choice ) {
// Do not overwrite manually created children.
if ( ! isset ( $element [ $key ])) {
if ( $element [ '#multiple' ]) {
2010-10-20 01:31:07 +00:00
$title = '' ;
if ( ! empty ( $element [ '#options' ][ $key ][ 'title' ][ 'data' ][ '#title' ])) {
$title = t ( 'Update @title' , array (
'@title' => $element [ '#options' ][ $key ][ 'title' ][ 'data' ][ '#title' ],
));
}
2009-01-28 07:43:26 +00:00
$element [ $key ] = array (
'#type' => 'checkbox' ,
2010-10-20 01:31:07 +00:00
'#title' => $title ,
'#title_display' => 'invisible' ,
2009-01-28 07:43:26 +00:00
'#return_value' => $key ,
2009-11-15 21:36:06 +00:00
'#default_value' => isset ( $value [ $key ]) ? $key : NULL ,
2009-01-28 07:43:26 +00:00
'#attributes' => $element [ '#attributes' ],
);
}
else {
// Generate the parents as the autogenerator does, so we will have a
// unique id for each radio button.
$parents_for_id = array_merge ( $element [ '#parents' ], array ( $key ));
$element [ $key ] = array (
'#type' => 'radio' ,
'#title' => '' ,
'#return_value' => $key ,
'#default_value' => ( $element [ '#default_value' ] == $key ) ? $key : NULL ,
'#attributes' => $element [ '#attributes' ],
'#parents' => $element [ '#parents' ],
2009-10-05 01:18:26 +00:00
'#id' => drupal_html_id ( 'edit-' . implode ( '-' , $parents_for_id )),
2010-07-10 00:03:37 +00:00
'#ajax' => isset ( $element [ '#ajax' ]) ? $element [ '#ajax' ] : NULL ,
2009-01-28 07:43:26 +00:00
);
}
2010-10-07 17:31:39 +00:00
if ( isset ( $element [ '#options' ][ $key ][ '#weight' ])) {
$element [ $key ][ '#weight' ] = $element [ '#options' ][ $key ][ '#weight' ];
}
2009-01-28 07:43:26 +00:00
}
}
}
else {
$element [ '#value' ] = array ();
}
return $element ;
}
2012-12-26 18:15:32 +00:00
/**
* #process callback for #type 'table' to add tableselect support.
*
* @ param array $element
* An associative array containing the properties and children of the
* table element .
* @ param array $form_state
* The current state of the form .
*
* @ return array
* The processed element .
*
* @ see form_process_tableselect ()
* @ see theme_tableselect ()
*/
function form_process_table ( $element , & $form_state ) {
if ( $element [ '#tableselect' ]) {
if ( $element [ '#multiple' ]) {
$value = is_array ( $element [ '#value' ]) ? $element [ '#value' ] : array ();
}
// Advanced selection behaviour makes no sense for radios.
else {
$element [ '#js_select' ] = FALSE ;
}
// Add a "Select all" checkbox column to the header.
// @todo D8: Rename into #select_all?
if ( $element [ '#js_select' ]) {
$element [ '#attached' ][ 'library' ][] = array ( 'system' , 'drupal.tableselect' );
array_unshift ( $element [ '#header' ], array ( 'class' => array ( 'select-all' )));
}
// Add an empty header column for radio buttons or when a "Select all"
// checkbox is not desired.
else {
array_unshift ( $element [ '#header' ], '' );
}
if ( ! isset ( $element [ '#default_value' ]) || $element [ '#default_value' ] === 0 ) {
$element [ '#default_value' ] = array ();
}
// Create a checkbox or radio for each row in a way that the value of the
// tableselect element behaves as if it had been of #type checkboxes or
// radios.
foreach ( element_children ( $element ) as $key ) {
// Do not overwrite manually created children.
if ( ! isset ( $element [ $key ][ 'select' ])) {
// Determine option label; either an assumed 'title' column, or the
// first available column containing a #title or #markup.
// @todo Consider to add an optional $element[$key]['#title_key']
// defaulting to 'title'?
$title = '' ;
if ( ! empty ( $element [ $key ][ 'title' ][ '#title' ])) {
$title = $element [ $key ][ 'title' ][ '#title' ];
}
else {
foreach ( element_children ( $element [ $key ]) as $column ) {
if ( isset ( $element [ $key ][ $column ][ '#title' ])) {
$title = $element [ $key ][ $column ][ '#title' ];
break ;
}
if ( isset ( $element [ $key ][ $column ][ '#markup' ])) {
$title = $element [ $key ][ $column ][ '#markup' ];
break ;
}
}
}
if ( $title !== '' ) {
$title = t ( 'Update !title' , array ( '!title' => $title ));
}
// Prepend the select column to existing columns.
$element [ $key ] = array ( 'select' => array ()) + $element [ $key ];
$element [ $key ][ 'select' ] += array (
'#type' => $element [ '#multiple' ] ? 'checkbox' : 'radio' ,
'#title' => $title ,
'#title_display' => 'invisible' ,
// @todo If rows happen to use numeric indexes instead of string keys,
// this results in a first row with $key === 0, which is always FALSE.
'#return_value' => $key ,
'#attributes' => $element [ '#attributes' ],
);
$element_parents = array_merge ( $element [ '#parents' ], array ( $key ));
if ( $element [ '#multiple' ]) {
$element [ $key ][ 'select' ][ '#default_value' ] = isset ( $value [ $key ]) ? $key : NULL ;
$element [ $key ][ 'select' ][ '#parents' ] = $element_parents ;
}
else {
$element [ $key ][ 'select' ][ '#default_value' ] = ( $element [ '#default_value' ] == $key ? $key : NULL );
$element [ $key ][ 'select' ][ '#parents' ] = $element [ '#parents' ];
$element [ $key ][ 'select' ][ '#id' ] = drupal_html_id ( 'edit-' . implode ( '-' , $element_parents ));
}
}
}
}
return $element ;
}
/**
* #element_validate callback for #type 'table'.
*
* @ param array $element
* An associative array containing the properties and children of the
* table element .
* @ param array $form_state
* The current state of the form .
*/
function form_validate_table ( $element , & $form_state ) {
// Skip this validation if the button to submit the form does not require
// selected table row data.
if ( empty ( $form_state [ 'triggering_element' ][ '#tableselect' ])) {
return ;
}
if ( $element [ '#multiple' ]) {
if ( ! is_array ( $element [ '#value' ]) || ! count ( array_filter ( $element [ '#value' ]))) {
form_error ( $element , t ( 'No items selected.' ));
}
}
elseif ( ! isset ( $element [ '#value' ]) || $element [ '#value' ] === '' ) {
form_error ( $element , t ( 'No item selected.' ));
}
}
2010-10-13 13:43:21 +00:00
/**
* Processes a machine - readable name form element .
*
* @ param $element
* The form element to process . Properties used :
* - #machine_name: An associative array containing:
2013-05-12 23:23:30 +00:00
* - exists : A callable to invoke for checking whether a submitted machine
* name value already exists . The submitted value is passed as an
2010-10-13 13:43:21 +00:00
* 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
2012-10-24 02:28:41 +00:00
* use as source for the machine name . Defaults to array ( 'label' ) .
2010-10-13 13:43:21 +00:00
* - 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 .
2012-03-12 03:00:51 +00:00
* - 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 .
2010-10-13 13:43:21 +00:00
* - #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 .
*/
function form_process_machine_name ( $element , & $form_state ) {
2013-05-02 07:51:45 +00:00
// We need to pass the langcode to the client.
2013-05-25 20:12:45 +00:00
$language = language ( Language :: TYPE_INTERFACE );
2013-05-02 07:51:45 +00:00
2010-10-13 13:43:21 +00:00
// 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 (),
2012-03-12 03:00:51 +00:00
'#field_prefix' => '' ,
'#field_suffix' => '' ,
'#suffix' => '' ,
2010-10-13 13:43:21 +00:00
);
// 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 (
2012-10-24 02:28:41 +00:00
'source' => array ( 'label' ),
2010-10-13 13:43:21 +00:00
'target' => '#' . $element [ '#id' ],
'label' => t ( 'Machine name' ),
'replace_pattern' => '[^a-z0-9_]+' ,
'replace' => '_' ,
2012-03-12 03:00:51 +00:00
'standalone' => FALSE ,
'field_prefix' => $element [ '#field_prefix' ],
'field_suffix' => $element [ '#field_suffix' ],
2010-10-13 13:43:21 +00:00
);
2011-12-08 03:29:13 +00:00
// 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' );
2010-10-13 13:43:21 +00:00
// 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
2012-03-12 03:00:51 +00:00
// complete form in $form_state. By reference, because we may need to append
2010-10-13 13:43:21 +00:00
// a #field_suffix that will hold the live preview.
$key_exists = NULL ;
2012-12-17 21:54:13 +00:00
$source = NestedArray :: getValue ( $form_state [ 'complete_form' ], $element [ '#machine_name' ][ 'source' ], $key_exists );
2010-10-13 13:43:21 +00:00
if ( ! $key_exists ) {
return $element ;
}
$suffix_id = $source [ '#id' ] . '-machine-name-suffix' ;
2012-03-12 03:00:51 +00:00
$element [ '#machine_name' ][ 'suffix' ] = '#' . $suffix_id ;
2010-10-13 13:43:21 +00:00
2012-03-12 03:00:51 +00:00
if ( $element [ '#machine_name' ][ 'standalone' ]) {
$element [ '#suffix' ] .= ' <small id="' . $suffix_id . '"> </small>' ;
}
else {
// Append a field suffix to the source form element, which will contain
// the live preview of the machine name.
$source += array ( '#field_suffix' => '' );
$source [ '#field_suffix' ] .= ' <small id="' . $suffix_id . '"> </small>' ;
2010-10-13 13:43:21 +00:00
2012-03-12 03:00:51 +00:00
$parents = array_merge ( $element [ '#machine_name' ][ 'source' ], array ( '#field_suffix' ));
2012-12-17 21:54:13 +00:00
NestedArray :: setValue ( $form_state [ 'complete_form' ], $parents , $source [ '#field_suffix' ]);
2012-03-12 03:00:51 +00:00
}
2010-10-13 13:43:21 +00:00
$js_settings = array (
'type' => 'setting' ,
'data' => array (
'machineName' => array (
'#' . $source [ '#id' ] => $element [ '#machine_name' ],
),
2013-06-29 10:56:53 +00:00
'langcode' => $language -> id ,
2010-10-13 13:43:21 +00:00
),
);
2012-08-30 19:24:38 +00:00
$element [ '#attached' ][ 'library' ][] = array ( 'system' , 'drupal.machine-name' );
2010-10-13 13:43:21 +00:00
$element [ '#attached' ][ 'js' ][] = $js_settings ;
return $element ;
}
/**
2012-02-19 03:41:24 +00:00
* Form element validation handler for machine_name elements .
2010-10-13 13:43:21 +00:00
*
* Note that #maxlength is validated by _form_validate() already.
*/
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 , 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 , t ( 'The machine-readable name must contain only lowercase letters, numbers, and hyphens.' ));
}
// Otherwise, we assume the default (underscore).
else {
form_error ( $element , t ( 'The machine-readable name must contain only lowercase letters, numbers, and underscores.' ));
}
}
else {
form_error ( $element , $element [ '#machine_name' ][ 'error' ]);
}
}
// Verify that the machine name is unique.
if ( $element [ '#default_value' ] !== $element [ '#value' ]) {
$function = $element [ '#machine_name' ][ 'exists' ];
2013-05-12 23:23:30 +00:00
if ( call_user_func ( $function , $element [ '#value' ], $element , $form_state )) {
2010-10-13 13:43:21 +00:00
form_error ( $element , t ( 'The machine-readable name is already in use. It must be unique.' ));
}
}
}
2009-04-11 22:19:46 +00:00
/**
2013-01-17 08:06:42 +00:00
* Arranges elements into groups .
2009-04-11 22:19:46 +00:00
*
2011-05-08 19:50:38 +00:00
* @ param $element
2009-04-11 22:19:46 +00:00
* An associative array containing the properties and children of the
2013-01-17 08:06:42 +00:00
* element . Note that $element must be taken by reference here , so processed
2009-12-14 13:32:53 +00:00
* child elements are taken over into $form_state .
2009-04-11 22:19:46 +00:00
* @ param $form_state
2013-01-17 08:06:42 +00:00
* The $form_state array for the form this element belongs to .
2012-02-19 03:41:24 +00:00
*
2009-04-11 22:19:46 +00:00
* @ return
* The processed element .
*/
2013-01-17 08:06:42 +00:00
function form_process_group ( & $element , & $form_state ) {
2009-04-11 22:19:46 +00:00
$parents = implode ( '][' , $element [ '#parents' ]);
2012-11-27 07:06:47 +00:00
// Each details element forms a new group. The #type 'vertical_tabs' basically
// only injects a new details element.
2009-04-11 22:19:46 +00:00
$form_state [ 'groups' ][ $parents ][ '#group_exists' ] = TRUE ;
2009-12-14 13:32:53 +00:00
$element [ '#groups' ] = & $form_state [ 'groups' ];
2012-11-27 07:06:47 +00:00
// Process vertical tabs group member details elements.
2009-12-14 13:32:53 +00:00
if ( isset ( $element [ '#group' ])) {
2012-11-27 07:06:47 +00:00
// Add this details element to the defined group (by reference).
2009-12-14 13:32:53 +00:00
$group = $element [ '#group' ];
$form_state [ 'groups' ][ $group ][] = & $element ;
2009-04-11 22:19:46 +00:00
}
2010-07-07 17:56:42 +00:00
// Contains form element summary functionalities.
2010-11-30 17:16:37 +00:00
$element [ '#attached' ][ 'library' ][] = array ( 'system' , 'drupal.form' );
2010-07-07 17:56:42 +00:00
2012-04-24 01:52:29 +00:00
return $element ;
}
/**
2013-01-17 08:06:42 +00:00
* Adds form element theming to details .
2012-04-24 01:52:29 +00:00
*
* @ param $element
* An associative array containing the properties and children of the
2012-11-27 07:06:47 +00:00
* details .
2012-04-24 01:52:29 +00:00
*
* @ return
2013-01-17 08:06:42 +00:00
* The modified element .
2012-04-24 01:52:29 +00:00
*/
2012-11-27 07:06:47 +00:00
function form_pre_render_details ( $element ) {
// The .form-wrapper class is required for #states to treat details like
2010-04-29 03:34:00 +00:00
// containers.
if ( ! isset ( $element [ '#attributes' ][ 'class' ])) {
$element [ '#attributes' ][ 'class' ] = array ();
}
2012-11-27 07:06:47 +00:00
// Collapsible details.
2013-02-01 21:48:05 +00:00
$element [ '#attached' ][ 'library' ][] = array ( 'system' , 'drupal.collapse' );
2013-05-29 21:08:39 +00:00
if ( empty ( $element [ '#collapsed' ])) {
2012-11-27 07:06:47 +00:00
$element [ '#attributes' ][ 'open' ] = 'open' ;
2009-08-29 16:30:14 +00:00
}
2009-04-11 22:19:46 +00:00
2013-01-17 08:06:42 +00:00
return $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 .
*/
function form_pre_render_group ( $element ) {
// The element may be rendered outside of a Form API context.
2010-09-16 20:14:49 +00:00
if ( ! isset ( $element [ '#parents' ]) || ! isset ( $element [ '#groups' ])) {
return $element ;
}
2013-01-17 08:06:42 +00:00
2009-12-14 13:32:53 +00:00
// Inject group member elements belonging to this group.
$parents = implode ( '][' , $element [ '#parents' ]);
$children = element_children ( $element [ '#groups' ][ $parents ]);
if ( ! empty ( $children )) {
foreach ( $children as $key ) {
// Break references and indicate that the element should be rendered as
// group member.
$child = ( array ) $element [ '#groups' ][ $parents ][ $key ];
2012-11-27 07:06:47 +00:00
$child [ '#group_details' ] = TRUE ;
2009-12-14 13:32:53 +00:00
// Inject the element as new child element.
$element [] = $child ;
$sort = TRUE ;
}
// Re-sort the element's children if we injected group member elements.
if ( isset ( $sort )) {
$element [ '#sorted' ] = FALSE ;
}
}
if ( isset ( $element [ '#group' ])) {
$group = $element [ '#group' ];
// If this element belongs to a group, but the group-holding element does
// not exist, we need to render it (at its original location).
if ( ! isset ( $element [ '#groups' ][ $group ][ '#group_exists' ])) {
// Intentionally empty to clarify the flow; we simply return $element.
}
// If we injected this element into the group, then we want to render it.
2012-11-27 07:06:47 +00:00
elseif ( ! empty ( $element [ '#group_details' ])) {
2009-12-14 13:32:53 +00:00
// Intentionally empty to clarify the flow; we simply return $element.
}
// Otherwise, this element belongs to a group and the group exists, so we do
// not render it.
elseif ( element_children ( $element [ '#groups' ][ $group ])) {
$element [ '#printed' ] = TRUE ;
2009-04-11 22:19:46 +00:00
}
}
return $element ;
}
/**
* Creates a group formatted as vertical tabs .
*
* @ param $element
* An associative array containing the properties and children of the
2012-11-27 07:06:47 +00:00
* details element .
2009-04-11 22:19:46 +00:00
* @ param $form_state
* The $form_state array for the form this vertical tab widget belongs to .
2012-02-19 03:41:24 +00:00
*
2009-04-11 22:19:46 +00:00
* @ return
* The processed element .
*/
function form_process_vertical_tabs ( $element , & $form_state ) {
2012-11-27 07:06:47 +00:00
// Inject a new details as child, so that form_process_details() processes
// this details element like any other details.
2009-04-11 22:19:46 +00:00
$element [ 'group' ] = array (
2012-11-27 07:06:47 +00:00
'#type' => 'details' ,
2009-08-04 06:38:57 +00:00
'#theme_wrappers' => array (),
2009-04-11 22:19:46 +00:00
'#parents' => $element [ '#parents' ],
);
2013-01-11 08:45:49 +00:00
// Add an invisible label for accessibility.
if ( ! isset ( $element [ '#title' ])) {
$element [ '#title' ] = t ( 'Vertical Tabs' );
$element [ '#title_display' ] = 'invisible' ;
}
$element [ '#attached' ][ 'library' ][] = array ( 'system' , 'drupal.vertical-tabs' );
2009-04-11 22:19:46 +00:00
// The JavaScript stores the currently selected tab in this hidden
// field so that the active tab can be restored the next time the
// form is rendered, e.g. on preview pages or when form validation
// fails.
$name = implode ( '__' , $element [ '#parents' ]);
if ( isset ( $form_state [ 'values' ][ $name . '__active_tab' ])) {
$element [ '#default_tab' ] = $form_state [ 'values' ][ $name . '__active_tab' ];
}
$element [ $name . '__active_tab' ] = array (
'#type' => 'hidden' ,
'#default_value' => $element [ '#default_tab' ],
2009-08-22 14:34:23 +00:00
'#attributes' => array ( 'class' => array ( 'vertical-tabs-active-tab' )),
2009-04-11 22:19:46 +00:00
);
return $element ;
}
2013-01-11 08:45:49 +00:00
/**
* Prepares a vertical_tabs element for rendering .
*
* @ param array $element
* An associative array containing the properties and children of the
* vertical tabs element .
*
* @ return array
* The modified element .
*/
function form_pre_render_vertical_tabs ( $element ) {
// Do not render the vertical tabs element if it is empty.
$group = implode ( '][' , $element [ '#parents' ]);
if ( ! element_get_visible_children ( $element [ 'group' ][ '#groups' ][ $group ])) {
$element [ '#printed' ] = TRUE ;
}
return $element ;
}
2009-04-11 22:19:46 +00:00
/**
2012-11-27 07:06:47 +00:00
* Returns HTML for an element ' s children details as vertical tabs .
2009-04-11 22:19:46 +00:00
*
2009-10-09 01:00:08 +00:00
* @ param $variables
* An associative array containing :
2012-02-19 03:41:24 +00:00
* - element : An associative array containing the properties and children of
2012-11-27 07:06:47 +00:00
* the details element . Properties used : #children.
2009-10-09 01:00:08 +00:00
*
2009-08-19 08:15:36 +00:00
* @ ingroup themeable
2009-04-11 22:19:46 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_vertical_tabs ( $variables ) {
$element = $variables [ 'element' ];
2013-01-11 08:45:49 +00:00
return '<div class="vertical-tabs-panes">' . $element [ '#children' ] . '</div>' ;
2009-04-11 22:19:46 +00:00
}
2012-03-30 16:17:26 +00:00
/**
* Adds autocomplete functionality to elements with a valid #autocomplete_path.
*
2012-04-17 03:40:10 +00:00
* Suppose your autocomplete path in the menu system is 'mymodule_autocomplete.'
* In your form you have :
* @ code
* '#autocomplete_path' => 'mymodule_autocomplete/' . $some_key . '/' . $some_id ,
* @ endcode
* The user types in " keywords " so the full path called is :
2012-12-03 03:23:33 +00:00
* 'mymodule_autocomplete/$some_key/$some_id?q=keywords'
2012-04-17 03:40:10 +00:00
*
2012-03-30 16:17:26 +00:00
* @ param $element
* The form element to process . Properties used :
* - #autocomplete_path: A system path to be used as callback URL by the
* autocomplete JavaScript library .
*/
function form_process_autocomplete ( $element , & $form_state ) {
if ( ! empty ( $element [ '#autocomplete_path' ]) && drupal_valid_path ( $element [ '#autocomplete_path' ])) {
$element [ '#attributes' ][ 'class' ][] = 'form-autocomplete' ;
$element [ '#attached' ][ 'library' ][] = array ( 'system' , 'drupal.autocomplete' );
// Provide a hidden element for the JavaScript behavior to bind to. Since
2013-01-20 10:56:27 +00:00
// this element is for client-side functionality only, do not process input.
2012-03-30 16:17:26 +00:00
// @todo Refactor autocomplete.js to accept Drupal.settings instead of
// requiring extraneous markup.
$element [ 'autocomplete' ] = array (
2013-01-20 10:56:27 +00:00
'#type' => 'hidden' ,
'#input' => FALSE ,
'#value' => url ( $element [ '#autocomplete_path' ], array ( 'absolute' => TRUE )),
'#disabled' => TRUE ,
2012-03-30 16:17:26 +00:00
'#attributes' => array (
'class' => array ( 'autocomplete' ),
2013-01-20 17:31:53 +00:00
'id' => $element [ '#id' ] . '-autocomplete' ,
2012-03-30 16:17:26 +00:00
),
);
}
return $element ;
}
2007-12-06 09:58:34 +00:00
/**
2013-01-10 05:45:55 +00:00
* Preprocesses variables for theme_input () .
2009-08-19 08:15:36 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $variables
2009-10-09 01:00:08 +00:00
* An associative array containing :
* - element : An associative array containing the properties of the element .
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
*/
2013-01-10 05:45:55 +00:00
function template_preprocess_input ( & $variables ) {
$element = $variables [ 'element' ];
$variables [ 'attributes' ] = new Attribute ( $element [ '#attributes' ]);
2005-10-07 06:11:12 +00:00
}
2007-12-06 09:58:34 +00:00
/**
2013-01-10 05:45:55 +00:00
* Returns HTML for an input form element .
2009-08-19 08:15:36 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $variables
2009-10-09 01:00:08 +00:00
* An associative array containing :
* - element : An associative array containing the properties of the element .
2013-01-10 05:45:55 +00:00
* Properties used : #attributes.
2012-11-21 19:37:44 +00:00
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
*/
2013-01-10 05:45:55 +00:00
function theme_input ( $variables ) {
2009-10-09 01:00:08 +00:00
$element = $variables [ 'element' ];
2013-01-10 05:45:55 +00:00
$attributes = $variables [ 'attributes' ];
return '<input' . $attributes . ' />' . drupal_render_children ( $element );
}
/**
* Prepares a #type 'button' render element for theme_input().
*
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #attributes, #button_type, #name, #value.
*
* The #button_type property accepts any value, though core themes have CSS that
* styles the following button_types appropriately : 'primary' , 'danger' .
*
* @ return array
* The $element with prepared variables ready for theme_input () .
*/
function form_pre_render_button ( $element ) {
2010-07-31 04:01:51 +00:00
$element [ '#attributes' ][ 'type' ] = 'submit' ;
2010-09-16 20:14:49 +00:00
element_set_attributes ( $element , array ( 'id' , 'name' , 'value' ));
2013-01-02 12:00:25 +00:00
$element [ '#attributes' ][ 'class' ][] = 'button' ;
2012-11-21 19:37:44 +00:00
if ( ! empty ( $element [ '#button_type' ])) {
2013-01-02 12:00:25 +00:00
$element [ '#attributes' ][ 'class' ][] = 'button-' . $element [ '#button_type' ];
2012-11-21 19:37:44 +00:00
}
// @todo Various JavaScript depends on this button class.
$element [ '#attributes' ][ 'class' ][] = 'form-submit' ;
2010-07-31 04:01:51 +00:00
if ( ! empty ( $element [ '#attributes' ][ 'disabled' ])) {
$element [ '#attributes' ][ 'class' ][] = 'form-button-disabled' ;
}
2006-03-29 23:29:41 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2005-10-07 06:11:12 +00:00
}
2007-07-29 17:28:23 +00:00
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'image_button' render element for theme_input().
2007-12-06 09:58:34 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #attributes, #button_type, #name, #value, #title, #src.
2009-10-09 01:00:08 +00:00
*
2012-11-21 19:37:44 +00:00
* The #button_type property accepts any value, though core themes have css that
* styles the following button_types appropriately : 'primary' , 'danger' .
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2007-07-29 17:28:23 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_image_button ( $element ) {
2010-08-20 01:08:18 +00:00
$element [ '#attributes' ][ 'type' ] = 'image' ;
2010-09-16 20:14:49 +00:00
element_set_attributes ( $element , array ( 'id' , 'name' , 'value' ));
2010-07-31 04:01:51 +00:00
$element [ '#attributes' ][ 'src' ] = file_create_url ( $element [ '#src' ]);
if ( ! empty ( $element [ '#title' ])) {
$element [ '#attributes' ][ 'alt' ] = $element [ '#title' ];
$element [ '#attributes' ][ 'title' ] = $element [ '#title' ];
}
2012-11-21 19:37:44 +00:00
$element [ '#attributes' ][ 'class' ][] = 'image-button' ;
if ( ! empty ( $element [ '#button_type' ])) {
$element [ '#attributes' ][ 'class' ][] = 'image-button-' . $element [ '#button_type' ];
}
// @todo Various JavaScript depends on this button class.
$element [ '#attributes' ][ 'class' ][] = 'form-submit' ;
2010-07-31 04:01:51 +00:00
if ( ! empty ( $element [ '#attributes' ][ 'disabled' ])) {
2012-11-21 19:37:44 +00:00
$element [ '#attributes' ][ 'class' ][] = 'image-button-disabled' ;
2010-07-31 04:01:51 +00:00
}
2007-07-29 17:28:23 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2007-07-29 17:28:23 +00:00
}
2005-10-07 06:11:12 +00:00
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'hidden' render element for theme_input().
2005-10-07 06:11:12 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #name, #value, #attributes.
2009-10-09 01:00:08 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2005-10-07 06:11:12 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_hidden ( $element ) {
2010-07-31 04:01:51 +00:00
$element [ '#attributes' ][ 'type' ] = 'hidden' ;
2010-09-22 03:34:57 +00:00
element_set_attributes ( $element , array ( 'name' , 'value' ));
2013-01-10 05:45:55 +00:00
return $element ;
2005-10-07 06:11:12 +00:00
}
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'textfield' render element for theme_input().
2005-10-07 06:11:12 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #title, #value, #description, #size, #maxlength,
* #placeholder, #required, #attributes.
2009-10-09 01:00:08 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2005-10-07 06:11:12 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_textfield ( $element ) {
2010-07-31 04:01:51 +00:00
$element [ '#attributes' ][ 'type' ] = 'text' ;
2011-08-06 22:03:31 +00:00
element_set_attributes ( $element , array ( 'id' , 'name' , 'value' , 'size' , 'maxlength' , 'placeholder' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-text' ));
2006-08-26 09:56:17 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2012-01-19 12:55:48 +00:00
}
2012-03-02 07:29:16 +00:00
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'email' render element for theme_input().
2012-03-02 07:29:16 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #title, #value, #description, #size, #maxlength,
* #placeholder, #required, #attributes.
2012-03-02 07:29:16 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2012-03-02 07:29:16 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_email ( $element ) {
2012-03-02 07:29:16 +00:00
$element [ '#attributes' ][ 'type' ] = 'email' ;
element_set_attributes ( $element , array ( 'id' , 'name' , 'value' , 'size' , 'maxlength' , 'placeholder' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-email' ));
2012-03-02 07:29:16 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2012-03-02 07:29:16 +00:00
}
/**
* Form element validation handler for #type 'email'.
*
* Note that #maxlength and #required is validated by _form_validate() already.
*/
function form_validate_email ( & $element , & $form_state ) {
$value = trim ( $element [ '#value' ]);
form_set_value ( $element , $value , $form_state );
if ( $value !== '' && ! valid_email_address ( $value )) {
form_error ( $element , t ( 'The e-mail address %mail is not valid.' , array ( '%mail' => $value )));
}
}
2012-01-19 12:55:48 +00:00
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'tel' render element for theme_input().
2012-01-19 12:55:48 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #title, #value, #description, #size, #maxlength,
* #placeholder, #required, #attributes.
2012-01-19 12:55:48 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2012-01-19 12:55:48 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_tel ( $element ) {
2012-01-19 12:55:48 +00:00
$element [ '#attributes' ][ 'type' ] = 'tel' ;
element_set_attributes ( $element , array ( 'id' , 'name' , 'value' , 'size' , 'maxlength' , 'placeholder' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-tel' ));
2012-01-19 12:55:48 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2005-10-07 06:11:12 +00:00
}
2012-04-07 02:35:00 +00:00
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'number' render element for theme_input().
2012-04-07 02:35:00 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #title, #value, #description, #min, #max, #placeholder,
2013-05-19 17:37:19 +00:00
* #required, #attributes, #step, #size.
2012-04-07 02:35:00 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2012-04-07 02:35:00 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_number ( $element ) {
2012-04-07 02:35:00 +00:00
$element [ '#attributes' ][ 'type' ] = 'number' ;
2013-05-19 17:37:19 +00:00
element_set_attributes ( $element , array ( 'id' , 'name' , 'value' , 'step' , 'min' , 'max' , 'placeholder' , 'size' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-number' ));
2012-04-07 02:35:00 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2012-04-07 02:35:00 +00:00
}
2012-04-19 03:07:55 +00:00
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'range' render element for theme_input().
2012-04-19 03:07:55 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #title, #value, #description, #min, #max, #attributes,
* #step.
2012-04-19 03:07:55 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2012-04-19 03:07:55 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_range ( $element ) {
2012-04-19 03:07:55 +00:00
$element [ '#attributes' ][ 'type' ] = 'range' ;
element_set_attributes ( $element , array ( 'id' , 'name' , 'value' , 'step' , 'min' , 'max' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-range' ));
2012-04-19 03:07:55 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2012-04-19 03:07:55 +00:00
}
2012-04-07 02:35:00 +00:00
/**
* Form element validation handler for #type 'number'.
*
* Note that #required is validated by _form_validate() already.
*/
function form_validate_number ( & $element , & $form_state ) {
$value = $element [ '#value' ];
if ( $value === '' ) {
return ;
}
$name = empty ( $element [ '#title' ]) ? $element [ '#parents' ][ 0 ] : $element [ '#title' ];
// Ensure the input is numeric.
if ( ! is_numeric ( $value )) {
form_error ( $element , t ( '%name must be a number.' , array ( '%name' => $name )));
return ;
}
// Ensure that the input is greater than the #min property, if set.
if ( isset ( $element [ '#min' ]) && $value < $element [ '#min' ]) {
form_error ( $element , t ( '%name must be higher or equal to %min.' , array ( '%name' => $name , '%min' => $element [ '#min' ])));
}
// Ensure that the input is less than the #max property, if set.
if ( isset ( $element [ '#max' ]) && $value > $element [ '#max' ]) {
form_error ( $element , t ( '%name must be below or equal to %max.' , array ( '%name' => $name , '%max' => $element [ '#max' ])));
}
if ( isset ( $element [ '#step' ]) && strtolower ( $element [ '#step' ]) != 'any' ) {
// Check that the input is an allowed multiple of #step (offset by #min if
// #min is set).
$offset = isset ( $element [ '#min' ]) ? $element [ '#min' ] : 0.0 ;
if ( ! valid_number_step ( $value , $element [ '#step' ], $offset )) {
form_error ( $element , t ( '%name is not a valid number.' , array ( '%name' => $name )));
}
}
}
2012-05-08 02:53:23 +00:00
/**
* Determines the value for a range element .
*
* Make sure range elements always have a value . The 'required' attribute is not
* allowed for range elements .
*
* @ param $element
* The form element whose value is being populated .
* @ param $input
* The incoming input to populate the form element . If this is FALSE , the
* element ' s default value should be returned .
*
* @ return
* The data that will appear in the $form_state [ 'values' ] collection for
* this element . Return nothing to use the default .
*/
function form_type_range_value ( $element , $input = FALSE ) {
if ( $input === '' ) {
$offset = ( $element [ '#max' ] - $element [ '#min' ]) / 2 ;
// Round to the step.
if ( strtolower ( $element [ '#step' ]) != 'any' ) {
$steps = round ( $offset / $element [ '#step' ]);
$offset = $element [ '#step' ] * $steps ;
}
return $element [ '#min' ] + $offset ;
}
}
2012-03-08 15:13:41 +00:00
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'url' render element for theme_input().
2012-03-08 15:13:41 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #title, #value, #description, #size, #maxlength,
* #placeholder, #required, #attributes.
2012-03-08 15:13:41 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2012-03-08 15:13:41 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_url ( $element ) {
2012-03-08 15:13:41 +00:00
$element [ '#attributes' ][ 'type' ] = 'url' ;
element_set_attributes ( $element , array ( 'id' , 'name' , 'value' , 'size' , 'maxlength' , 'placeholder' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-url' ));
2012-03-08 15:13:41 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2012-03-27 06:17:38 +00:00
}
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'search' render element for theme_input().
2012-03-27 06:17:38 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #title, #value, #description, #size, #maxlength,
* #placeholder, #required, #attributes.
2012-03-27 06:17:38 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2012-03-27 06:17:38 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_search ( $element ) {
2012-03-27 06:17:38 +00:00
$element [ '#attributes' ][ 'type' ] = 'search' ;
element_set_attributes ( $element , array ( 'id' , 'name' , 'value' , 'size' , 'maxlength' , 'placeholder' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-search' ));
2012-03-27 06:17:38 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2012-03-08 15:13:41 +00:00
}
/**
* Form element validation handler for #type 'url'.
*
* Note that #maxlength and #required is validated by _form_validate() already.
*/
function form_validate_url ( & $element , & $form_state ) {
$value = trim ( $element [ '#value' ]);
form_set_value ( $element , $value , $form_state );
if ( $value !== '' && ! valid_url ( $value , TRUE )) {
form_error ( $element , t ( 'The URL %url is not valid.' , array ( '%url' => $value )));
}
}
2012-07-07 20:21:18 +00:00
/**
* Form element validation handler for #type 'color'.
*/
function form_validate_color ( & $element , & $form_state ) {
$value = trim ( $element [ '#value' ]);
// Default to black if no value is given.
// @see http://www.w3.org/TR/html5/number-state.html#color-state
if ( $value === '' ) {
form_set_value ( $element , '#000000' , $form_state );
}
else {
// Try to parse the value and normalize it.
try {
form_set_value ( $element , Color :: rgbToHex ( Color :: hexToRgb ( $value )), $form_state );
}
catch ( InvalidArgumentException $e ) {
form_error ( $element , t ( '%name must be a valid color.' , array ( '%name' => empty ( $element [ '#title' ]) ? $element [ '#parents' ][ 0 ] : $element [ '#title' ])));
}
}
}
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'color' render element for theme_input().
2012-07-07 20:21:18 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #title, #value, #description, #attributes.
2012-07-07 20:21:18 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2012-07-07 20:21:18 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_color ( $element ) {
2012-07-07 20:21:18 +00:00
$element [ '#attributes' ][ 'type' ] = 'color' ;
element_set_attributes ( $element , array ( 'id' , 'name' , 'value' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-color' ));
2012-07-07 20:21:18 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2012-07-07 20:21:18 +00:00
}
2005-10-07 06:11:12 +00:00
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a form .
2005-10-07 06:11:12 +00:00
*
2009-10-09 01:00:08 +00:00
* @ param $variables
* An associative array containing :
* - element : An associative array containing the properties of the element .
* Properties used : #action, #method, #attributes, #children
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_form ( $variables ) {
$element = $variables [ 'element' ];
2010-09-16 20:14:49 +00:00
if ( isset ( $element [ '#action' ])) {
2010-07-31 04:01:51 +00:00
$element [ '#attributes' ][ 'action' ] = drupal_strip_dangerous_protocols ( $element [ '#action' ]);
}
2010-09-16 20:14:49 +00:00
element_set_attributes ( $element , array ( 'method' , 'id' ));
2010-06-25 20:19:13 +00:00
if ( empty ( $element [ '#attributes' ][ 'accept-charset' ])) {
$element [ '#attributes' ][ 'accept-charset' ] = " UTF-8 " ;
}
2010-07-31 04:01:51 +00:00
// Anonymous DIV to satisfy XHTML compliance.
2012-09-04 13:32:47 +00:00
return '<form' . new Attribute ( $element [ '#attributes' ]) . '><div>' . $element [ '#children' ] . '</div></form>' ;
2005-10-07 06:11:12 +00:00
}
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a textarea form element .
2005-10-07 06:11:12 +00:00
*
2009-10-09 01:00:08 +00:00
* @ param $variables
* An associative array containing :
* - element : An associative array containing the properties of the element .
2011-08-06 22:03:31 +00:00
* Properties used : #title, #value, #description, #rows, #cols,
2012-03-02 06:28:37 +00:00
* #placeholder, #required, #attributes, #resizable
2009-10-09 01:00:08 +00:00
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_textarea ( $variables ) {
$element = $variables [ 'element' ];
2011-08-06 22:03:31 +00:00
element_set_attributes ( $element , array ( 'id' , 'name' , 'rows' , 'cols' , 'placeholder' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-textarea' ));
2010-07-31 04:01:51 +00:00
2010-03-09 11:45:37 +00:00
$wrapper_attributes = array (
'class' => array ( 'form-textarea-wrapper' ),
);
2007-04-09 13:58:03 +00:00
2010-03-09 11:45:37 +00:00
// Add resizable behavior.
if ( ! empty ( $element [ '#resizable' ])) {
2012-03-02 06:28:37 +00:00
$element [ '#attributes' ][ 'class' ][] = 'resize-' . $element [ '#resizable' ];
2005-12-29 03:59:30 +00:00
}
2012-09-04 13:32:47 +00:00
$output = '<div' . new Attribute ( $wrapper_attributes ) . '>' ;
$output .= '<textarea' . new Attribute ( $element [ '#attributes' ]) . '>' . check_plain ( $element [ '#value' ]) . '</textarea>' ;
2010-03-09 11:45:37 +00:00
$output .= '</div>' ;
return $output ;
2005-10-07 06:11:12 +00:00
}
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'password' render element for theme_input().
2007-05-04 09:41:37 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #title, #value, #description, #size, #maxlength,
* #placeholder, #required, #attributes.
2009-10-09 01:00:08 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2007-05-04 09:41:37 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_password ( $element ) {
2010-07-31 04:01:51 +00:00
$element [ '#attributes' ][ 'type' ] = 'password' ;
2011-08-06 22:03:31 +00:00
element_set_attributes ( $element , array ( 'id' , 'name' , 'size' , 'maxlength' , 'placeholder' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-text' ));
2010-07-31 04:01:51 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2005-10-07 06:11:12 +00:00
}
/**
2012-02-19 03:41:24 +00:00
* Expands a weight element into a select element .
2005-10-07 06:11:12 +00:00
*/
2008-07-18 07:06:24 +00:00
function form_process_weight ( $element ) {
2006-05-04 09:57:14 +00:00
$element [ '#is_weight' ] = TRUE ;
2011-12-02 05:05:37 +00:00
// If the number of options is small enough, use a select field.
2012-12-02 04:56:04 +00:00
$max_elements = config ( 'system.site' ) -> get ( 'weight_select_max' );
2011-12-02 05:05:37 +00:00
if ( $element [ '#delta' ] <= $max_elements ) {
$element [ '#type' ] = 'select' ;
for ( $n = ( - 1 * $element [ '#delta' ]); $n <= $element [ '#delta' ]; $n ++ ) {
$weights [ $n ] = $n ;
}
$element [ '#options' ] = $weights ;
$element += element_info ( 'select' );
}
// Otherwise, use a text field.
else {
2012-04-09 18:11:07 +00:00
$element [ '#type' ] = 'number' ;
2011-12-02 05:05:37 +00:00
// Use a field big enough to fit most weights.
$element [ '#size' ] = 10 ;
2012-04-09 18:11:07 +00:00
$element += element_info ( 'number' );
2011-12-02 05:05:37 +00:00
}
2006-05-04 09:57:14 +00:00
return $element ;
2005-10-07 06:11:12 +00:00
}
/**
2013-01-10 05:45:55 +00:00
* Prepares a #type 'file' render element for theme_input().
2010-04-13 15:23:03 +00:00
*
* For assistance with handling the uploaded file correctly , see the API
* provided by file . inc .
2005-10-07 06:11:12 +00:00
*
2013-01-10 05:45:55 +00:00
* @ param array $element
* An associative array containing the properties of the element .
* Properties used : #title, #name, #size, #description, #required,
* #attributes.
2009-10-09 01:00:08 +00:00
*
2013-01-10 05:45:55 +00:00
* @ return array
* The $element with prepared variables ready for theme_input () .
2005-10-07 06:11:12 +00:00
*/
2013-01-10 05:45:55 +00:00
function form_pre_render_file ( $element ) {
2010-07-31 04:01:51 +00:00
$element [ '#attributes' ][ 'type' ] = 'file' ;
2010-09-16 20:14:49 +00:00
element_set_attributes ( $element , array ( 'id' , 'name' , 'size' ));
2012-10-23 22:43:41 +00:00
_form_set_attributes ( $element , array ( 'form-file' ));
2010-07-31 04:01:51 +00:00
2013-01-10 05:45:55 +00:00
return $element ;
2006-05-02 09:26:33 +00:00
}
2013-04-20 03:34:14 +00:00
/**
* Processes a file upload element , make use of #multiple if present.
*/
function form_process_file ( $element ) {
if ( $element [ '#multiple' ]) {
$element [ '#attributes' ] = array ( 'multiple' => 'multiple' );
$element [ '#name' ] .= '[]' ;
}
return $element ;
}
2006-05-02 09:26:33 +00:00
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a form element .
2006-05-02 09:26:33 +00:00
*
2010-07-31 04:01:51 +00:00
* Each form element is wrapped in a DIV container having the following CSS
* classes :
* - form - item : Generic for all form elements .
* - form - type - #type: The internal element #type.
* - form - item - #name: The internal form element #name (usually derived from the
* $form structure and set via form_builder ()) .
* - form - disabled : Only set if the form element is #disabled.
*
* In addition to the element itself , the DIV contains a label for the element
* based on the optional #title_display property, and an optional #description.
2009-12-02 15:09:16 +00:00
*
* The optional #title_display property can have these values:
* - before : The label is output before the element . This is the default .
* The label includes the #title and the required marker, if #required.
* - after : The label is output after the element . For example , this is used
* for radio and checkbox #type elements as set in system_element_info().
* If the #title is empty but the field is #required, the label will
* contain only the required marker .
2010-04-07 04:39:59 +00:00
* - invisible : Labels are critical for screen readers to enable them to
* properly navigate through forms but can be visually distracting . This
* property hides the label for everyone except screen readers .
2009-12-02 15:09:16 +00:00
* - attribute : Set the title attribute on the element to create a tooltip
* but output no label element . This is supported only for checkboxes
* and radios in form_pre_render_conditional_form_element () . It is used
* where a visual label is not needed , such as a table of checkboxes where
* the row and column provide the context . The tooltip will include the
* title and required marker .
*
* If the #title property is not set, then the label and any required marker
* will not be output , regardless of the #title_display or #required values.
* This can be useful in cases such as the password_confirm element , which
* creates children elements that have their own labels and required markers ,
* but the parent element should have neither . Use this carefully because a
* field without an associated label can cause accessibility challenges .
*
2009-10-09 01:00:08 +00:00
* @ param $variables
* An associative array containing :
* - element : An associative array containing the properties of the element .
2009-12-02 15:09:16 +00:00
* Properties used : #title, #title_display, #description, #id, #required,
* #children, #type, #name.
2009-10-09 01:00:08 +00:00
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
2006-05-02 09:26:33 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_form_element ( $variables ) {
2010-09-16 20:14:49 +00:00
$element = & $variables [ 'element' ];
2007-11-13 14:04:08 +00:00
2010-09-16 20:14:49 +00:00
// This function is invoked as theme wrapper, but the rendered form element
// may not necessarily have been processed by form_builder().
$element += array (
'#title_display' => 'before' ,
);
2013-01-17 20:43:29 +00:00
// Take over any #wrapper_attributes defined by the element.
// @todo Temporary hack for #type 'item'.
// @see http://drupal.org/node/1829202
if ( isset ( $element [ '#wrapper_attributes' ])) {
$attributes = $element [ '#wrapper_attributes' ];
}
2010-05-01 00:19:35 +00:00
// Add element #id for #type 'item'.
if ( isset ( $element [ '#markup' ]) && ! empty ( $element [ '#id' ])) {
$attributes [ 'id' ] = $element [ '#id' ];
}
2009-06-14 08:04:25 +00:00
// Add element's #type and #name as class to aid with JS/CSS selectors.
2013-01-17 20:43:29 +00:00
$attributes [ 'class' ][] = 'form-item' ;
2009-06-14 08:04:25 +00:00
if ( ! empty ( $element [ '#type' ])) {
2010-05-01 00:19:35 +00:00
$attributes [ 'class' ][] = 'form-type-' . strtr ( $element [ '#type' ], '_' , '-' );
2009-06-14 08:04:25 +00:00
}
if ( ! empty ( $element [ '#name' ])) {
2010-05-01 00:19:35 +00:00
$attributes [ 'class' ][] = 'form-item-' . strtr ( $element [ '#name' ], array ( ' ' => '-' , '_' => '-' , '[' => '-' , ']' => '' ));
2009-06-14 08:04:25 +00:00
}
2010-07-31 04:01:51 +00:00
// Add a class for disabled elements to facilitate cross-browser styling.
if ( ! empty ( $element [ '#attributes' ][ 'disabled' ])) {
$attributes [ 'class' ][] = 'form-disabled' ;
}
2012-09-04 13:32:47 +00:00
$output = '<div' . new Attribute ( $attributes ) . '>' . " \n " ;
2009-06-14 08:04:25 +00:00
2009-12-02 15:09:16 +00:00
// If #title is not set, we don't display any label or required marker.
if ( ! isset ( $element [ '#title' ])) {
$element [ '#title_display' ] = 'none' ;
}
2010-04-28 16:11:22 +00:00
$prefix = isset ( $element [ '#field_prefix' ]) ? '<span class="field-prefix">' . $element [ '#field_prefix' ] . '</span> ' : '' ;
$suffix = isset ( $element [ '#field_suffix' ]) ? ' <span class="field-suffix">' . $element [ '#field_suffix' ] . '</span>' : '' ;
2010-03-11 22:46:32 +00:00
2009-12-02 15:09:16 +00:00
switch ( $element [ '#title_display' ]) {
case 'before' :
2010-10-01 22:28:55 +00:00
case 'invisible' :
2009-12-02 15:09:16 +00:00
$output .= ' ' . theme ( 'form_element_label' , $variables );
2010-04-28 16:11:22 +00:00
$output .= ' ' . $prefix . $element [ '#children' ] . $suffix . " \n " ;
2009-12-02 15:09:16 +00:00
break ;
case 'after' :
2010-04-28 16:11:22 +00:00
$output .= ' ' . $prefix . $element [ '#children' ] . $suffix ;
2009-12-02 15:09:16 +00:00
$output .= ' ' . theme ( 'form_element_label' , $variables ) . " \n " ;
break ;
2006-05-02 09:26:33 +00:00
2009-12-02 15:09:16 +00:00
case 'none' :
case 'attribute' :
// Output no label and no required marker, only the children.
2010-04-28 16:11:22 +00:00
$output .= ' ' . $prefix . $element [ '#children' ] . $suffix . " \n " ;
2009-12-02 15:09:16 +00:00
break ;
}
2006-05-02 09:26:33 +00:00
if ( ! empty ( $element [ '#description' ])) {
2012-04-05 07:09:10 +00:00
$attributes = array ( 'class' => 'description' );
if ( ! empty ( $element [ '#id' ])) {
$attributes [ 'id' ] = $element [ '#id' ] . '--description' ;
}
2012-09-04 13:32:47 +00:00
$output .= '<div' . new Attribute ( $attributes ) . '>' . $element [ '#description' ] . " </div> \n " ;
2006-05-02 09:26:33 +00:00
}
$output .= " </div> \n " ;
return $output ;
2005-10-07 06:11:12 +00:00
}
2006-01-24 10:15:03 +00:00
2009-11-02 03:00:28 +00:00
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a marker for required form elements .
2009-11-02 03:00:28 +00:00
*
* @ param $variables
* An associative array containing :
* - element : An associative array containing the properties of the element .
*
* @ ingroup themeable
*/
function theme_form_required_marker ( $variables ) {
$attributes = array (
'class' => 'form-required' ,
2013-06-17 13:35:07 +00:00
'title' => t ( 'This field is required.' ),
2009-11-02 03:00:28 +00:00
);
2012-09-04 13:32:47 +00:00
return '<abbr' . new Attribute ( $attributes ) . '>*</abbr>' ;
2009-11-02 03:00:28 +00:00
}
2009-12-02 15:09:16 +00:00
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a form element label and required marker .
2009-12-02 15:09:16 +00:00
*
* Form element labels include the #title and a #required marker. The label is
* associated with the element itself by the element #id. Labels may appear
2012-02-19 03:41:24 +00:00
* before or after elements , depending on theme_form_element () and
* #title_display.
2009-12-02 15:09:16 +00:00
*
* This function will not be called for elements with no labels , depending on
* #title_display. For elements that have an empty #title and are not required,
* this function will output no label ( '' ) . For required elements that have an
* empty #title, this will output the required marker alone within the label.
* The label will use the #id to associate the marker with the field that is
* required . That is especially important for screenreader users to know
* which field is required .
*
* @ param $variables
* An associative array containing :
* - element : An associative array containing the properties of the element .
* Properties used : #required, #title, #id, #value, #description.
*
* @ ingroup themeable
*/
function theme_form_element_label ( $variables ) {
$element = $variables [ 'element' ];
// If title and required marker are both empty, output no label.
2011-12-12 16:21:23 +00:00
if (( ! isset ( $element [ '#title' ]) || $element [ '#title' ] === '' ) && empty ( $element [ '#required' ])) {
2009-12-02 15:09:16 +00:00
return '' ;
}
// If the element is required, a required marker is appended to the label.
$required = ! empty ( $element [ '#required' ]) ? theme ( 'form_required_marker' , array ( 'element' => $element )) : '' ;
$title = filter_xss_admin ( $element [ '#title' ]);
$attributes = array ();
2010-04-07 04:39:59 +00:00
// Style the label as class option to display inline with the element.
2009-12-02 15:09:16 +00:00
if ( $element [ '#title_display' ] == 'after' ) {
$attributes [ 'class' ] = 'option' ;
}
2010-04-07 04:39:59 +00:00
// Show label only to screen readers to avoid disruption in visual flows.
elseif ( $element [ '#title_display' ] == 'invisible' ) {
2013-06-17 19:58:27 +00:00
$attributes [ 'class' ] = 'visually-hidden' ;
2010-04-07 04:39:59 +00:00
}
2009-12-02 15:09:16 +00:00
if ( ! empty ( $element [ '#id' ])) {
$attributes [ 'for' ] = $element [ '#id' ];
}
2013-06-17 13:35:07 +00:00
return '<label' . new Attribute ( $attributes ) . '>' . t ( '!title!required' , array ( '!title' => $title , '!required' => $required )) . '</label>' ;
2009-12-02 15:09:16 +00:00
}
2006-04-05 18:12:48 +00:00
/**
* Sets a form element ' s class attribute .
*
* Adds 'required' and 'error' classes as needed .
*
2011-05-08 19:50:38 +00:00
* @ param $element
2007-01-23 19:17:55 +00:00
* The form element .
2006-04-05 18:12:48 +00:00
* @ param $name
2007-01-23 19:17:55 +00:00
* Array of new class names to be added .
2006-04-05 18:12:48 +00:00
*/
2012-10-23 22:43:41 +00:00
function _form_set_attributes ( & $element , $class = array ()) {
2009-08-22 14:34:23 +00:00
if ( ! empty ( $class )) {
if ( ! isset ( $element [ '#attributes' ][ 'class' ])) {
$element [ '#attributes' ][ 'class' ] = array ();
}
$element [ '#attributes' ][ 'class' ] = array_merge ( $element [ '#attributes' ][ 'class' ], $class );
}
2010-09-16 20:14:49 +00:00
// This function is invoked from form element theme functions, but the
// rendered form element may not necessarily have been processed by
// form_builder().
if ( ! empty ( $element [ '#required' ])) {
2009-08-22 14:34:23 +00:00
$element [ '#attributes' ][ 'class' ][] = 'required' ;
2012-03-27 06:23:35 +00:00
$element [ '#attributes' ][ 'required' ] = 'required' ;
$element [ '#attributes' ][ 'aria-required' ] = 'true' ;
2006-04-05 18:12:48 +00:00
}
2013-03-14 10:53:22 +00:00
if ( isset ( $element [ '#parents' ]) && form_get_error ( $element ) !== NULL && ! empty ( $element [ '#validated' ])) {
2009-08-22 14:34:23 +00:00
$element [ '#attributes' ][ 'class' ][] = 'error' ;
2006-04-05 18:12:48 +00:00
}
2005-10-07 06:11:12 +00:00
}
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
/**
* Triggers kernel . response and sends a form response .
*
* @ deprecated This function is to be used internally by Form API only .
*
* @ param \Symfony\Component\HttpFoundation\Response $response
* A response object .
*/
function _drupal_form_send_response ( Response $response ) {
$request = Drupal :: request ();
$kernel = Drupal :: service ( 'http_kernel' );
$event = new FilterResponseEvent ( $kernel , $request , $kernel :: MASTER_REQUEST , $response );
Drupal :: service ( 'event_dispatcher' ) -> dispatch ( KernelEvents :: RESPONSE , $event );
// Prepare and send the response.
$event -> getResponse ()
-> prepare ( $request )
-> send ();
$kernel -> terminate ( $request , $response );
exit ;
}
2005-10-07 06:11:12 +00:00
/**
2008-01-21 15:17:01 +00:00
* @ } End of " defgroup form_api " .
2005-10-07 06:11:12 +00:00
*/
2007-05-04 09:41:37 +00:00
/**
* @ defgroup batch Batch operations
* @ {
2012-02-19 03:41:24 +00:00
* Creates and processes batch operations .
2011-01-03 18:03:54 +00:00
*
2007-05-04 09:41:37 +00:00
* Functions allowing forms processing to be spread out over several page
* requests , thus ensuring that the processing does not get interrupted
* because of a PHP timeout , while allowing the user to receive feedback
* on the progress of the ongoing operations .
*
* The API is primarily designed to integrate nicely with the Form API
2010-06-15 15:52:04 +00:00
* workflow , but can also be used by non - Form API scripts ( like update . php )
2007-05-04 09:41:37 +00:00
* or even simple page callbacks ( which should probably be used sparingly ) .
*
* Example :
* @ code
* $batch = array (
* 'title' => t ( 'Exporting' ),
* 'operations' => array (
2013-07-11 17:29:02 +00:00
* array ( 'my_function_1' , array ( $account -> id (), 'story' )),
2007-05-04 09:41:37 +00:00
* array ( 'my_function_2' , array ()),
* ),
* 'finished' => 'my_finished_callback' ,
2009-11-07 14:44:04 +00:00
* 'file' => 'path_to_file_containing_myfunctions' ,
2007-05-04 09:41:37 +00:00
* );
* batch_set ( $batch );
2011-07-28 19:29:53 +00:00
* // Only needed if not inside a form _submit handler.
* // Setting redirect in batch_process.
* batch_process ( 'node/1' );
2007-05-04 09:41:37 +00:00
* @ endcode
*
2009-11-07 14:44:04 +00:00
* Note : if the batch 'title' , 'init_message' , 'progress_message' , or
* 'error_message' could contain any user input , it is the responsibility of
2008-10-14 11:01:08 +00:00
* the code calling batch_set () to sanitize them first with a function like
2011-05-01 10:37:37 +00:00
* check_plain () or filter_xss () . Furthermore , if the batch operation
* returns any user input in the 'results' or 'message' keys of $context ,
* it must also sanitize them first .
2008-10-14 11:01:08 +00:00
*
2007-05-04 09:41:37 +00:00
* Sample batch operations :
* @ code
* // Simple and artificial: load a node of a given type for a given user
* function my_function_1 ( $uid , $type , & $context ) {
* // The $context array gathers batch context information about the execution (read),
* // as well as 'return values' for the current operation (write)
* // The following keys are provided :
* // 'results' (read / write): The array of results gathered so far by
2007-07-02 14:41:37 +00:00
* // the batch processing, for the current operation to append its own.
2007-05-04 09:41:37 +00:00
* // 'message' (write): A text message displayed in the progress page.
* // The following keys allow for multi-step operations :
* // 'sandbox' (read / write): An array that can be freely used to
* // store persistent data between iterations. It is recommended to
* // use this instead of $_SESSION, which is unsafe if the user
* // continues browsing in a separate window while the batch is processing.
* // 'finished' (write): A float number between 0 and 1 informing
* // the processing engine of the completion level for the operation.
2007-05-07 10:15:57 +00:00
* // 1 (or no value explicitly set) means the operation is finished
* // and the batch processing can continue to the next operation.
*
2012-08-21 15:38:04 +00:00
* $nodes = entity_load_multiple_by_properties ( 'node' , array ( 'uid' => $uid , 'type' => $type ));
* $node = reset ( $nodes );
2013-07-20 12:21:43 +00:00
* $context [ 'results' ][] = $node -> id () . ' : ' . check_plain ( $node -> label ());
2012-07-22 18:07:00 +00:00
* $context [ 'message' ] = check_plain ( $node -> label ());
2007-05-04 09:41:37 +00:00
* }
*
2013-05-26 20:18:10 +00:00
* // A more advanced example is a multi-step operation that loads all rows,
* // five by five.
2007-05-04 09:41:37 +00:00
* function my_function_2 ( & $context ) {
* if ( empty ( $context [ 'sandbox' ])) {
* $context [ 'sandbox' ][ 'progress' ] = 0 ;
2013-05-26 20:18:10 +00:00
* $context [ 'sandbox' ][ 'current_id' ] = 0 ;
* $context [ 'sandbox' ][ 'max' ] = db_query ( 'SELECT COUNT(DISTINCT id) FROM {example}' ) -> fetchField ();
2007-05-04 09:41:37 +00:00
* }
* $limit = 5 ;
2013-05-26 20:18:10 +00:00
* $result = db_select ( 'example' )
* -> fields ( 'example' , array ( 'id' ))
* -> condition ( 'id' , $context [ 'sandbox' ][ 'current_id' ], '>' )
* -> orderBy ( 'id' )
2009-03-14 16:27:58 +00:00
* -> range ( 0 , $limit )
* -> execute ();
* foreach ( $result as $row ) {
2013-05-26 20:18:10 +00:00
* $context [ 'results' ][] = $row -> id . ' : ' . check_plain ( $row -> title );
2007-05-04 09:41:37 +00:00
* $context [ 'sandbox' ][ 'progress' ] ++ ;
2013-05-26 20:18:10 +00:00
* $context [ 'sandbox' ][ 'current_id' ] = $row -> id ;
* $context [ 'message' ] = check_plain ( $row -> title );
2007-05-04 09:41:37 +00:00
* }
* if ( $context [ 'sandbox' ][ 'progress' ] != $context [ 'sandbox' ][ 'max' ]) {
* $context [ 'finished' ] = $context [ 'sandbox' ][ 'progress' ] / $context [ 'sandbox' ][ 'max' ];
* }
* }
* @ endcode
*
* Sample 'finished' callback :
* @ code
* function batch_test_finished ( $success , $results , $operations ) {
2011-06-01 08:39:51 +00:00
* // The 'success' parameter means no fatal PHP errors were detected. All
* // other error management should be handled using 'results'.
2007-05-04 09:41:37 +00:00
* if ( $success ) {
2007-11-12 19:06:33 +00:00
* $message = format_plural ( count ( $results ), 'One post processed.' , '@count posts processed.' );
2007-05-04 09:41:37 +00:00
* }
* else {
* $message = t ( 'Finished with an error.' );
* }
* drupal_set_message ( $message );
2007-07-02 14:41:37 +00:00
* // Providing data for the redirected page is done through $_SESSION.
2007-05-04 09:41:37 +00:00
* foreach ( $results as $result ) {
* $items [] = t ( 'Loaded node %title.' , array ( '%title' => $result ));
* }
2009-06-02 06:58:17 +00:00
* $_SESSION [ 'my_batch_results' ] = $items ;
2007-05-04 09:41:37 +00:00
* }
* @ endcode
*/
/**
2012-04-10 17:58:43 +00:00
* Adds a new batch .
*
* Batch operations are added as new batch sets . Batch sets are used to spread
* processing ( primarily , but not exclusively , forms processing ) over several
* page requests . This helps to ensure that the processing is not interrupted
* due to PHP timeouts , while users are still able to receive feedback on the
* progress of the ongoing operations . Combining related operations into
* distinct batch sets provides clean code independence for each batch set ,
* ensuring that two or more batches , submitted independently , can be processed
* without mutual interference . Each batch set may specify its own set of
* operations and results , produce its own UI messages , and trigger its own
* 'finished' callback . Batch sets are processed sequentially , with the progress
* bar starting afresh for each new set .
*
* @ param $batch_definition
* An associative array defining the batch , with the following elements ( all
* are optional except as noted ) :
* - operations : ( required ) Array of function calls to be performed .
2009-11-07 14:44:04 +00:00
* Example :
* @ code
* array (
* array ( 'my_function_1' , array ( $arg1 )),
* array ( 'my_function_2' , array ( $arg2_1 , $arg2_2 )),
* )
* @ endcode
2012-04-10 17:58:43 +00:00
* - title : A safe , translated string to use as the title for the progress
* page . Defaults to t ( 'Processing' ) .
* - init_message : Message displayed while the processing is initialized .
2009-11-07 14:44:04 +00:00
* Defaults to t ( 'Initializing.' ) .
2012-04-10 17:58:43 +00:00
* - progress_message : Message displayed while processing the batch . Available
* placeholders are @ current , @ remaining , @ total , @ percentage , @ estimate and
* @ elapsed . Defaults to t ( 'Completed @current of @total.' ) .
* - error_message : Message displayed if an error occurred while processing
2009-11-07 14:44:04 +00:00
* the batch . Defaults to t ( 'An error has occurred.' ) .
2012-04-10 17:58:43 +00:00
* - finished : Name of a function to be executed after the batch has
* completed . This should be used to perform any result massaging that may
* be needed , and possibly save data in $_SESSION for display after final
* page redirection .
* - file : Path to the file containing the definitions of the 'operations' and
* 'finished' functions , for instance if they don ' t reside in the main
* . module file . The path should be relative to base_path (), and thus should
* be built using drupal_get_path () .
* - css : Array of paths to CSS files to be used on the progress page .
* - url_options : options passed to url () when constructing redirect URLs for
* the batch .
2007-05-04 09:41:37 +00:00
*/
function batch_set ( $batch_definition ) {
if ( $batch_definition ) {
$batch =& batch_get ();
2010-01-08 06:36:34 +00:00
// Initialize the batch if needed.
2007-05-04 09:41:37 +00:00
if ( empty ( $batch )) {
$batch = array (
'sets' => array (),
2010-01-08 06:36:34 +00:00
'has_form_submits' => FALSE ,
2007-05-04 09:41:37 +00:00
);
}
2010-01-08 06:36:34 +00:00
// Base and default properties for the batch set.
2007-05-04 09:41:37 +00:00
$init = array (
'sandbox' => array (),
'results' => array (),
'success' => FALSE ,
2010-01-08 06:36:34 +00:00
'start' => 0 ,
2009-01-12 06:23:57 +00:00
'elapsed' => 0 ,
2007-05-04 09:41:37 +00:00
);
$defaults = array (
2013-06-17 13:35:07 +00:00
'title' => t ( 'Processing' ),
'init_message' => t ( 'Initializing.' ),
'progress_message' => t ( 'Completed @current of @total.' ),
'error_message' => t ( 'An error has occurred.' ),
2008-06-24 21:51:03 +00:00
'css' => array (),
2007-05-04 09:41:37 +00:00
);
$batch_set = $init + $batch_definition + $defaults ;
2010-01-08 06:36:34 +00:00
// Tweak init_message to avoid the bottom of the page flickering down after
// init phase.
2007-05-04 09:41:37 +00:00
$batch_set [ 'init_message' ] .= '<br/> ' ;
2010-01-08 06:36:34 +00:00
// The non-concurrent workflow of batch execution allows us to save
// numberOfItems() queries by handling our own counter.
2007-05-04 09:41:37 +00:00
$batch_set [ 'total' ] = count ( $batch_set [ 'operations' ]);
2010-01-08 06:36:34 +00:00
$batch_set [ 'count' ] = $batch_set [ 'total' ];
2007-05-04 09:41:37 +00:00
2010-01-08 06:36:34 +00:00
// Add the set to the batch.
if ( empty ( $batch [ 'id' ])) {
// The batch is not running yet. Simply add the new set.
$batch [ 'sets' ][] = $batch_set ;
2007-05-04 09:41:37 +00:00
}
else {
2010-01-08 06:36:34 +00:00
// The set is being added while the batch is running. Insert the new set
// right after the current one to ensure execution order, and store its
// operations in a queue.
$index = $batch [ 'current_set' ] + 1 ;
$slice1 = array_slice ( $batch [ 'sets' ], 0 , $index );
$slice2 = array_slice ( $batch [ 'sets' ], $index );
$batch [ 'sets' ] = array_merge ( $slice1 , array ( $batch_set ), $slice2 );
_batch_populate_queue ( $batch , $index );
2007-05-04 09:41:37 +00:00
}
}
}
/**
2009-11-07 14:44:04 +00:00
* Processes the batch .
2007-05-06 05:47:52 +00:00
*
2008-10-15 14:17:27 +00:00
* This function is generally not needed in form submit handlers ;
* Form API takes care of batches that were set during form submission .
2007-05-04 09:41:37 +00:00
*
* @ param $redirect
* ( optional ) Path to redirect to when the batch has finished processing .
* @ param $url
2007-07-02 14:41:37 +00:00
* ( optional - should only be used for separate scripts like update . php )
2007-05-04 09:41:37 +00:00
* URL of the batch processing page .
2009-10-03 20:17:46 +00:00
* @ param $redirect_callback
* ( optional ) Specify a function to be called to redirect to the progressive
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
* processing page .
*
* @ return \Symfony\Component\HttpFoundation\RedirectResponse | null
* A redirect response if the batch is progressive . No return value otherwise .
2007-05-04 09:41:37 +00:00
*/
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
function batch_process ( $redirect = NULL , $url = 'batch' , $redirect_callback = NULL ) {
2007-05-04 09:41:37 +00:00
$batch =& batch_get ();
2009-09-11 04:09:26 +00:00
drupal_theme_initialize ();
2010-01-02 23:30:53 +00:00
2007-05-04 09:41:37 +00:00
if ( isset ( $batch )) {
// Add process information
$process_info = array (
'current_set' => 0 ,
'progressive' => TRUE ,
2009-10-03 20:17:46 +00:00
'url' => $url ,
2009-10-27 04:12:39 +00:00
'url_options' => array (),
2012-04-29 15:16:27 +00:00
'source_url' => current_path (),
2007-05-04 09:41:37 +00:00
'redirect' => $redirect ,
2009-09-11 04:09:26 +00:00
'theme' => $GLOBALS [ 'theme_key' ],
2009-10-03 20:17:46 +00:00
'redirect_callback' => $redirect_callback ,
2007-05-04 09:41:37 +00:00
);
$batch += $process_info ;
2010-01-08 06:36:34 +00:00
// The batch is now completely built. Allow other modules to make changes
// to the batch so that it is easier to reuse batch processes in other
2010-01-25 10:38:35 +00:00
// environments.
2009-10-03 20:17:46 +00:00
drupal_alter ( 'batch' , $batch );
2010-01-08 06:36:34 +00:00
// Assign an arbitrary id: don't rely on a serial column in the 'batch'
// table, since non-progressive batches skip database storage completely.
$batch [ 'id' ] = db_next_id ();
// Move operations to a job queue. Non-progressive batches will use a
// memory-based queue.
foreach ( $batch [ 'sets' ] as $key => $batch_set ) {
_batch_populate_queue ( $batch , $key );
}
// Initiate processing.
2007-05-04 09:41:37 +00:00
if ( $batch [ 'progressive' ]) {
2010-01-08 06:36:34 +00:00
// Now that we have a batch id, we can generate the redirection link in
// the generic error message.
2013-06-17 13:35:07 +00:00
$batch [ 'error_message' ] = t ( 'Please continue to <a href="@error_url">the error page</a>' , array ( '@error_url' => url ( $url , array ( 'query' => array ( 'id' => $batch [ 'id' ], 'op' => 'finished' )))));
2010-01-08 06:36:34 +00:00
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
// Clear the way for the redirection to the batch processing page, by
// saving and unsetting the 'destination', if there is any.
$request = Drupal :: request ();
if ( $request -> query -> has ( 'destination' )) {
$batch [ 'destination' ] = $request -> query -> get ( 'destination' );
$request -> query -> remove ( 'destination' );
2007-05-04 09:41:37 +00:00
}
2007-07-20 05:44:13 +00:00
2010-01-08 06:36:34 +00:00
// Store the batch.
2013-05-27 18:12:50 +00:00
Drupal :: service ( 'batch.storage' ) -> create ( $batch );
2007-07-20 05:44:13 +00:00
2009-06-02 06:58:17 +00:00
// Set the batch number in the session to guarantee that it will stay alive.
$_SESSION [ 'batches' ][ $batch [ 'id' ]] = TRUE ;
2010-01-08 06:36:34 +00:00
// Redirect for processing.
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
$options = array ( 'query' => array ( 'op' => 'start' , 'id' => $batch [ 'id' ]));
if (( $function = $batch [ 'redirect_callback' ]) && function_exists ( $function )) {
$function ( $batch [ 'url' ], $options );
}
else {
$options [ 'absolute' ] = TRUE ;
return new RedirectResponse ( url ( $batch [ 'url' ], $options ));
}
2007-05-04 09:41:37 +00:00
}
else {
// Non-progressive execution: bypass the whole progressbar workflow
// and execute the batch in one pass.
2013-05-09 09:25:10 +00:00
require_once __DIR__ . '/batch.inc' ;
2007-05-04 09:41:37 +00:00
_batch_process ();
}
}
}
/**
2009-11-07 14:44:04 +00:00
* Retrieves the current batch .
2007-05-04 09:41:37 +00:00
*/
function & batch_get () {
2009-11-21 14:06:46 +00:00
// Not drupal_static(), because Batch API operates at a lower level than most
// use-cases for resetting static variables, and we specifically do not want a
// global drupal_static_reset() resetting the batch information. Functions
// that are part of the Batch API and need to reset the batch information may
// call batch_get() and manipulate the result by reference. Functions that are
// not part of the Batch API can also do this, but shouldn't.
static $batch = array ();
2007-05-04 09:41:37 +00:00
return $batch ;
}
2010-01-08 06:36:34 +00:00
/**
* Populates a job queue with the operations of a batch set .
*
2012-03-12 03:07:39 +00:00
* Depending on whether the batch is progressive or not , the
* Drupal\Core\Queue\Batch or Drupal\Core\Queue\BatchMemory handler classes will
* be used .
2010-01-08 06:36:34 +00:00
*
* @ param $batch
* The batch array .
* @ param $set_id
* The id of the set to process .
2012-02-19 03:41:24 +00:00
*
2010-01-08 06:36:34 +00:00
* @ return
* The name and class of the queue are added by reference to the batch set .
*/
function _batch_populate_queue ( & $batch , $set_id ) {
$batch_set = & $batch [ 'sets' ][ $set_id ];
if ( isset ( $batch_set [ 'operations' ])) {
$batch_set += array (
'queue' => array (
'name' => 'drupal_batch:' . $batch [ 'id' ] . ':' . $set_id ,
2012-03-12 03:07:39 +00:00
'class' => $batch [ 'progressive' ] ? 'Drupal\Core\Queue\Batch' : 'Drupal\Core\Queue\BatchMemory' ,
2010-01-08 06:36:34 +00:00
),
);
$queue = _batch_queue ( $batch_set );
$queue -> createQueue ();
foreach ( $batch_set [ 'operations' ] as $operation ) {
$queue -> createItem ( $operation );
}
unset ( $batch_set [ 'operations' ]);
}
}
/**
* Returns a queue object for a batch set .
*
* @ param $batch_set
* The batch set .
2012-02-19 03:41:24 +00:00
*
2010-01-08 06:36:34 +00:00
* @ return
* The queue object .
*/
function _batch_queue ( $batch_set ) {
static $queues ;
2010-09-26 23:31:36 +00:00
if ( ! isset ( $queues )) {
2010-01-08 06:36:34 +00:00
$queues = array ();
}
if ( isset ( $batch_set [ 'queue' ])) {
$name = $batch_set [ 'queue' ][ 'name' ];
$class = $batch_set [ 'queue' ][ 'class' ];
if ( ! isset ( $queues [ $class ][ $name ])) {
2013-01-15 15:35:31 +00:00
$queues [ $class ][ $name ] = new $class ( $name , Database :: getConnection ());
2010-01-08 06:36:34 +00:00
}
return $queues [ $class ][ $name ];
}
}
2007-05-04 09:41:37 +00:00
/**
* @ } End of " defgroup batch " .
*/