2005-10-07 06:11:12 +00:00
< ? php
2005-11-01 09:58:01 +00:00
// $Id$
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 :
*
* \ @ ingroup forms
* \ @ see user_pass_validate () .
* \ @ see user_pass_submit () .
*
* @ } End of " defgroup forms " .
*/
/**
* @ 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 .
*
2009-05-24 17:39:35 +00:00
* The drupal_get_form () function handles retrieving and processing an HTML
2009-05-12 08:37:45 +00:00
* form for modules automatically . For example :
2006-08-18 18:58:47 +00:00
*
2007-11-09 07:40:55 +00:00
* @ code
2007-01-23 19:17:55 +00:00
* // Display the user registration form.
2006-08-18 18:58:47 +00:00
* $output = drupal_get_form ( 'user_register' );
2007-11-09 07:40:55 +00:00
* @ endcode
2006-08-18 18:58:47 +00:00
*
* Forms can also be built and submitted programmatically without any user input
2009-04-29 07:18:04 +00:00
* using the drupal_form_submit () function .
2006-08-18 18:58:47 +00:00
*
* For information on the format of the structured arrays used to define forms ,
2007-03-07 17:31:40 +00:00
* and more detailed explanations of the Form API workflow , see the
2007-11-09 07:40:55 +00:00
* @ link http :// api . drupal . org / api / file / developer / topics / forms_api_reference . html reference @ endlink
* and the @ link http :// api . drupal . org / api / file / developer / topics / forms_api . html quickstart guide . @ endlink
2005-10-07 06:11:12 +00:00
*/
/**
2009-03-14 20:13:27 +00:00
* Wrapper for drupal_build_form () for use when $form_state is not needed .
2005-10-07 06:11:12 +00:00
*
* @ param $form_id
2009-03-14 20:13:27 +00:00
* 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 (),
* search_forms (), and user_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
* when it is called .
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
*/
function drupal_get_form ( $form_id ) {
2009-03-14 20:13:27 +00:00
$form_state = array ();
2007-05-14 13:43:38 +00:00
$args = func_get_args ();
2009-03-14 20:13:27 +00:00
// Remove $form_id from the arguments.
array_shift ( $args );
$form_state [ 'args' ] = $args ;
return drupal_build_form ( $form_id , $form_state );
}
/**
2009-05-12 08:37:45 +00:00
* Build and process a form based on a 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 (),
* search_forms (), and user_forms () .
* @ param & $form_state
* An array which stores information about the form . This is passed as a
* reference so that the caller can use it to examine what the form changed
* when the form submission process is complete .
*
* The following parameters may be set in $form_state to affect how the form
* is rendered :
* - args : An array of arguments to pass to the form builder .
* - input : An array of input that corresponds to $_POST or $_GET , depending
* on the 'method' chosen ( see below ) .
* - 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
* forms that do not change data , as that is exclusively the domain of post .
* - no_redirect : If set to TRUE the form will NOT perform a drupal_goto (),
* even if a redirect is set .
* - 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 .
* - must_validate : Ordinarily , a form is only validated once but there are
* times when a form is resubmitted internally and should be validated
* again . Setting this to TRUE will force that to happen . This is most
* likely to occur during AHAH or AJAX operations .
* @ return
* The rendered form or NULL , depending upon the $form_state flags that were set .
*/
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-11-19 19:23:28 +00:00
$cacheable = FALSE ;
2007-05-14 13:43:38 +00:00
if ( isset ( $_SESSION [ 'batch_form_state' ])) {
// We've been redirected here after a batch processing : the form has
// already been processed, so we grab the post-process $form_state value
// and move on to form display. See _batch_finished() function.
$form_state = $_SESSION [ 'batch_form_state' ];
unset ( $_SESSION [ 'batch_form_state' ]);
2006-08-25 08:15:24 +00:00
}
else {
2009-03-14 20:13:27 +00:00
// If the incoming input contains a form_build_id, we'll check the
2007-05-14 13:43:38 +00:00
// 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.
2009-03-14 20:13:27 +00:00
if ( isset ( $form_state [ 'input' ][ 'form_id' ]) && $form_state [ 'input' ][ 'form_id' ] == $form_id && ! empty ( $form_state [ 'input' ][ 'form_build_id' ])) {
$form = form_get_cache ( $form_state [ 'input' ][ 'form_build_id' ], $form_state );
2006-08-25 08:15:24 +00:00
}
2007-05-14 13:43:38 +00:00
// If the previous bit of code didn't result in a populated $form
// object, we're hitting the form for the first time and we need
// to build it from scratch.
if ( ! isset ( $form )) {
2009-03-14 20:13:27 +00:00
$form = drupal_retrieve_form ( $form_id , $form_state );
2008-10-11 04:06:29 +00:00
$form_build_id = 'form-' . md5 ( uniqid ( mt_rand (), TRUE ));
2006-08-25 08:15:24 +00:00
$form [ '#build_id' ] = $form_build_id ;
2009-03-14 20:13:27 +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' ;
}
2007-05-14 13:43:38 +00:00
drupal_prepare_form ( $form_id , $form , $form_state );
2007-11-19 19:23:28 +00:00
// Store a copy of the unprocessed form for caching and indicate that it
// is cacheable if #cache will be set.
$original_form = $form ;
$cacheable = TRUE ;
2006-08-25 08:15:24 +00:00
}
2006-08-18 18:58:47 +00:00
2007-05-14 13:43:38 +00:00
// Now that we know we have a form, we'll process it (validating,
// submitting, and handling the results returned by its submission
// handlers. Submit handlers accumulate data in the form_state by
// altering the $form_state variable, which is passed into them by
// reference.
drupal_process_form ( $form_id , $form , $form_state );
2009-03-14 20:13:27 +00:00
if ( $cacheable && ! empty ( $form [ '#cache' ]) && empty ( $form [ '#no_cache' ])) {
2007-11-19 19:23:28 +00:00
// Caching is done past drupal_process_form so #process callbacks can
2009-04-22 09:12:44 +00:00
// set #cache.
form_set_cache ( $form_build_id , $original_form , $form_state );
2007-11-19 19:23:28 +00:00
}
2007-05-14 13:43:38 +00:00
}
// Most simple, single-step forms will be finished by this point --
// drupal_process_form() usually redirects to another page (or to
// a 'fresh' copy of the form) once processing is complete. If one
// of the form's handlers has set $form_state['redirect'] to FALSE,
// the form will simply be re-rendered with the values still in its
// fields.
//
2009-04-22 09:12:44 +00:00
// If $form_state['storage'] or $form_state['rebuild'] has been set
// and the form has been submitted, we know that we're in a complex
// multi-part process of some sort and the form's workflow is NOT
// complete. We need to construct a fresh copy of the form, passing
// in the latest $form_state in addition to any other variables passed
// into drupal_get_form().
if (( ! empty ( $form_state [ 'storage' ]) || ! empty ( $form_state [ 'rebuild' ])) && ! empty ( $form_state [ 'submitted' ]) && ! form_get_errors ()) {
2009-03-14 20:13:27 +00:00
$form = drupal_rebuild_form ( $form_id , $form_state );
2006-12-06 15:49:45 +00:00
}
2009-05-24 17:39:35 +00:00
2009-05-12 08:37:45 +00:00
// Don't override #theme if someone already set it.
if ( ! isset ( $form [ '#theme' ])) {
init_theme ();
$registry = theme_get_registry ();
if ( isset ( $registry [ $form_id ])) {
$form [ '#theme' ] = $form_id ;
}
}
2006-12-06 15:49:45 +00:00
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
/**
* Retrieve default values for the $form_state array .
*/
function form_state_defaults () {
return array (
'storage' => NULL ,
'submitted' => FALSE ,
'method' => 'post' ,
'programmed' => FALSE ,
2009-04-11 22:19:46 +00:00
'groups' => array (),
2009-03-14 20:13:27 +00:00
);
}
2007-11-19 19:23:28 +00:00
/**
* Retrieves a form , caches it and processes it with an empty $_POST .
*
* This function clears $_POST and passes the empty $_POST to the form_builder .
* To preserve some parts from $_POST , pass them in $form_state .
*
* If your AHAH callback simulates the pressing of a button , then your AHAH
* callback will need to do the same as what drupal_get_form would do when the
* button is pressed : get the form from the cache , run drupal_process_form over
* it and then if it needs rebuild , run drupal_rebuild_form over it . Then send
* back a part of the returned form .
* $form_state [ 'clicked_button' ][ '#array_parents' ] will help you to find which
* part .
*
2009-03-14 20:13:27 +00:00
* When getting a form from the cache , the $form_id must be shifted off from
* $form [ '#args' ], so the resulting array can be given to $form_state [ 'args' ] .
*
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
* may be found in node_forms (), search_forms (), and user_forms () .
* @ param $form_state
* A keyed array containing the current state of the form . Most
* important is the $form_state [ 'storage' ] collection .
* @ param $form_build_id
* If the AHAH callback calling this function only alters part of the form ,
* then pass in the existing form_build_id so we can re - cache with the same
* csid .
* @ return
* The newly built form .
*/
2009-03-14 20:13:27 +00:00
function drupal_rebuild_form ( $form_id , & $form_state , $form_build_id = NULL ) {
$form = drupal_retrieve_form ( $form_id , $form_state );
2007-11-19 19:23:28 +00:00
if ( ! isset ( $form_build_id )) {
// We need a new build_id for the new version of the form.
2008-04-14 17:48:46 +00:00
$form_build_id = 'form-' . md5 ( mt_rand ());
2007-11-19 19:23:28 +00:00
}
$form [ '#build_id' ] = $form_build_id ;
drupal_prepare_form ( $form_id , $form , $form_state );
2009-03-14 20:13:27 +00:00
if ( empty ( $form [ '#no_cache' ])) {
// We cache the form structure so it can be retrieved later for validation.
// If $form_state['storage'] is populated, we also cache it so that it can
// be used to resume complex multi-step processes.
form_set_cache ( $form_build_id , $form , $form_state );
}
2007-11-19 19:23:28 +00:00
// Clear out all post data, as we don't want the previous step's
// data to pollute this one and trigger validate/submit handling,
// then process the form for rendering.
2009-03-14 20:13:27 +00:00
$form_state [ 'input' ] = array ();
2009-04-11 22:19:46 +00:00
// Also clear out all group associations as these might be different
// when rerendering the form.
$form_state [ 'groups' ] = array ();
2009-03-14 20:13:27 +00:00
// Do not call drupal_process_form(), since it would prevent the rebuilt form
// to submit.
$form = form_builder ( $form_id , $form , $form_state );
2007-11-19 19:23:28 +00:00
return $form ;
}
2007-07-29 17:28:23 +00:00
/**
* Fetch a form from cache .
*/
function form_get_cache ( $form_build_id , & $form_state ) {
2008-04-14 17:48:46 +00:00
if ( $cached = cache_get ( 'form_' . $form_build_id , 'cache_form' )) {
2007-07-29 17:28:23 +00:00
$form = $cached -> data ;
2008-10-11 04:06:29 +00:00
global $user ;
if (( isset ( $form [ '#cache_token' ]) && drupal_valid_token ( $form [ '#cache_token' ])) || ( ! isset ( $form [ '#cache_token' ]) && ! $user -> uid )) {
if ( $cached = cache_get ( 'storage_' . $form_build_id , 'cache_form' )) {
$form_state [ 'storage' ] = $cached -> data ;
}
return $form ;
2007-07-29 17:28:23 +00:00
}
}
}
/**
* Store a form in the cache
*/
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 ;
2008-10-11 04:06:29 +00:00
global $user ;
if ( $user -> uid ) {
$form [ '#cache_token' ] = drupal_get_token ();
}
2008-09-17 07:11:59 +00:00
cache_set ( 'form_' . $form_build_id , $form , 'cache_form' , REQUEST_TIME + $expire );
2007-07-29 17:28:23 +00:00
if ( ! empty ( $form_state [ 'storage' ])) {
2008-09-17 07:11:59 +00:00
cache_set ( 'storage_' . $form_build_id , $form_state [ 'storage' ], 'cache_form' , REQUEST_TIME + $expire );
2007-07-29 17:28:23 +00:00
}
}
2006-08-31 14:59:28 +00:00
/**
2007-05-14 13:43:38 +00:00
* Retrieves a form using a form_id , populates it with $form_state [ 'values' ],
2006-08-31 14:59:28 +00:00
* processes it , and returns any validation errors encountered . This
* function is the programmatic counterpart to drupal_get_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 . Examples
2006-08-31 14:59:28 +00:00
* may be found in node_forms (), search_forms (), and user_forms () .
2007-05-14 13:43:38 +00:00
* @ param $form_state
* 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 .
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
* in here when it is called .
2006-11-26 22:35:20 +00:00
* For example :
*
* // 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' ;
* $form_state [ 'values' ][ 'pass' ] = 'password' ;
2007-09-25 15:14:37 +00:00
* $form_state [ 'values' ][ 'op' ] = t ( 'Create new account' );
2009-04-29 07:18:04 +00:00
* drupal_form_submit ( 'user_register' , $form_state );
2006-11-26 22:35:20 +00:00
*
* // Create a new node
2007-05-14 13:43:38 +00:00
* $form_state = array ();
2008-02-03 19:26:10 +00:00
* module_load_include ( 'inc' , 'node' , 'node.pages' );
2006-11-26 22:35:20 +00:00
* $node = array ( 'type' => 'story' );
2007-05-14 13:43:38 +00:00
* $form_state [ 'values' ][ 'title' ] = 'My node' ;
* $form_state [ 'values' ][ 'body' ] = 'This is the body text!' ;
* $form_state [ 'values' ][ 'name' ] = 'robo-user' ;
2007-09-25 15:14:37 +00:00
* $form_state [ 'values' ][ 'op' ] = t ( 'Save' );
2009-04-29 07:18:04 +00:00
* drupal_form_submit ( 'story_node_form' , $form_state , ( object ) $node );
2006-08-31 14:59:28 +00:00
*/
2009-04-29 07:18:04 +00:00
function drupal_form_submit ( $form_id , & $form_state ) {
2009-03-14 20:13:27 +00:00
if ( ! isset ( $form_state [ 'args' ])) {
$args = func_get_args ();
array_shift ( $args );
array_shift ( $args );
$form_state [ 'args' ] = $args ;
}
2009-01-26 14:08:44 +00:00
2009-03-14 20:13:27 +00:00
$form = drupal_retrieve_form ( $form_id , $form_state );
$form_state [ 'input' ] = $form_state [ 'values' ];
$form_state [ 'programmed' ] = TRUE ;
// Merge in default values.
$form_state += form_state_defaults ();
2009-01-22 12:46:07 +00:00
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
* A keyed array containing the current state of the form .
2006-08-18 18:58:47 +00:00
* @ param ...
2007-10-31 15:10:33 +00:00
* Any additional arguments needed by the unique form constructor
* function . Generally , these are any arguments passed into the
2009-04-29 07:18:04 +00:00
* drupal_get_form () or drupal_form_submit () functions after the first
2007-10-31 15:10:33 +00:00
* argument . If a module implements hook_forms (), it can examine
* these additional arguments and conditionally return different
* builder functions as well .
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
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.
$args = $form_state [ 'args' ];
2006-10-12 20:36:51 +00:00
// We first check to see if there's a function named after the $form_id.
// If there is, we simply pass the arguments on to it to get the form.
2008-05-06 12:18:54 +00:00
if ( ! drupal_function_exists ( $form_id )) {
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' ];
2008-05-06 12:18:54 +00:00
drupal_function_exists ( $callback );
2006-08-18 18:58:47 +00:00
}
}
2007-06-04 07:22:23 +00:00
2009-03-14 20:13:27 +00:00
$args = array_merge ( array ( & $form_state ), $args );
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.
2006-08-31 14:59:28 +00:00
$form = call_user_func_array ( isset ( $callback ) ? $callback : $form_id , $args );
2009-03-14 20:13:27 +00:00
$form [ '#form_id' ] = $form_id ;
$form [ '#args' ] = $form_state [ 'args' ];
2006-08-31 14:59:28 +00:00
return $form ;
2006-08-18 18:58:47 +00:00
}
/**
* This function is the heart of form API . The form gets built , validated and in
* appropriate cases , submitted .
*
* @ 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' ]);
}
}
2007-05-14 13:43:38 +00:00
$form = form_builder ( $form_id , $form , $form_state );
2007-08-23 18:02:52 +00:00
// Only process the form if it is programmed or the form_id coming
// from the POST data is set and matches the current form_id.
2009-03-14 20:13:27 +00:00
if (( ! empty ( $form_state [ 'programmed' ])) || ( ! empty ( $form_state [ 'input' ]) && ( isset ( $form_state [ 'input' ][ 'form_id' ]) && ( $form_state [ 'input' ][ 'form_id' ] == $form_id )))) {
2007-05-14 13:43:38 +00:00
drupal_validate_form ( $form_id , $form , $form_state );
2007-10-24 13:35:26 +00:00
// form_clean_id() maintains a cache of element IDs it has seen,
// 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.
2009-06-02 13:47:26 +00:00
drupal_static_reset ( 'form_clean_id' );
2007-10-24 13:35:26 +00:00
2007-05-14 13:43:38 +00:00
if (( ! empty ( $form_state [ 'submitted' ])) && ! form_get_errors () && empty ( $form_state [ 'rebuild' ])) {
$form_state [ 'redirect' ] = NULL ;
form_execute_handlers ( 'submit' , $form , $form_state );
// We'll clear out the cached copies of the form and its stored data
// here, as we've finished with them. The in-memory copies are still
// here, though.
2009-04-30 15:20:06 +00:00
if ( variable_get ( 'cache' , CACHE_DISABLED ) == CACHE_DISABLED && ! empty ( $form_state [ 'values' ][ 'form_build_id' ])) {
2008-04-14 17:48:46 +00:00
cache_clear_all ( 'form_' . $form_state [ 'values' ][ 'form_build_id' ], 'cache_form' );
cache_clear_all ( 'storage_' . $form_state [ 'values' ][ 'form_build_id' ], 'cache_form' );
2007-05-14 13:43:38 +00:00
}
// 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' ])) {
2007-05-14 13:43:38 +00:00
// The batch uses its own copies of $form and $form_state for
2008-12-30 16:43:20 +00:00
// late execution of submit handlers and post-batch redirection.
2007-05-14 13:43:38 +00:00
$batch [ 'form' ] = $form ;
$batch [ 'form_state' ] = $form_state ;
2009-03-14 20:13:27 +00:00
$batch [ 'progressive' ] = ! $form_state [ 'programmed' ];
2007-05-04 09:41:37 +00:00
batch_process ();
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
// If no submit handlers have populated the $form_state['storage']
// bundle, and the $form_state['rebuild'] flag has not been set,
// we're finished and should redirect to a new destination page
// if one has been set (and a fresh, unpopulated copy of the form
2009-04-29 07:18:04 +00:00
// if one hasn't). If the form was called by drupal_form_submit(),
2007-05-14 13:43:38 +00:00
// however, we'll skip this and let the calling function examine
// the resulting $form_state bundle itself.
2009-03-14 20:13:27 +00:00
if ( ! $form_state [ 'programmed' ] && empty ( $form_state [ 'rebuild' ]) && empty ( $form_state [ 'storage' ])) {
if ( ! empty ( $form_state [ 'no_redirect' ])) {
$form_state [ 'executed' ] = TRUE ;
}
else {
drupal_redirect_form ( $form , $form_state [ 'redirect' ]);
}
2006-08-22 19:12:05 +00:00
}
2006-07-22 19:26:58 +00:00
}
}
}
/**
* Prepares a structured form array by adding required elements ,
* executing any hook_form_alter functions , and optionally inserting
* a validation token to prevent tampering .
*
* @ 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
2006-08-25 08:15:24 +00:00
if ( isset ( $form [ '#build_id' ])) {
$form [ 'form_build_id' ] = array (
'#type' => 'hidden' ,
'#value' => $form [ '#build_id' ],
'#id' => $form [ '#build_id' ],
'#name' => 'form_build_id' ,
);
}
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.
2005-10-11 19:44:35 +00:00
if ( isset ( $form [ '#token' ])) {
2009-03-14 20:13:27 +00:00
if ( $form [ '#token' ] === FALSE || $user -> uid == 0 || $form_state [ 'programmed' ]) {
2006-03-20 16:28:10 +00:00
unset ( $form [ '#token' ]);
2005-11-06 11:38:56 +00:00
}
2006-03-20 16:28:10 +00:00
else {
2006-10-31 08:06:18 +00:00
$form [ 'form_token' ] = array ( '#type' => 'token' , '#default_value' => drupal_get_token ( $form [ '#token' ]));
2006-03-20 16:28:10 +00:00
}
2005-10-07 06:11:12 +00:00
}
2009-03-14 20:13:27 +00:00
elseif ( isset ( $user -> uid ) && $user -> uid && ! $form_state [ 'programmed' ]) {
2006-10-31 08:06:18 +00:00
$form [ '#token' ] = $form_id ;
$form [ 'form_token' ] = array (
2008-04-14 17:48:46 +00:00
'#id' => form_clean_id ( 'edit-' . $form_id . '-form-token' ),
2006-10-31 08:06:18 +00:00
'#type' => 'token' ,
'#default_value' => drupal_get_token ( $form [ '#token' ]),
);
}
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 ,
'#id' => form_clean_id ( " edit- $form_id " ),
);
2006-02-27 14:46:49 +00:00
}
2006-02-02 02:10:31 +00:00
if ( ! isset ( $form [ '#id' ])) {
2006-12-12 09:39:50 +00:00
$form [ '#id' ] = form_clean_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' ])) {
2008-05-06 12:18:54 +00:00
if ( drupal_function_exists ( $form_id . '_validate' )) {
2008-04-14 17:48:46 +00:00
$form [ '#validate' ] = array ( $form_id . '_validate' );
2005-11-22 21:31:15 +00:00
}
}
2005-12-02 15:21:01 +00:00
if ( ! isset ( $form [ '#submit' ])) {
2008-05-06 12:18:54 +00:00
if ( drupal_function_exists ( $form_id . '_submit' )) {
2007-01-23 19:17:55 +00:00
// We set submit here so that it can be altered.
2008-04-14 17:48:46 +00:00
$form [ '#submit' ] = array ( $form_id . '_submit' );
2005-11-22 21:31:15 +00:00
}
}
2007-12-18 16:24:01 +00:00
// Normally, we would call drupal_alter($form_id, $form, $form_state).
// However, drupal_alter() normally supports just one byref parameter. Using
// the __drupal_alter_by_ref key, we can store any additional parameters
// that need to be altered, and they'll be split out into additional params
// for the hook_form_alter() implementations.
// @todo: Remove this in Drupal 7.
$data = & $form ;
$data [ '__drupal_alter_by_ref' ] = array ( & $form_state );
2008-04-14 17:48:46 +00:00
drupal_alter ( 'form_' . $form_id , $data );
2007-12-18 16:24:01 +00:00
// __drupal_alter_by_ref is unset in the drupal_alter() function, we need
// to repopulate it to ensure both calls get the data.
$data [ '__drupal_alter_by_ref' ] = array ( & $form_state );
drupal_alter ( 'form' , $data , $form_id );
2005-10-07 06:11:12 +00:00
}
2006-07-22 19:26:58 +00:00
/**
2007-05-14 13:43:38 +00:00
* Validates user - submitted form data from the $form_state using
2006-07-22 19:26:58 +00:00
* the validate functions defined in a structured form array .
*
* @ 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 . 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 :
* $form_state [ 'data_for_submision' ] = $data ;
* 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
*/
2007-05-14 13:43:38 +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' ])) {
2007-01-23 19:17:55 +00:00
// Setting this error will cause the form to fail validation.
2006-05-07 00:08:36 +00:00
form_set_error ( 'form_token' , t ( 'Validation error, please try again. If this error persists, please contact the site administrator.' ));
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 ;
2005-10-07 06:11:12 +00:00
}
2006-07-22 19:26:58 +00:00
/**
* Redirect the user to a URL after a form has been processed .
*
* @ param $form
* An associative array containing the structure of the form .
* @ param $redirect
2007-05-14 13:43:38 +00:00
* An optional value containing the destination path to redirect
2006-07-22 19:26:58 +00:00
* to if none is specified by the form .
*/
function drupal_redirect_form ( $form , $redirect = NULL ) {
2007-05-14 13:43:38 +00:00
$goto = NULL ;
2006-07-22 19:26:58 +00:00
if ( isset ( $redirect )) {
$goto = $redirect ;
}
2007-05-14 13:43:38 +00:00
if ( $goto !== FALSE && isset ( $form [ '#redirect' ])) {
2006-07-22 19:26:58 +00:00
$goto = $form [ '#redirect' ];
}
2007-07-01 17:41:16 +00:00
if ( ! isset ( $goto ) || ( $goto !== FALSE )) {
if ( isset ( $goto )) {
if ( is_array ( $goto )) {
call_user_func_array ( 'drupal_goto' , $goto );
}
else {
drupal_goto ( $goto );
}
}
drupal_goto ( $_GET [ 'q' ]);
}
2006-07-22 19:26:58 +00:00
}
2007-01-23 19:17:55 +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 .
*
* @ 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 :
* $form_state [ 'data_for_submision' ] = $data ;
* 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 .
*/
2007-05-14 13:43:38 +00:00
function _form_validate ( $elements , & $form_state , $form_id = NULL ) {
2009-06-02 13:47:26 +00:00
$complete_form = & drupal_static ( __FUNCTION__ );
2008-01-30 21:42:17 +00:00
2007-11-11 16:14:45 +00:00
// Also used in the installer, pre-database setup.
$t = get_t ();
2007-11-13 14:04:08 +00:00
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
}
}
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' ]) {
2006-03-06 14:46:51 +00:00
if ( isset ( $elements [ '#needs_validation' ])) {
2008-03-15 11:02:47 +00:00
// Make sure a value is passed when the field is 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.
if ( $elements [ '#required' ] && ( ! count ( $elements [ '#value' ]) || ( is_string ( $elements [ '#value' ]) && strlen ( trim ( $elements [ '#value' ])) == 0 ))) {
2007-11-11 16:14:45 +00:00
form_error ( $elements , $t ( '!name field is required.' , array ( '!name' => $elements [ '#title' ])));
2006-01-06 07:15:47 +00:00
}
2006-03-05 02:46:55 +00:00
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' ]) {
2007-11-11 16:14:45 +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' ])) {
$value = $elements [ '#type' ] == 'checkboxes' ? array_keys ( array_filter ( $elements [ '#value' ])) : $elements [ '#value' ];
foreach ( $value as $v ) {
if ( ! isset ( $options [ $v ])) {
2007-11-11 16:14:45 +00:00
form_error ( $elements , $t ( 'An illegal choice has been detected. Please contact the site administrator.' ));
2007-10-06 15:23:18 +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
}
}
2006-03-05 02:46:55 +00:00
elseif ( ! isset ( $options [ $elements [ '#value' ]])) {
2007-11-11 16:14:45 +00:00
form_error ( $elements , $t ( 'An illegal choice has been detected. Please contact the site administrator.' ));
2007-10-15 14:52:18 +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
}
}
2008-01-30 21:42:17 +00:00
// Call user-defined form level validators and store a copy of the full
// form so that element-specific validators can examine the entire structure
// if necessary.
2007-05-14 13:43:38 +00:00
if ( isset ( $form_id )) {
form_execute_handlers ( 'validate' , $elements , $form_state );
2008-01-30 21:42:17 +00:00
$complete_form = $elements ;
2007-05-14 13:43:38 +00:00
}
// Call any element-specific validators. These must act on the element
// #value data.
elseif ( isset ( $elements [ '#element_validate' ])) {
foreach ( $elements [ '#element_validate' ] as $function ) {
2008-05-06 12:18:54 +00:00
if ( drupal_function_exists ( $function )) {
2008-01-30 21:42:17 +00:00
$function ( $elements , $form_state , $complete_form );
2005-10-07 06:11:12 +00:00
}
}
}
2005-10-11 19:44:35 +00:00
$elements [ '#validated' ] = TRUE ;
2005-10-07 06:11:12 +00:00
}
}
2007-05-14 13:43:38 +00:00
/**
* A helper function used to execute 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 .
*
* @ 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 ) {
$return = FALSE ;
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
}
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 ) {
2008-05-06 12:18:54 +00:00
if ( drupal_function_exists ( $function )) {
2009-05-24 17:39:35 +00:00
// Check to see if a previous _submit handler has set a batch, but
// make sure we do not react to a batch that is already being processed
2009-04-29 07:18:04 +00:00
// (for instance if a batch operation performs a drupal_form_submit()).
2009-03-28 18:09:11 +00:00
if ( $type == 'submit' && ( $batch =& batch_get ()) && ! isset ( $batch [ 'current_set' ])) {
2007-05-14 13:43:38 +00:00
// Some previous _submit handler has set a batch. We store the call
// in a special 'control' batch set, for execution at the correct
// time during the batch processing workflow.
2007-05-16 07:56:19 +00:00
$batch [ 'sets' ][] = array ( 'form_submit' => $function );
2007-05-14 13:43:38 +00:00
}
else {
2007-06-04 07:22:23 +00:00
$function ( $form , $form_state );
2007-05-14 13:43:38 +00:00
}
$return = TRUE ;
}
}
return $return ;
}
2005-10-13 10:02:31 +00:00
/**
2007-12-23 12:33:13 +00:00
* File an error against a form element .
*
* @ 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 .
2008-09-19 02:37:29 +00:00
* @ param $reset
* Reset the form errors static cache .
2007-12-23 12:33:13 +00:00
* @ return
* Never use the return value of this function , use form_get_errors and
* form_get_error instead .
2005-10-13 10:02:31 +00:00
*/
2009-06-02 13:47:26 +00:00
function form_set_error ( $name = NULL , $message = '' ) {
$form = & drupal_static ( __FUNCTION__ , array ());
2005-10-13 10:02:31 +00:00
if ( isset ( $name ) && ! isset ( $form [ $name ])) {
$form [ $name ] = $message ;
2006-01-24 10:15:03 +00:00
if ( $message ) {
drupal_set_message ( $message , 'error' );
}
2005-10-13 10:02:31 +00:00
}
return $form ;
}
2009-06-02 13:47:26 +00:00
/**
* Clear all errors against all form elements made by form_set_error () .
*/
function form_clear_error () {
drupal_static_reset ( 'form_set_error' );
}
2005-10-13 10:02:31 +00:00
/**
* Return an associative array of all errors .
*/
function form_get_errors () {
$form = form_set_error ();
if ( ! empty ( $form )) {
return $form ;
}
}
/**
* Return the error message filed against the form with the specified name .
*/
function form_get_error ( $element ) {
$form = form_set_error ();
$key = $element [ '#parents' ][ 0 ];
if ( isset ( $form [ $key ])) {
return $form [ $key ];
}
$key = implode ( '][' , $element [ '#parents' ]);
if ( isset ( $form [ $key ])) {
return $form [ $key ];
}
}
2005-10-07 06:11:12 +00:00
/**
* Flag an element as having an error .
*/
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
}
/**
2007-05-14 13:43:38 +00:00
* Walk through the structured form array , adding any required
* properties to each element and mapping the incoming $_POST
2009-04-11 22:19:46 +00:00
* data to the proper elements . Also , execute any #process handlers
* attached to a specific element .
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 .
2006-02-17 10:51:57 +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 . 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
*/
2007-05-14 13:43:38 +00:00
function form_builder ( $form_id , $form , & $form_state ) {
2009-06-02 13:47:26 +00:00
$complete_form = & drupal_static ( __FUNCTION__ );
$cache = & drupal_static ( __FUNCTION__ . ':cache' );
$file = & drupal_static ( __FUNCTION__ . ':file' );
2007-10-16 14:06:23 +00:00
2006-06-23 08:31:23 +00:00
// Initialize as unprocessed.
$form [ '#processed' ] = FALSE ;
2007-12-31 08:54:37 +00:00
// Use element defaults.
2009-02-03 18:55:32 +00:00
if (( ! empty ( $form [ '#type' ])) && ( $info = element_info ( $form [ '#type' ]))) {
2007-01-23 19:17:55 +00:00
// Overlay $info onto $form, retaining preexisting keys in $form.
2005-10-07 06:11:12 +00:00
$form += $info ;
}
2007-10-16 14:06:23 +00:00
if ( isset ( $form [ '#type' ]) && $form [ '#type' ] == 'form' ) {
2008-06-24 17:01:33 +00:00
$cache = NULL ;
2007-10-16 14:06:23 +00:00
$complete_form = $form ;
2009-03-14 20:13:27 +00:00
if ( ! empty ( $form_state [ 'programmed' ])) {
2007-10-16 14:06:23 +00:00
$form_state [ 'submitted' ] = TRUE ;
}
2007-06-17 12:07:51 +00:00
}
2009-04-11 22:19:46 +00:00
if ( ! isset ( $form [ '#id' ])) {
$form [ '#id' ] = form_clean_id ( 'edit-' . implode ( '-' , $form [ '#parents' ]));
}
2006-01-24 08:20:45 +00:00
if ( isset ( $form [ '#input' ]) && $form [ '#input' ]) {
2007-10-16 14:06:23 +00:00
_form_builder_handle_input_element ( $form_id , $form , $form_state , $complete_form );
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.
if ( isset ( $form [ '#process' ]) && ! $form [ '#processed' ]) {
foreach ( $form [ '#process' ] as $process ) {
if ( drupal_function_exists ( $process )) {
$form = $process ( $form , $form_state , $complete_form );
}
}
$form [ '#processed' ] = TRUE ;
}
2007-06-28 07:48:41 +00:00
$form [ '#defaults_loaded' ] = TRUE ;
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.
$form [ '#sorted' ] = TRUE ;
2005-10-07 06:11:12 +00:00
// Recurse through all child elements.
2007-01-10 23:30:07 +00:00
$count = 0 ;
2005-10-07 06:11:12 +00:00
foreach ( element_children ( $form ) as $key ) {
2007-01-23 19:17:55 +00:00
// Don't squash an existing tree value.
2005-11-18 13:48:09 +00:00
if ( ! isset ( $form [ $key ][ '#tree' ])) {
$form [ $key ][ '#tree' ] = $form [ '#tree' ];
}
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.
2006-08-22 11:13:04 +00:00
if ( isset ( $form [ '#access' ]) && ! $form [ '#access' ]) {
$form [ $key ][ '#access' ] = FALSE ;
}
2007-01-23 19:17:55 +00:00
// Don't squash existing parents value.
2005-11-18 13:48:09 +00:00
if ( ! isset ( $form [ $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.
2005-12-04 08:14:07 +00:00
$form [ $key ][ '#parents' ] = $form [ $key ][ '#tree' ] && $form [ '#tree' ] ? array_merge ( $form [ '#parents' ], array ( $key )) : array ( $key );
2007-11-19 19:23:28 +00:00
$array_parents = isset ( $form [ '#array_parents' ]) ? $form [ '#array_parents' ] : array ();
$array_parents [] = $key ;
$form [ $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.
2005-11-18 13:48:09 +00:00
if ( ! isset ( $form [ $key ][ '#weight' ])) {
$form [ $key ][ '#weight' ] = $count / 1000 ;
}
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.
2007-01-10 23:30:07 +00:00
unset ( $form [ '#sorted' ]);
}
2007-05-14 13:43:38 +00:00
$form [ $key ] = form_builder ( $form_id , $form [ $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.
2006-04-20 07:11:37 +00:00
if ( isset ( $form [ '#after_build' ]) && ! isset ( $form [ '#after_build_done' ])) {
foreach ( $form [ '#after_build' ] as $function ) {
2007-06-04 07:22:23 +00:00
$form = $function ( $form , $form_state );
2007-05-14 13:43:38 +00:00
$form [ '#after_build_done' ] = TRUE ;
}
}
// Now that we've processed everything, we can go back to handle the funky
2007-07-02 14:41:37 +00:00
// Internet Explorer button-click scenario.
2007-05-14 13:43:38 +00:00
_form_builder_ie_cleanup ( $form , $form_state );
2008-02-06 19:38:28 +00:00
2008-12-30 16:43:20 +00:00
// We should keep the buttons array until the IE clean up function
2008-01-25 16:24:48 +00:00
// has recognized the submit button so the form has been marked
// as submitted. If we already know which button was submitted,
// we don't need the array.
if ( ! empty ( $form_state [ 'submitted' ])) {
unset ( $form_state [ 'buttons' ]);
}
2007-07-03 19:13:49 +00:00
2007-11-19 19:23:28 +00:00
// If some callback set #cache, we need to flip a static flag so later it
// can be found.
2008-09-21 06:34:41 +00:00
if ( ! empty ( $form [ '#cache' ])) {
2007-11-19 19:23:28 +00:00
$cache = $form [ '#cache' ];
}
2008-11-15 13:01:11 +00:00
// If there is a file element, we need to flip a static flag so later the
// form encoding can be set.
2008-11-15 15:32:36 +00:00
if ( isset ( $form [ '#type' ]) && $form [ '#type' ] == 'file' ) {
2008-11-15 13:01:11 +00:00
$file = TRUE ;
}
if ( isset ( $form [ '#type' ]) && $form [ '#type' ] == 'form' ) {
// We are on the top form, we can copy back #cache if it's set.
if ( isset ( $cache )) {
$form [ '#cache' ] = TRUE ;
}
2008-11-24 00:40:45 +00:00
// If there is a file element, we set the form encoding.
if ( isset ( $file )) {
$form [ '#attributes' ][ 'enctype' ] = 'multipart/form-data' ;
}
2007-11-19 19:23:28 +00:00
}
2007-05-14 13:43:38 +00:00
return $form ;
}
/**
* Populate the #value and #name properties of input elements so they
2009-04-11 22:19:46 +00:00
* can be processed and rendered .
2007-05-14 13:43:38 +00:00
*/
2007-10-16 14:06:23 +00:00
function _form_builder_handle_input_element ( $form_id , & $form , & $form_state , $complete_form ) {
2007-05-14 13:43:38 +00:00
if ( ! isset ( $form [ '#name' ])) {
$name = array_shift ( $form [ '#parents' ]);
$form [ '#name' ] = $name ;
if ( $form [ '#type' ] == 'file' ) {
// 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.
2008-04-14 17:48:46 +00:00
$form [ '#name' ] = 'files[' . $form [ '#name' ] . ']' ;
2007-05-14 13:43:38 +00:00
}
elseif ( count ( $form [ '#parents' ])) {
2008-04-14 17:48:46 +00:00
$form [ '#name' ] .= '[' . implode ( '][' , $form [ '#parents' ]) . ']' ;
2007-05-14 13:43:38 +00:00
}
array_unshift ( $form [ '#parents' ], $name );
}
if ( ! empty ( $form [ '#disabled' ])) {
$form [ '#attributes' ][ 'disabled' ] = 'disabled' ;
}
if ( ! isset ( $form [ '#value' ]) && ! array_key_exists ( '#value' , $form )) {
2008-04-14 17:48:46 +00:00
$function = ! empty ( $form [ '#value_callback' ]) ? $form [ '#value_callback' ] : 'form_type_' . $form [ '#type' ] . '_value' ;
2009-03-14 20:13:27 +00:00
if (( $form_state [ 'programmed' ]) || (( ! isset ( $form [ '#access' ]) || $form [ '#access' ]) && isset ( $form_state [ 'input' ]) && ( isset ( $form_state [ 'input' ][ 'form_id' ]) && $form_state [ 'input' ][ 'form_id' ] == $form_id ))) {
$edit = $form_state [ 'input' ];
2007-05-14 13:43:38 +00:00
foreach ( $form [ '#parents' ] as $parent ) {
$edit = isset ( $edit [ $parent ]) ? $edit [ $parent ] : NULL ;
}
2009-03-14 20:13:27 +00:00
if ( ! $form_state [ 'programmed' ] || isset ( $edit )) {
2007-07-04 15:45:37 +00:00
// Call #type_value to set the form value;
if ( function_exists ( $function )) {
2009-03-14 20:13:27 +00:00
$form [ '#value' ] = $function ( $form , $edit , $form_state );
2007-05-14 13:43:38 +00:00
}
2007-07-04 15:45:37 +00:00
if ( ! isset ( $form [ '#value' ]) && isset ( $edit )) {
$form [ '#value' ] = $edit ;
2007-05-14 13:43:38 +00:00
}
}
2007-07-04 15:45:37 +00:00
// Mark all posted values for validation.
if ( isset ( $form [ '#value' ]) || ( isset ( $form [ '#required' ]) && $form [ '#required' ])) {
$form [ '#needs_validation' ] = TRUE ;
}
2007-05-14 13:43:38 +00:00
}
2007-07-04 15:45:37 +00:00
// Load defaults.
2007-05-14 13:43:38 +00:00
if ( ! isset ( $form [ '#value' ])) {
2007-07-04 15:45:37 +00:00
// Call #type_value without a second argument to request default_value handling.
2006-04-20 07:11:37 +00:00
if ( function_exists ( $function )) {
2007-07-04 15:45:37 +00:00
$form [ '#value' ] = $function ( $form );
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.
if ( ! isset ( $form [ '#value' ]) && empty ( $form [ '#has_garbage_value' ])) {
2007-05-14 13:43:38 +00:00
$form [ '#value' ] = isset ( $form [ '#default_value' ]) ? $form [ '#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
2007-05-14 13:43:38 +00:00
// Determine which button (if any) was clicked to submit the form.
// We compare the incoming values with the buttons defined in the form,
// and flag the one that matches. We have to do some funky tricks to
// deal with Internet Explorer's handling of single-button forms, though.
2009-03-14 20:13:27 +00:00
if ( ! empty ( $form_state [ 'input' ]) && isset ( $form [ '#executes_submit_callback' ])) {
2007-05-14 13:43:38 +00:00
// First, accumulate a collection of buttons, divided into two bins:
// those that execute full submit callbacks and those that only validate.
$button_type = $form [ '#executes_submit_callback' ] ? 'submit' : 'button' ;
$form_state [ 'buttons' ][ $button_type ][] = $form ;
2009-03-14 20:13:27 +00:00
if ( _form_button_was_clicked ( $form , $form_state )) {
2007-05-14 13:43:38 +00:00
$form_state [ 'submitted' ] = $form_state [ 'submitted' ] || $form [ '#executes_submit_callback' ];
// In most cases, we want to use form_set_value() to manipulate
// the global variables. In this special case, we want to make sure that
// the value of this element is listed in $form_variables under 'op'.
$form_state [ 'values' ][ $form [ '#name' ]] = $form [ '#value' ];
2007-07-03 19:13:49 +00:00
$form_state [ 'clicked_button' ] = $form ;
2007-05-14 13:43:38 +00:00
if ( isset ( $form [ '#validate' ])) {
$form_state [ 'validate_handlers' ] = $form [ '#validate' ];
}
if ( isset ( $form [ '#submit' ])) {
$form_state [ 'submit_handlers' ] = $form [ '#submit' ];
}
}
}
form_set_value ( $form , $form [ '#value' ], $form_state );
}
2007-07-29 17:28:23 +00:00
/**
* Helper function to handle the sometimes - convoluted logic of button
* click detection .
*
* In Internet Explorer , if ONLY one submit button is present , AND the
* enter key is used to submit the form , no form value is sent for it
* and we ' ll never detect a match . That special case is handled by
* _form_builder_ie_cleanup () .
*/
2009-03-14 20:13:27 +00:00
function _form_button_was_clicked ( $form , & $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.
2009-03-14 20:13:27 +00:00
if ( isset ( $form_state [ 'input' ][ $form [ '#name' ]]) && $form_state [ 'input' ][ $form [ '#name' ]] == $form [ '#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.
elseif ( ! empty ( $form [ '#has_garbage_value' ]) && isset ( $form [ '#value' ]) && $form [ '#value' ] !== '' ) {
return TRUE ;
}
return FALSE ;
}
/**
* In IE , if only one submit button is present , AND the enter key is
* used to submit the form , no form value is sent for it and our normal
* button detection code will never detect a match . We call this
* function after all other button - detection is complete to check
* for the proper conditions , and treat the single button on the form
* as 'clicked' if they are met .
*/
function _form_builder_ie_cleanup ( $form , & $form_state ) {
// Quick check to make sure we're always looking at the full form
// and not a sub-element.
if ( ! empty ( $form [ '#type' ]) && $form [ '#type' ] == 'form' ) {
// If we haven't recognized a submission yet, and there's a single
// submit button, we know that we've hit the right conditions. Grab
// the first one and treat it as the clicked button.
if ( empty ( $form_state [ 'submitted' ]) && ! empty ( $form_state [ 'buttons' ][ 'submit' ]) && empty ( $form_state [ 'buttons' ][ 'button' ])) {
$button = $form_state [ 'buttons' ][ 'submit' ][ 0 ];
// Set up all the $form_state information that would have been
// populated had the button been recognized earlier.
$form_state [ 'submitted' ] = TRUE ;
$form_state [ 'submit_handlers' ] = empty ( $button [ '#submit' ]) ? NULL : $button [ '#submit' ];
$form_state [ 'validate_handlers' ] = empty ( $button [ '#validate' ]) ? NULL : $button [ '#validate' ];
$form_state [ 'values' ][ $button [ '#name' ]] = $button [ '#value' ];
$form_state [ 'clicked_button' ] = $button ;
}
}
}
2007-08-10 10:51:17 +00:00
/**
* Helper function to determine the value for an image button form element .
*
* @ param $form
* The form element whose value is being populated .
* @ param $edit
* The incoming POST data to populate the form element . If this is FALSE ,
* the element ' s default value should be returned .
2009-03-14 20:13:27 +00:00
* @ param $form_state
* A keyed array containing the current state of the form .
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-03-14 20:13:27 +00:00
function form_type_image_button_value ( $form , $edit , $form_state ) {
2007-08-10 10:51:17 +00:00
if ( $edit !== FALSE ) {
if ( ! empty ( $edit )) {
// 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-03-14 20:13:27 +00:00
$post = $form_state [ 'input' ];
2007-08-10 10:51:17 +00:00
foreach ( split ( '\[' , $form [ '#name' ]) as $element_name ) {
// chop off the ] that may exist.
if ( substr ( $element_name , - 1 ) == ']' ) {
$element_name = substr ( $element_name , 0 , - 1 );
}
if ( ! isset ( $post [ $element_name ])) {
2008-04-14 17:48:46 +00:00
if ( isset ( $post [ $element_name . '_x' ])) {
2007-08-10 10:51:17 +00:00
return $form [ '#return_value' ];
}
return NULL ;
}
2008-01-28 12:09:12 +00:00
$post = $post [ $element_name ];
2007-08-10 10:51:17 +00:00
}
return $form [ '#return_value' ];
}
}
}
2007-07-04 15:45:37 +00:00
/**
* Helper function to determine the value for a checkbox form element .
*
* @ param $form
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2007-07-04 15:45:37 +00:00
* @ param $edit
2007-07-14 15:23:29 +00:00
* The incoming POST data to populate the form element . If this is FALSE ,
* the element ' s default value should be returned .
2007-07-04 15:45:37 +00:00
* @ return
2007-07-14 15:23:29 +00:00
* The data that will appear in the $form_state [ 'values' ] collection
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2007-07-14 15:23:29 +00:00
function form_type_checkbox_value ( $form , $edit = FALSE ) {
if ( $edit !== FALSE ) {
2008-09-27 06:29:47 +00:00
if ( empty ( $form [ '#disabled' ])) {
return ! empty ( $edit ) ? $form [ '#return_value' ] : 0 ;
}
else {
return $form [ '#default_value' ];
}
2007-07-04 15:45:37 +00:00
}
}
/**
* Helper function to determine the value for a checkboxes form element .
*
* @ param $form
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2007-07-04 15:45:37 +00:00
* @ param $edit
2007-07-14 15:23:29 +00:00
* The incoming POST data to populate the form element . If this is FALSE ,
* the element ' s default value should be returned .
2007-07-04 15:45:37 +00:00
* @ return
2007-07-14 15:23:29 +00:00
* The data that will appear in the $form_state [ 'values' ] collection
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2007-09-25 11:29:10 +00:00
function form_type_checkboxes_value ( $form , $edit = FALSE ) {
2007-07-14 15:23:29 +00:00
if ( $edit === FALSE ) {
2007-07-04 15:45:37 +00:00
$value = array ();
$form += array ( '#default_value' => array ());
foreach ( $form [ '#default_value' ] as $key ) {
$value [ $key ] = 1 ;
}
return $value ;
}
2007-09-25 11:29:10 +00:00
elseif ( ! isset ( $edit )) {
return array ();
}
2007-07-04 15:45:37 +00:00
}
/**
* Helper function to determine the value for a password_confirm form
* element .
*
* @ param $form
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2007-07-04 15:45:37 +00:00
* @ param $edit
2007-07-14 15:23:29 +00:00
* The incoming POST data to populate the form element . If this is FALSE ,
* the element ' s default value should be returned .
2007-07-04 15:45:37 +00:00
* @ return
2007-07-14 15:23:29 +00:00
* The data that will appear in the $form_state [ 'values' ] collection
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2007-07-14 15:23:29 +00:00
function form_type_password_confirm_value ( $form , $edit = FALSE ) {
if ( $edit === FALSE ) {
2007-07-04 15:45:37 +00:00
$form += array ( '#default_value' => array ());
return $form [ '#default_value' ] + array ( 'pass1' => '' , 'pass2' => '' );
}
}
/**
* Helper function to determine the value for a select form element .
*
* @ param $form
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2007-07-04 15:45:37 +00:00
* @ param $edit
2007-07-14 15:23:29 +00:00
* The incoming POST data to populate the form element . If this is FALSE ,
* the element ' s default value should be returned .
2007-07-04 15:45:37 +00:00
* @ return
2007-07-14 15:23:29 +00:00
* The data that will appear in the $form_state [ 'values' ] collection
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2007-07-14 15:23:29 +00:00
function form_type_select_value ( $form , $edit = FALSE ) {
if ( $edit !== FALSE ) {
2007-07-04 15:45:37 +00:00
if ( isset ( $form [ '#multiple' ]) && $form [ '#multiple' ]) {
return ( is_array ( $edit )) ? drupal_map_assoc ( $edit ) : array ();
}
else {
return $edit ;
}
}
}
/**
* Helper function to determine the value for a textfield form element .
*
* @ param $form
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2007-07-04 15:45:37 +00:00
* @ param $edit
2007-07-14 15:23:29 +00:00
* The incoming POST data to populate the form element . If this is FALSE ,
* the element ' s default value should be returned .
2007-07-04 15:45:37 +00:00
* @ return
2007-07-14 15:23:29 +00:00
* The data that will appear in the $form_state [ 'values' ] collection
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2007-07-14 15:23:29 +00:00
function form_type_textfield_value ( $form , $edit = FALSE ) {
if ( $edit !== FALSE ) {
2007-07-04 15:45:37 +00:00
// Equate $edit to the form value to ensure it's marked for
// validation.
return str_replace ( array ( " \r " , " \n " ), '' , $edit );
}
}
/**
* Helper function to determine the value for form ' s token value .
*
* @ param $form
2007-07-14 15:23:29 +00:00
* The form element whose value is being populated .
2007-07-04 15:45:37 +00:00
* @ param $edit
2007-07-14 15:23:29 +00:00
* The incoming POST data to populate the form element . If this is FALSE ,
* the element ' s default value should be returned .
2007-07-04 15:45:37 +00:00
* @ return
2007-07-14 15:23:29 +00:00
* The data that will appear in the $form_state [ 'values' ] collection
* for this element . Return nothing to use the default .
2007-07-04 15:45:37 +00:00
*/
2007-07-14 15:23:29 +00:00
function form_type_token_value ( $form , $edit = FALSE ) {
if ( $edit !== FALSE ) {
2007-07-04 15:45:37 +00:00
return ( string ) $edit ;
}
}
2006-04-15 21:52:44 +00:00
/**
2008-04-14 17:51:38 +00:00
* Change submitted form values during the form processing cycle .
2006-04-15 21:52:44 +00:00
*
2008-04-14 17:51:38 +00:00
* Use this function to change the submitted value of a form item in the
* validation phase so that it persists in $form_state through to the
* submission handlers in the submission phase .
2006-04-15 21:52:44 +00:00
*
2008-04-14 17:51:38 +00:00
* Since $form_state [ 'values' ] can either be a flat array of values , or a tree
* of nested values , some care must be taken when using this function .
* Specifically , $form_item [ '#parents' ] is an array that describes the branch of
* the tree whose value should be updated . For example , if we wanted to update
* $form_state [ 'values' ][ 'one' ][ 'two' ] to 'new value' , we ' d pass in
* $form_item [ '#parents' ] = array ( 'one' , 'two' ) and $value = 'new value' .
*
* @ param $form_item
* The form item that should have its value updated . Keys used : #parents,
* #value. In most cases you can just pass in the right element from the $form
* array .
2006-04-15 21:52:44 +00:00
* @ param $value
2008-04-14 17:51:38 +00:00
* The new value for the form item .
* @ param $form_state
* The array where the value change should be recorded .
2006-04-15 21:52:44 +00:00
*/
2008-04-14 17:51:38 +00:00
function form_set_value ( $form_item , $value , & $form_state ) {
_form_set_value ( $form_state [ 'values' ], $form_item , $form_item [ '#parents' ], $value );
2006-04-15 21:52:44 +00:00
}
/**
* Helper function for form_set_value () .
*
2007-01-23 19:17:55 +00:00
* We iterate over $parents and create nested arrays for them
2007-06-04 07:22:23 +00:00
* in $form_state [ 'values' ] if needed . Then we insert the value into
2006-04-15 21:52:44 +00:00
* the right array .
*/
2008-04-14 17:51:38 +00:00
function _form_set_value ( & $form_values , $form_item , $parents , $value ) {
2006-04-15 21:52:44 +00:00
$parent = array_shift ( $parents );
if ( empty ( $parents )) {
$form_values [ $parent ] = $value ;
}
else {
if ( ! isset ( $form_values [ $parent ])) {
$form_values [ $parent ] = array ();
}
2008-04-14 17:51:38 +00:00
_form_set_value ( $form_values [ $parent ], $form_item , $parents , $value );
2006-04-15 21:52:44 +00:00
}
}
2006-01-06 07:15:47 +00:00
function form_options_flatten ( $array , $reset = TRUE ) {
2009-06-02 13:47:26 +00:00
// $reset has been ignored here as the function recurses, retaining
// its value while recursing and resetting itself when called.
2006-01-06 07:15:47 +00:00
static $return ;
if ( $reset ) {
$return = array ();
}
foreach ( $array as $key => $value ) {
2006-10-31 07:37:25 +00:00
if ( is_object ( $value )) {
form_options_flatten ( $value -> option , FALSE );
}
2008-10-12 04:30:09 +00:00
elseif ( is_array ( $value )) {
2006-01-06 07:15:47 +00:00
form_options_flatten ( $value , FALSE );
}
else {
$return [ $key ] = 1 ;
}
}
return $return ;
}
2005-10-07 06:11:12 +00:00
/**
* Format a dropdown menu or scrolling selection box .
*
* @ param $element
* An associative array containing the properties of the element .
2006-01-18 19:04:12 +00:00
* Properties used : title , value , options , description , extra , multiple , required
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the form element .
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
*
2005-10-07 06:11:12 +00:00
* 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 .
*/
function theme_select ( $element ) {
$select = '' ;
2008-04-14 17:48:46 +00:00
$size = $element [ '#size' ] ? ' size="' . $element [ '#size' ] . '"' : '' ;
2006-04-07 13:22:12 +00:00
_form_set_class ( $element , array ( 'form-select' ));
2007-01-31 15:49:26 +00:00
$multiple = $element [ '#multiple' ];
2009-02-03 18:55:32 +00:00
return '<select name="' . $element [ '#name' ] . '' . ( $multiple ? '[]' : '' ) . '"' . ( $multiple ? ' multiple="multiple" ' : '' ) . drupal_attributes ( $element [ '#attributes' ]) . ' id="' . $element [ '#id' ] . '" ' . $size . '>' . form_select_options ( $element ) . '</select>' ;
2006-01-19 09:22:16 +00:00
}
function form_select_options ( $element , $choices = NULL ) {
if ( ! isset ( $choices )) {
$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 );
$value_is_array = 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 {
2006-01-19 09:22:16 +00:00
$key = ( string ) $key ;
2007-04-23 17:00:36 +00:00
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
/**
2007-01-05 19:08:30 +00:00
* Traverses a select element ' s #option array looking for any values
* that hold the given key . Returns an array of indexes that match .
*
* 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 .
* @ 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
/**
* Format a group of form items .
*
* @ param $element
* An associative array containing the properties of the element .
2006-01-24 08:20:45 +00:00
* Properties used : attributes , title , value , description , children , collapsible , collapsed
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the form item group .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
function theme_fieldset ( $element ) {
2008-11-02 06:26:33 +00:00
if ( ! empty ( $element [ '#collapsible' ])) {
2005-10-07 06:11:12 +00:00
drupal_add_js ( 'misc/collapse.js' );
2006-08-21 06:25:49 +00:00
if ( ! isset ( $element [ '#attributes' ][ 'class' ])) {
$element [ '#attributes' ][ 'class' ] = '' ;
}
2005-10-11 19:44:35 +00:00
$element [ '#attributes' ][ 'class' ] .= ' collapsible' ;
2008-11-02 06:26:33 +00:00
if ( ! empty ( $element [ '#collapsed' ])) {
2007-12-08 14:06:23 +00:00
$element [ '#attributes' ][ 'class' ] .= ' collapsed' ;
2005-10-07 06:11:12 +00:00
}
}
2009-04-11 22:19:46 +00:00
$element [ '#attributes' ][ 'id' ] = $element [ '#id' ];
2005-10-07 06:11:12 +00:00
2008-06-25 09:57:07 +00:00
return '<fieldset' . drupal_attributes ( $element [ '#attributes' ]) . '>' . ( $element [ '#title' ] ? '<legend>' . $element [ '#title' ] . '</legend>' : '' ) . ( isset ( $element [ '#description' ]) && $element [ '#description' ] ? '<div class="description">' . $element [ '#description' ] . '</div>' : '' ) . ( ! empty ( $element [ '#children' ]) ? $element [ '#children' ] : '' ) . ( isset ( $element [ '#value' ]) ? $element [ '#value' ] : '' ) . " </fieldset> \n " ;
2005-10-07 06:11:12 +00:00
}
/**
* Format a radio button .
*
* @ param $element
* An associative array containing the properties of the element .
2006-01-18 19:04:12 +00:00
* Properties used : required , return_value , value , attributes , title , description
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the form item group .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
function theme_radio ( $element ) {
2006-04-05 18:12:48 +00:00
_form_set_class ( $element , array ( 'form-radio' ));
2005-10-07 06:11:12 +00:00
$output = '<input type="radio" ' ;
2008-08-12 10:23:27 +00:00
$output .= 'id="' . $element [ '#id' ] . '" ' ;
2008-04-14 17:48:46 +00:00
$output .= 'name="' . $element [ '#name' ] . '" ' ;
$output .= 'value="' . $element [ '#return_value' ] . '" ' ;
2007-12-05 18:13:03 +00:00
$output .= ( check_plain ( $element [ '#value' ]) == $element [ '#return_value' ]) ? ' checked="checked" ' : ' ' ;
2008-04-14 17:48:46 +00:00
$output .= drupal_attributes ( $element [ '#attributes' ]) . ' />' ;
2005-10-11 19:44:35 +00:00
if ( ! is_null ( $element [ '#title' ])) {
2009-01-18 06:51:41 +00:00
$output = '<label class="option" for="' . $element [ '#id' ] . '">' . $output . ' ' . $element [ '#title' ] . '</label>' ;
2005-10-07 06:11:12 +00:00
}
2006-05-02 09:26:33 +00:00
2009-02-03 18:55:32 +00:00
return $output ;
2005-10-07 06:11:12 +00:00
}
/**
* Format a set of radio buttons .
*
* @ param $element
* An associative array containing the properties of the element .
2006-01-18 19:04:12 +00:00
* Properties used : title , value , options , description , required and attributes .
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the radio button set .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
function theme_radios ( $element ) {
2006-12-12 10:01:38 +00:00
$class = 'form-radios' ;
if ( isset ( $element [ '#attributes' ][ 'class' ])) {
2008-04-14 17:48:46 +00:00
$class .= ' ' . $element [ '#attributes' ][ 'class' ];
2006-12-12 10:01:38 +00:00
}
2008-04-14 17:48:46 +00:00
$element [ '#children' ] = '<div class="' . $class . '">' . ( ! empty ( $element [ '#children' ]) ? $element [ '#children' ] : '' ) . '</div>' ;
2005-10-07 06:11:12 +00:00
2009-02-03 18:55:32 +00:00
return $element [ '#children' ];
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' ],
2007-06-08 06:04:15 +00:00
'#attributes' => array ( 'class' => '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' ],
2007-06-08 06:04:15 +00:00
'#attributes' => array ( 'class' => '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
/**
2006-01-24 10:15:03 +00:00
* Validate password_confirm element .
2006-01-02 08:35:59 +00:00
*/
2007-05-14 13:43:38 +00:00
function password_confirm_validate ( $form , & $form_state ) {
2006-04-25 20:44:00 +00:00
$pass1 = trim ( $form [ 'pass1' ][ '#value' ]);
if ( ! empty ( $pass1 )) {
2006-01-24 10:15:03 +00:00
$pass2 = trim ( $form [ 'pass2' ][ '#value' ]);
2009-05-22 07:39:09 +00:00
if ( strcmp ( $pass1 , $pass2 )) {
2006-01-24 10:15:03 +00:00
form_error ( $form , t ( 'The specified passwords do not match.' ));
2006-01-02 08:35:59 +00:00
}
2006-01-24 10:15:03 +00:00
}
2009-03-14 20:13:27 +00:00
elseif ( $form [ '#required' ] && ! empty ( $form_state [ 'input' ])) {
2006-04-20 16:35:29 +00:00
form_error ( $form , 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.
2007-05-14 13:43:38 +00:00
form_set_value ( $form [ 'pass1' ], NULL , $form_state );
form_set_value ( $form [ 'pass2' ], NULL , $form_state );
form_set_value ( $form , $pass1 , $form_state );
2006-04-25 20:44:00 +00:00
2006-01-02 08:35:59 +00:00
return $form ;
2007-05-28 06:08:47 +00:00
2006-01-02 08:35:59 +00:00
}
2005-10-07 06:11:12 +00:00
/**
2005-11-21 09:42:14 +00:00
* Format a date selection element .
2005-10-07 06:11:12 +00:00
*
* @ param $element
* An associative array containing the properties of the element .
2006-01-18 19:04:12 +00:00
* Properties used : title , value , options , description , required and attributes .
2005-10-07 06:11:12 +00:00
* @ return
2005-11-21 09:42:14 +00:00
* A themed HTML string representing the date selection boxes .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
function theme_date ( $element ) {
2009-02-03 18:55:32 +00:00
return '<div class="container-inline">' . drupal_render_children ( $element ) . '</div>' ;
2005-10-07 06:11:12 +00:00
}
/**
2005-11-21 09:42:14 +00:00
* Roll out a single date element .
2005-10-07 06:11:12 +00:00
*/
2008-07-18 07:06:24 +00:00
function form_process_date ( $element ) {
2005-10-07 06:11:12 +00:00
// Default to current date
2007-05-07 07:29:23 +00:00
if ( empty ( $element [ '#value' ])) {
2008-09-17 07:11:59 +00:00
$element [ '#value' ] = array ( 'day' => format_date ( REQUEST_TIME , 'custom' , 'j' ),
'month' => format_date ( REQUEST_TIME , 'custom' , 'n' ),
'year' => format_date ( REQUEST_TIME , 'custom' , 'Y' ));
2005-10-07 06:11:12 +00:00
}
2006-01-02 08:04:02 +00:00
$element [ '#tree' ] = TRUE ;
2005-10-07 06:11:12 +00:00
// Determine the order of day, month, year in the site's chosen date format.
2006-09-06 06:53:39 +00:00
$format = variable_get ( 'date_format_short' , 'm/d/Y - H:i' );
2005-10-07 06:11:12 +00:00
$sort = array ();
$sort [ 'day' ] = max ( strpos ( $format , 'd' ), strpos ( $format , 'j' ));
$sort [ 'month' ] = max ( strpos ( $format , 'm' ), strpos ( $format , 'M' ));
$sort [ 'year' ] = strpos ( $format , 'Y' );
asort ( $sort );
$order = array_keys ( $sort );
2007-01-23 19:17:55 +00:00
// Output multi-selector for date.
2005-10-07 06:11:12 +00:00
foreach ( $order as $type ) {
switch ( $type ) {
case 'day' :
$options = drupal_map_assoc ( range ( 1 , 31 ));
break ;
case 'month' :
2005-11-21 09:42:14 +00:00
$options = drupal_map_assoc ( range ( 1 , 12 ), 'map_month' );
2005-10-07 06:11:12 +00:00
break ;
case 'year' :
$options = drupal_map_assoc ( range ( 1900 , 2050 ));
break ;
}
2007-07-20 05:44:13 +00:00
$parents = $element [ '#parents' ];
$parents [] = $type ;
2005-12-14 13:22:19 +00:00
$element [ $type ] = array (
'#type' => 'select' ,
'#value' => $element [ '#value' ][ $type ],
'#attributes' => $element [ '#attributes' ],
'#options' => $options ,
);
2005-10-07 06:11:12 +00:00
}
return $element ;
}
2006-05-15 06:27:32 +00:00
/**
* Validates the date type to stop dates like February 30 , 2006.
*/
function date_validate ( $form ) {
if ( ! checkdate ( $form [ '#value' ][ 'month' ], $form [ '#value' ][ 'day' ], $form [ '#value' ][ 'year' ])) {
form_error ( $form , t ( 'The specified date is invalid.' ));
}
}
2005-11-21 09:42:14 +00:00
/**
* Helper function for usage with drupal_map_assoc to display month names .
*/
function map_month ( $month ) {
2009-06-02 13:47:26 +00:00
$months = & drupal_static ( __FUNCTION__ , array (
2008-11-03 09:54:43 +00:00
1 => 'Jan' ,
2 => 'Feb' ,
3 => 'Mar' ,
4 => 'Apr' ,
5 => 'May' ,
6 => 'Jun' ,
7 => 'Jul' ,
8 => 'Aug' ,
9 => 'Sep' ,
10 => 'Oct' ,
11 => 'Nov' ,
12 => 'Dec' ,
2009-06-02 13:47:26 +00:00
));
2008-11-03 09:54:43 +00:00
return t ( $months [ $month ]);
2005-11-21 09:42:14 +00:00
}
2005-10-07 06:11:12 +00:00
2006-01-26 13:39:48 +00:00
/**
* If no default value is set for weight select boxes , use 0.
*/
function weight_value ( & $form ) {
if ( isset ( $form [ '#default_value' ])) {
$form [ '#value' ] = $form [ '#default_value' ];
}
else {
$form [ '#value' ] = 0 ;
}
}
2009-02-01 06:48:15 +00:00
/**
* Menu callback for AHAH callbacks through the #ahah['callback'] FAPI property.
*/
function form_ahah_callback () {
2009-03-14 20:13:27 +00:00
$form_state = form_state_defaults ();
2009-02-01 06:48:15 +00:00
$form_build_id = $_POST [ 'form_build_id' ];
// Get the form from the cache.
$form = form_get_cache ( $form_build_id , $form_state );
2009-05-01 14:51:41 +00:00
if ( ! $form ) {
// If $form cannot be loaded from the cache, the form_build_id in $_POST must
// be invalid, which means that someone performed a POST request onto
// system/ahah without actually viewing the concerned form in the browser.
// This is likely a hacking attempt as it never happens under normal
// circumstances, so we just do nothing.
exit ;
}
2009-02-01 06:48:15 +00:00
// We will run some of the submit handlers so we need to disable redirecting.
$form [ '#redirect' ] = FALSE ;
// We need to process the form, prepare for that by setting a few internals
// variables.
2009-03-14 20:13:27 +00:00
$form_state [ 'input' ] = $_POST ;
$form_state [ 'args' ] = $form [ '#args' ];
$form_id = $form [ '#form_id' ];
2009-02-01 06:48:15 +00:00
// Build, validate and if possible, submit the form.
drupal_process_form ( $form_id , $form , $form_state );
// This call recreates the form relying solely on the form_state that the
// drupal_process_form set up.
2009-03-14 20:13:27 +00:00
$form = drupal_rebuild_form ( $form_id , $form_state , $form_build_id );
2009-02-01 06:48:15 +00:00
// Get the callback function from the clicked button.
$callback = $form_state [ 'clicked_button' ][ '#ahah' ][ 'callback' ];
2009-05-01 14:51:41 +00:00
if ( drupal_function_exists ( $callback )) {
$callback ( $form , $form_state );
}
2009-02-01 06:48:15 +00:00
}
2005-10-07 06:11:12 +00:00
/**
2006-01-18 19:04:12 +00:00
* Roll out a single radios element to a list of radios ,
* using the options array as index .
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 ) {
foreach ( $element [ '#options' ] as $key => $choice ) {
2006-07-02 20:10:20 +00:00
if ( ! isset ( $element [ $key ])) {
2007-09-11 17:23:58 +00:00
// 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 ));
2007-03-27 05:13:55 +00:00
$element [ $key ] = array (
'#type' => 'radio' ,
'#title' => $choice ,
2007-12-05 18:13:03 +00:00
'#return_value' => check_plain ( $key ),
2007-03-27 05:13:55 +00:00
'#default_value' => isset ( $element [ '#default_value' ]) ? $element [ '#default_value' ] : NULL ,
'#attributes' => $element [ '#attributes' ],
'#parents' => $element [ '#parents' ],
2008-04-14 17:48:46 +00:00
'#id' => form_clean_id ( 'edit-' . implode ( '-' , $parents_for_id )),
2008-08-13 06:38:33 +00:00
'#ahah' => isset ( $element [ '#ahah' ]) ? $element [ '#ahah' ] : NULL ,
2007-03-27 05:13:55 +00:00
);
2005-10-07 06:11:12 +00:00
}
}
}
return $element ;
}
2008-09-27 19:47:43 +00:00
/**
2009-01-21 16:58:42 +00:00
* Add text format selector to text elements with the #text_format property.
2008-09-27 19:47:43 +00:00
*
2009-01-21 16:58:42 +00:00
* The #text_format property should be the ID of an text format, found in
2008-12-03 16:32:22 +00:00
* { filter_format } . format , which gets passed to filter_form () .
2008-09-27 19:47:43 +00:00
*
2009-01-21 16:58:42 +00:00
* If the property #text_format is set, the form element will be expanded into
2008-09-27 19:47:43 +00:00
* two separate form elements , one holding the content of the element , and the
2009-01-21 16:58:42 +00:00
* other holding the text format selector . The original element is shifted into
2008-09-27 19:47:43 +00:00
* a child element , but is otherwise unaltered , so that the format selector is
* at the same level as the text field which it affects .
*
* For example :
* @ code
* // A simple textarea, such as a node body.
* $form [ 'body' ] = array (
* '#type' => 'textarea' ,
* '#title' => t ( 'Body' ),
2009-01-21 16:58:42 +00:00
* '#text_format' => isset ( $node -> format ) ? $node -> format : FILTER_FORMAT_DEFAULT ,
2008-10-27 10:34:09 +00:00
* );
2008-09-27 19:47:43 +00:00
* @ endcode
*
* Becomes :
* @ code
* $form [ 'body' ] = array (
* // Type switches to 'markup', as we're only interested in submitting the child elements.
* '#type' => 'markup' ,
* // 'value' holds the original element.
* 'value' => array (
* '#type' => 'textarea' ,
* '#title' => t ( 'Body' ),
* '#parents' => array ( 'body' ),
* ),
2009-01-21 16:58:42 +00:00
* // 'format' holds the text format selector.
2008-09-27 19:47:43 +00:00
* 'format' => array (
* '#parents' => array ( 'body_format' ),
* ...
* ),
* );
* @ endcode
*
* And would result in :
* @ code
* // Original, unaltered form element value.
* $form_state [ 'values' ][ 'body' ] = 'Example content' ;
2009-01-21 16:58:42 +00:00
* // Chosen text format.
2008-09-27 19:47:43 +00:00
* $form_state [ 'values' ][ 'body_format' ] = 1 ;
* @ endcode
*
* @ see system_elements (), filter_form ()
*/
2009-03-08 21:25:18 +00:00
function form_process_text_format ( $element ) {
2009-01-21 16:58:42 +00:00
if ( isset ( $element [ '#text_format' ])) {
2008-09-27 19:47:43 +00:00
// Determine the form element parents and element name to use for the input
// format widget. This simulates the 'element' and 'element_format' pair of
// parents that filter_form() expects.
$element_parents = $element [ '#parents' ];
$element_name = array_pop ( $element_parents );
$element_parents [] = $element_name . '_format' ;
// We need to break references, otherwise form_builder recurses infinitely.
$element [ 'value' ] = ( array ) $element ;
2009-03-30 03:15:41 +00:00
$element [ 'value' ][ '#weight' ] = 0 ;
unset ( $element [ 'value' ][ '#description' ]);
2008-09-27 19:47:43 +00:00
$element [ '#type' ] = 'markup' ;
2009-02-03 18:55:32 +00:00
$element [ '#theme' ] = NULL ;
2009-03-30 03:15:41 +00:00
$element [ '#theme_wrapper' ] = 'text_format_wrapper' ;
2009-01-21 16:58:42 +00:00
$element [ 'format' ] = filter_form ( $element [ '#text_format' ], 1 , $element_parents );
2008-09-27 19:47:43 +00:00
2009-01-21 16:58:42 +00:00
// We need to clear the #text_format from the new child otherwise we
2008-09-27 19:47:43 +00:00
// would get into an infinite loop.
2009-01-21 16:58:42 +00:00
unset ( $element [ 'value' ][ '#text_format' ]);
2008-09-27 19:47:43 +00:00
}
return $element ;
}
2009-03-30 03:15:41 +00:00
/**
* Return a themed text format form element .
*
* @ param element
* An associative array containing the properties of the element .
* Properties used : children , description
* @ return
* A string representing the form element .
*
* @ ingroup themeable
*/
function theme_text_format_wrapper ( $element ) {
$output = '<div class="text-format-wrapper">' . " \n " ;
$output .= $element [ '#children' ] . " \n " ;
if ( ! empty ( $element [ '#description' ])) {
$output .= '<div class="description">' . $element [ '#description' ] . " </div> \n " ;
}
$output .= " </div> \n " ;
return $output ;
}
2007-07-04 15:42:38 +00:00
/**
* Add AHAH information about a form element to the page to communicate with
2007-10-05 09:35:09 +00:00
* javascript . If #ahah[path] is set on an element, this additional javascript is
2007-07-04 15:42:38 +00:00
* added to the page header to attach the AHAH behaviors . See ahah . js for more
* information .
*
* @ param $element
* An associative array containing the properties of the element .
* Properties used : ahah_event , ahah_path , ahah_wrapper , ahah_parameters ,
* ahah_effect .
* @ return
* None . Additional code is added to the header of the page using
* drupal_add_js .
*/
2008-07-18 07:06:24 +00:00
function form_process_ahah ( $element ) {
2009-06-02 13:47:26 +00:00
$js_added = & drupal_static ( __FUNCTION__ , array ());
2007-10-05 09:35:09 +00:00
// Add a reasonable default event handler if none specified.
2009-02-01 06:48:15 +00:00
if ( isset ( $element [ '#ahah' ]) && ! isset ( $element [ '#ahah' ][ 'event' ])) {
2007-10-05 09:35:09 +00:00
switch ( $element [ '#type' ]) {
case 'submit' :
case 'button' :
case 'image_button' :
2008-02-12 13:52:33 +00:00
// Use the mousedown instead of the click event because form
// submission via pressing the enter key triggers a click event on
// submit inputs, inappropriately triggering AHAH behaviors.
$element [ '#ahah' ][ 'event' ] = 'mousedown' ;
2008-12-30 16:43:20 +00:00
// Attach an additional event handler so that AHAH behaviors
2008-02-12 13:52:33 +00:00
// can be triggered still via keyboard input.
$element [ '#ahah' ][ 'keypress' ] = TRUE ;
2007-10-05 09:35:09 +00:00
break ;
case 'password' :
case 'textfield' :
case 'textarea' :
$element [ '#ahah' ][ 'event' ] = 'blur' ;
break ;
case 'radio' :
case 'checkbox' :
case 'select' :
$element [ '#ahah' ][ 'event' ] = 'change' ;
break ;
2008-09-27 19:47:43 +00:00
default :
return $element ;
2007-10-05 09:35:09 +00:00
}
}
2007-07-04 15:42:38 +00:00
// Adding the same javascript settings twice will cause a recursion error,
// we avoid the problem by checking if the javascript has already been added.
2009-02-01 06:48:15 +00:00
if (( isset ( $element [ '#ahah' ][ 'callback' ]) || isset ( $element [ '#ahah' ][ 'path' ])) && isset ( $element [ '#ahah' ][ 'event' ]) && ! isset ( $js_added [ $element [ '#id' ]])) {
2008-11-10 05:23:01 +00:00
drupal_add_js ( 'misc/jquery.form.js' , array ( 'weight' => JS_LIBRARY ));
2007-07-04 15:42:38 +00:00
drupal_add_js ( 'misc/ahah.js' );
$ahah_binding = array (
2009-02-01 06:48:15 +00:00
'url' => isset ( $element [ '#ahah' ][ 'callback' ]) ? url ( 'system/ahah' ) : url ( $element [ '#ahah' ][ 'path' ]),
2007-11-19 10:05:48 +00:00
'event' => $element [ '#ahah' ][ 'event' ],
2008-02-12 13:52:33 +00:00
'keypress' => empty ( $element [ '#ahah' ][ 'keypress' ]) ? NULL : $element [ '#ahah' ][ 'keypress' ],
2007-11-19 10:05:48 +00:00
'wrapper' => empty ( $element [ '#ahah' ][ 'wrapper' ]) ? NULL : $element [ '#ahah' ][ 'wrapper' ],
2008-04-14 17:48:46 +00:00
'selector' => empty ( $element [ '#ahah' ][ 'selector' ]) ? '#' . $element [ '#id' ] : $element [ '#ahah' ][ 'selector' ],
2007-10-05 09:35:09 +00:00
'effect' => empty ( $element [ '#ahah' ][ 'effect' ]) ? 'none' : $element [ '#ahah' ][ 'effect' ],
'method' => empty ( $element [ '#ahah' ][ 'method' ]) ? 'replace' : $element [ '#ahah' ][ 'method' ],
2007-10-10 10:24:25 +00:00
'progress' => empty ( $element [ '#ahah' ][ 'progress' ]) ? array ( 'type' => 'throbber' ) : $element [ '#ahah' ][ 'progress' ],
2008-01-02 15:18:15 +00:00
'button' => isset ( $element [ '#executes_submit_callback' ]) ? array ( $element [ '#name' ] => $element [ '#value' ]) : FALSE ,
2007-07-04 15:42:38 +00:00
);
2007-10-10 10:24:25 +00:00
// Convert a simple #ahah[progress] type string into an array.
if ( is_string ( $ahah_binding [ 'progress' ])) {
$ahah_binding [ 'progress' ] = array ( 'type' => $ahah_binding [ 'progress' ]);
}
// Change progress path to a full url.
if ( isset ( $ahah_binding [ 'progress' ][ 'path' ])) {
$ahah_binding [ 'progress' ][ 'url' ] = url ( $ahah_binding [ 'progress' ][ 'path' ]);
}
// Add progress.js if we're doing a bar display.
if ( $ahah_binding [ 'progress' ][ 'type' ] == 'bar' ) {
2008-11-10 05:23:01 +00:00
drupal_add_js ( 'misc/progress.js' , array ( 'cache' => FALSE ));
2007-10-10 10:24:25 +00:00
}
2007-07-04 15:42:38 +00:00
drupal_add_js ( array ( 'ahah' => array ( $element [ '#id' ] => $ahah_binding )), 'setting' );
$js_added [ $element [ '#id' ]] = TRUE ;
2007-11-19 19:23:28 +00:00
$element [ '#cache' ] = TRUE ;
2007-07-04 15:42:38 +00:00
}
return $element ;
}
2005-10-07 06:11:12 +00:00
/**
* Format a checkbox .
*
* @ param $element
* An associative array containing the properties of the element .
2006-01-18 19:04:12 +00:00
* Properties used : title , value , return_value , description , required
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the checkbox .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
function theme_checkbox ( $element ) {
2006-04-05 18:12:48 +00:00
_form_set_class ( $element , array ( 'form-checkbox' ));
2005-10-07 06:11:12 +00:00
$checkbox = '<input ' ;
$checkbox .= 'type="checkbox" ' ;
2008-04-14 17:48:46 +00:00
$checkbox .= 'name="' . $element [ '#name' ] . '" ' ;
$checkbox .= 'id="' . $element [ '#id' ] . '" ' ;
$checkbox .= 'value="' . $element [ '#return_value' ] . '" ' ;
2006-04-13 19:50:13 +00:00
$checkbox .= $element [ '#value' ] ? ' checked="checked" ' : ' ' ;
2008-04-14 17:48:46 +00:00
$checkbox .= drupal_attributes ( $element [ '#attributes' ]) . ' />' ;
2005-10-11 19:44:35 +00:00
if ( ! is_null ( $element [ '#title' ])) {
2009-01-18 06:51:41 +00:00
$checkbox = '<label class="option" for="' . $element [ '#id' ] . '">' . $checkbox . ' ' . $element [ '#title' ] . '</label>' ;
2005-10-07 06:11:12 +00:00
}
2009-02-03 18:55:32 +00:00
return $checkbox ;
2005-10-07 06:11:12 +00:00
}
/**
* Format a set of checkboxes .
*
* @ param $element
* An associative array containing the properties of the element .
* @ return
* A themed HTML string representing the checkbox set .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
function theme_checkboxes ( $element ) {
2006-12-12 10:01:38 +00:00
$class = 'form-checkboxes' ;
if ( isset ( $element [ '#attributes' ][ 'class' ])) {
2008-04-14 17:48:46 +00:00
$class .= ' ' . $element [ '#attributes' ][ 'class' ];
2006-12-12 10:01:38 +00:00
}
2008-04-14 17:48:46 +00:00
$element [ '#children' ] = '<div class="' . $class . '">' . ( ! empty ( $element [ '#children' ]) ? $element [ '#children' ] : '' ) . '</div>' ;
2009-03-14 20:13:27 +00:00
2009-02-03 18:55:32 +00:00
return $element [ '#children' ];
}
/**
* Add form_element theming to an element if title or desription is set .
*
* This is used as a pre render function for checkboxes and radios .
*/
function form_pre_render_conditional_form_element ( $element ) {
2005-10-11 19:44:35 +00:00
if ( $element [ '#title' ] || $element [ '#description' ]) {
2006-05-02 09:26:33 +00:00
unset ( $element [ '#id' ]);
2009-02-03 18:55:32 +00:00
$element [ '#theme_wrapper' ] = '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
}
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
}
2005-10-11 19:44:35 +00:00
foreach ( $element [ '#options' ] as $key => $choice ) {
2005-10-07 06:11:12 +00:00
if ( ! isset ( $element [ $key ])) {
2008-10-30 02:35:54 +00:00
$element [ $key ] = array (
'#type' => 'checkbox' ,
'#processed' => TRUE ,
'#title' => $choice ,
'#return_value' => $key ,
'#default_value' => isset ( $value [ $key ]),
'#attributes' => $element [ '#attributes' ],
'#ahah' => isset ( $element [ '#ahah' ]) ? $element [ '#ahah' ] : NULL ,
);
2005-10-07 06:11:12 +00:00
}
}
}
return $element ;
}
2009-01-28 07:43:26 +00:00
/**
* Format a table with radio buttons or checkboxes .
*
* @ param $element
* An associative array containing the properties and children of the
* tableselect element .
* Properties used : header , options , empty , js_select .
* @ return
* A themed HTML string representing the table .
*
* @ ingroup themeable
*/
function theme_tableselect ( $element ) {
$rows = array ();
if ( ! empty ( $element [ '#options' ])) {
// Generate a table row for each selectable item in #options.
foreach ( $element [ '#options' ] as $key => $value ) {
$row = array ();
// Render the checkbox / radio element.
2009-02-03 18:55:32 +00:00
$row [] = 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 ) {
$row [] = $element [ '#options' ][ $key ][ $fieldname ];
}
$rows [] = $row ;
}
// Add an empty header or a "Select all" checkbox to provide room for the
// checkboxes/radios in the first table column.
$first_col = $element [ '#js_select' ] ? array ( theme ( 'table_select_header_cell' )) : array ( '' );
$header = array_merge ( $first_col , $element [ '#header' ]);
}
else {
// If there are no selectable options, display the empty text over the
// entire width of the table.
$header = $element [ '#header' ];
$rows [] = array ( array ( 'data' => $element [ '#empty' ], 'colspan' => count ( $header )));
}
return theme ( 'table' , $header , $rows );
}
/**
* Create the correct amount of checkbox or radio elements to populate the table .
*
* @ param $element
* An associative array containing the properties and children of the
* tableselect element .
* @ return
* The processed element .
*/
function form_process_tableselect ( $element ) {
if ( $element [ '#multiple' ]) {
$value = is_array ( $element [ '#value' ]) ? $element [ '#value' ] : array ();
}
else {
// Advanced selection behaviour make no sense for radios.
$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' ]) {
$element [ $key ] = array (
'#type' => 'checkbox' ,
'#title' => '' ,
'#return_value' => $key ,
'#default_value' => isset ( $value [ $key ]),
'#attributes' => $element [ '#attributes' ],
'#ahah' => isset ( $element [ '#ahah' ]) ? $element [ '#ahah' ] : NULL ,
);
}
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' ],
'#id' => form_clean_id ( 'edit-' . implode ( '-' , $parents_for_id )),
'#ahah' => isset ( $element [ '#ahah' ]) ? $element [ '#ahah' ] : NULL ,
);
}
}
}
}
else {
$element [ '#value' ] = array ();
}
return $element ;
}
2009-04-11 22:19:46 +00:00
/**
* Adds fieldsets to the specified group or adds group members to this
* fieldset .
*
* @ param $element
* An associative array containing the properties and children of the
* fieldset .
* @ param $form_state
* The $form_state array for the form this fieldset belongs to .
* @ return
* The processed element .
*/
function form_process_fieldset ( & $element , & $form_state ) {
$parents = implode ( '][' , $element [ '#parents' ]);
// Add this fieldset to a group if one is set and if it's not being
// added to itself.
if ( isset ( $element [ '#group' ]) && $element [ '#group' ] != $parents ) {
if ( isset ( $form_state [ 'groups' ][ $element [ '#group' ]]) && ! empty ( $form_state [ 'groups' ][ $element [ '#group' ]][ '#group_exists' ])) {
// Trick drupal_render() into believing this has already been output.
// The group widget will rerender this later. This only happens when the
// group is actually defined ('#group_exists' is TRUE). This prevents
// fieldsets from disappearing when the group they are associated to
// does not exist.
// If the group does not exist yet, the element's #printed value is left
// as is. As soon as the group is processed (fieldsets are also groups;
// see below), this element's #printed value is set to TRUE to prevent
// rendering in the original context.
$element [ '#printed' ] = TRUE ;
}
// Store a reference to this fieldset for the vertical tabs processing
// function.
$form_state [ 'groups' ][ $element [ '#group' ]][] = & $element ;
}
// Each fieldset can be a group itself and gets a reference to all
// elements in its group.
$form_state [ 'groups' ][ $parents ][ '#group_exists' ] = TRUE ;
// There might already be elements associated with this group. Since the
// group did not exist yet at the time they were added to this group, they
// couldn't set #printed to TRUE (see above). We now know that this group
// does in fact exist and set #printed to TRUE to prevent rendering in the
// original context.
foreach ( element_children ( $form_state [ 'groups' ][ $parents ]) as $key ) {
$form_state [ 'groups' ][ $parents ][ $key ][ '#printed' ] = TRUE ;
}
$element [ '#group_members' ] = & $form_state [ 'groups' ][ $parents ];
// Contains form element summary functionalities.
drupal_add_js ( 'misc/form.js' , array ( 'weight' => JS_LIBRARY + 1 ));
return $element ;
}
/**
* Adds members of this group as actual elements for rendering .
*
* @ param $element
* An associative array containing the properties and children of the
* fieldset .
* @ return
* The modified element with all group members .
*/
function form_pre_render_fieldset ( $element ) {
if ( ! empty ( $element [ '#group_members' ])) {
// Add the group members to this fieldset for rendering purposes only.
foreach ( element_children ( $element [ '#group_members' ]) as $key ) {
// This was set in form_process_fieldset so that fieldsets which are
// added to groups are not rendered at their original location.
// drupal_render_children() will set this back to TRUE.
unset ( $element [ '#group_members' ][ $key ][ '#printed' ]);
$element [] = & $element [ '#group_members' ][ $key ];
}
// Resort the element's children after the group members have been added.
$element [ '#sorted' ] = FALSE ;
}
return $element ;
}
/**
* Creates a group formatted as vertical tabs .
*
* @ param $element
* An associative array containing the properties and children of the
* fieldset .
* @ param $form_state
* The $form_state array for the form this vertical tab widget belongs to .
* @ return
* The processed element .
*/
function form_process_vertical_tabs ( $element , & $form_state ) {
// To save us from modifying the existing element and changing its #type,
// a new form element is created as a child. The default #process hooks
// are called automatically by the form renderer and we don't have to do
// that manually.
$element [ 'group' ] = array (
'#type' => 'fieldset' ,
'#theme_wrapper' => '' ,
'#parents' => $element [ '#parents' ],
);
// 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' ],
'#attributes' => array ( 'class' => 'vertical-tabs-active-tab' ),
);
return $element ;
}
/**
* Makes the element ' s children fieldsets be vertical tabs .
*
* @ param $element
* An associative array containing the properties and children of the
* fieldset .
*/
2009-05-09 18:28:13 +00:00
function theme_vertical_tabs ( $element ) {
2009-04-11 22:19:46 +00:00
// Add required JavaScript and Stylesheet.
drupal_add_js ( 'misc/vertical-tabs.js' , array ( 'weight' => JS_DEFAULT - 1 ));
drupal_add_css ( 'misc/vertical-tabs.css' );
return '<div class="vertical-tabs-panes">' . $element [ '#children' ] . '</div>' ;
}
2007-12-06 09:58:34 +00:00
/**
* Theme a form submit button .
*
* @ ingroup themeable
*/
2005-10-07 06:11:12 +00:00
function theme_submit ( $element ) {
return theme ( 'button' , $element );
}
2007-12-06 09:58:34 +00:00
/**
* Theme a form button .
*
* @ ingroup themeable
*/
2005-10-07 06:11:12 +00:00
function theme_button ( $element ) {
2007-01-15 04:09:40 +00:00
// Make sure not to overwrite classes.
2006-03-16 15:32:44 +00:00
if ( isset ( $element [ '#attributes' ][ 'class' ])) {
2008-04-14 17:48:46 +00:00
$element [ '#attributes' ][ 'class' ] = 'form-' . $element [ '#button_type' ] . ' ' . $element [ '#attributes' ][ 'class' ];
2006-03-16 15:32:44 +00:00
}
else {
2008-04-14 17:48:46 +00:00
$element [ '#attributes' ][ 'class' ] = 'form-' . $element [ '#button_type' ];
2006-03-16 15:32:44 +00:00
}
2006-03-29 23:29:41 +00:00
2008-04-14 17:48:46 +00:00
return '<input type="submit" ' . ( empty ( $element [ '#name' ]) ? '' : 'name="' . $element [ '#name' ] . '" ' ) . 'id="' . $element [ '#id' ] . '" value="' . check_plain ( $element [ '#value' ]) . '" ' . drupal_attributes ( $element [ '#attributes' ]) . " /> \n " ;
2005-10-07 06:11:12 +00:00
}
2007-07-29 17:28:23 +00:00
/**
2007-12-06 09:58:34 +00:00
* Theme a form image button .
*
* @ ingroup themeable
2007-07-29 17:28:23 +00:00
*/
function theme_image_button ( $element ) {
// Make sure not to overwrite classes.
if ( isset ( $element [ '#attributes' ][ 'class' ])) {
2008-04-14 17:48:46 +00:00
$element [ '#attributes' ][ 'class' ] = 'form-' . $element [ '#button_type' ] . ' ' . $element [ '#attributes' ][ 'class' ];
2007-07-29 17:28:23 +00:00
}
else {
2008-04-14 17:48:46 +00:00
$element [ '#attributes' ][ 'class' ] = 'form-' . $element [ '#button_type' ];
2007-07-29 17:28:23 +00:00
}
2008-04-14 17:48:46 +00:00
return '<input type="image" name="' . $element [ '#name' ] . '" ' .
( ! empty ( $element [ '#value' ]) ? ( 'value="' . check_plain ( $element [ '#value' ]) . '" ' ) : '' ) .
'id="' . $element [ '#id' ] . '" ' .
2007-07-29 17:28:23 +00:00
drupal_attributes ( $element [ '#attributes' ]) .
2008-04-14 17:48:46 +00:00
' src="' . base_path () . $element [ '#src' ] . '" ' .
( ! empty ( $element [ '#title' ]) ? 'alt="' . check_plain ( $element [ '#title' ]) . '" title="' . check_plain ( $element [ '#title' ]) . '" ' : '' ) .
2007-07-29 17:28:23 +00:00
" /> \n " ;
}
2005-10-07 06:11:12 +00:00
/**
* Format a hidden form field .
*
* @ param $element
* An associative array containing the properties of the element .
* @ return
* A themed HTML string representing the hidden form field .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
function theme_hidden ( $element ) {
2008-04-14 17:48:46 +00:00
return '<input type="hidden" name="' . $element [ '#name' ] . '" id="' . $element [ '#id' ] . '" value="' . check_plain ( $element [ '#value' ]) . " \" " . drupal_attributes ( $element [ '#attributes' ]) . " /> \n " ;
2005-10-07 06:11:12 +00:00
}
/**
* Format a textfield .
*
* @ param $element
* An associative array containing the properties of the element .
2006-01-18 19:04:12 +00:00
* Properties used : title , value , description , size , maxlength , required , attributes autocomplete_path
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the textfield .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
function theme_textfield ( $element ) {
2008-04-14 17:48:46 +00:00
$size = empty ( $element [ '#size' ]) ? '' : ' size="' . $element [ '#size' ] . '"' ;
$maxlength = empty ( $element [ '#maxlength' ]) ? '' : ' maxlength="' . $element [ '#maxlength' ] . '"' ;
2006-04-05 18:12:48 +00:00
$class = array ( 'form-text' );
2006-01-24 08:20:45 +00:00
$extra = '' ;
2006-08-26 09:56:17 +00:00
$output = '' ;
2008-10-10 07:49:49 +00:00
if ( $element [ '#autocomplete_path' ] && menu_valid_path ( array ( 'link_path' => $element [ '#autocomplete_path' ]))) {
2005-10-07 06:11:12 +00:00
drupal_add_js ( 'misc/autocomplete.js' );
2006-04-05 18:12:48 +00:00
$class [] = 'form-autocomplete' ;
2008-04-14 17:48:46 +00:00
$extra = '<input class="autocomplete" type="hidden" id="' . $element [ '#id' ] . '-autocomplete" value="' . check_url ( url ( $element [ '#autocomplete_path' ], array ( 'absolute' => TRUE ))) . '" disabled="disabled" />' ;
2005-10-07 06:11:12 +00:00
}
2006-04-05 18:12:48 +00:00
_form_set_class ( $element , $class );
2006-08-26 09:56:17 +00:00
if ( isset ( $element [ '#field_prefix' ])) {
2008-04-14 17:48:46 +00:00
$output .= '<span class="field-prefix">' . $element [ '#field_prefix' ] . '</span> ' ;
2006-08-26 09:56:17 +00:00
}
2008-04-14 17:48:46 +00:00
$output .= '<input type="text"' . $maxlength . ' name="' . $element [ '#name' ] . '" id="' . $element [ '#id' ] . '"' . $size . ' value="' . check_plain ( $element [ '#value' ]) . '"' . drupal_attributes ( $element [ '#attributes' ]) . ' />' ;
2006-08-26 09:56:17 +00:00
if ( isset ( $element [ '#field_suffix' ])) {
2008-04-14 17:48:46 +00:00
$output .= ' <span class="field-suffix">' . $element [ '#field_suffix' ] . '</span>' ;
2006-08-26 09:56:17 +00:00
}
2009-02-03 18:55:32 +00:00
return $output . $extra ;
2005-10-07 06:11:12 +00:00
}
/**
* Format a form .
*
* @ param $element
* An associative array containing the properties of the element .
2006-01-18 19:04:12 +00:00
* Properties used : action , method , attributes , children
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the form .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
function theme_form ( $element ) {
2006-04-11 11:33:15 +00:00
// Anonymous div to satisfy XHTML compliance.
2008-04-14 17:48:46 +00:00
$action = $element [ '#action' ] ? 'action="' . check_url ( $element [ '#action' ]) . '" ' : '' ;
return '<form ' . $action . ' accept-charset="UTF-8" method="' . $element [ '#method' ] . '" id="' . $element [ '#id' ] . '"' . drupal_attributes ( $element [ '#attributes' ]) . " > \n <div> " . $element [ '#children' ] . " \n </div></form> \n " ;
2005-10-07 06:11:12 +00:00
}
/**
* Format a textarea .
*
* @ param $element
* An associative array containing the properties of the element .
2006-01-18 19:04:12 +00:00
* Properties used : title , value , description , rows , cols , required , attributes
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the textarea .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
function theme_textarea ( $element ) {
2006-04-07 13:22:12 +00:00
$class = array ( 'form-textarea' );
2007-04-09 13:58:03 +00:00
2007-10-21 18:59:02 +00:00
// Add teaser behavior (must come before resizable)
2007-04-09 13:58:03 +00:00
if ( ! empty ( $element [ '#teaser' ])) {
drupal_add_js ( 'misc/teaser.js' );
// Note: arrays are merged in drupal_get_js().
drupal_add_js ( array ( 'teaserCheckbox' => array ( $element [ '#id' ] => $element [ '#teaser_checkbox' ])), 'setting' );
drupal_add_js ( array ( 'teaser' => array ( $element [ '#id' ] => $element [ '#teaser' ])), 'setting' );
$class [] = 'teaser' ;
}
2007-10-21 18:59:02 +00:00
// Add resizable behavior
2006-07-05 11:45:51 +00:00
if ( $element [ '#resizable' ] !== FALSE ) {
2005-12-29 03:59:30 +00:00
drupal_add_js ( 'misc/textarea.js' );
2006-04-05 18:12:48 +00:00
$class [] = 'resizable' ;
2005-12-29 03:59:30 +00:00
}
2006-04-05 18:12:48 +00:00
_form_set_class ( $element , $class );
2009-02-03 18:55:32 +00:00
return '<textarea cols="' . $element [ '#cols' ] . '" rows="' . $element [ '#rows' ] . '" name="' . $element [ '#name' ] . '" id="' . $element [ '#id' ] . '" ' . drupal_attributes ( $element [ '#attributes' ]) . '>' . check_plain ( $element [ '#value' ]) . '</textarea>' ;
2005-10-07 06:11:12 +00:00
}
/**
* Format HTML markup for use in forms .
*
* @ param $element
* An associative array containing the properties of the element .
2009-03-08 21:25:18 +00:00
* Properties used : markup , children .
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the HTML markup .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2005-10-07 06:11:12 +00:00
*/
function theme_markup ( $element ) {
2009-02-03 18:55:32 +00:00
return ( ! empty ( $element [ '#markup' ]) ? $element [ '#markup' ] : '' ) . drupal_render_children ( $element );
2005-10-07 06:11:12 +00:00
}
/**
2007-05-04 09:41:37 +00:00
* Format a password field .
*
* @ param $element
* An associative array containing the properties of the element .
* Properties used : title , value , description , size , maxlength , required , attributes
* @ return
* A themed HTML string representing the form .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2007-05-04 09:41:37 +00:00
*/
2005-10-07 06:11:12 +00:00
function theme_password ( $element ) {
2008-04-14 17:48:46 +00:00
$size = $element [ '#size' ] ? ' size="' . $element [ '#size' ] . '" ' : '' ;
$maxlength = $element [ '#maxlength' ] ? ' maxlength="' . $element [ '#maxlength' ] . '" ' : '' ;
2005-10-07 06:11:12 +00:00
2006-04-05 18:12:48 +00:00
_form_set_class ( $element , array ( 'form-text' ));
2008-04-14 17:48:46 +00:00
$output = '<input type="password" name="' . $element [ '#name' ] . '" id="' . $element [ '#id' ] . '" ' . $maxlength . $size . drupal_attributes ( $element [ '#attributes' ]) . ' />' ;
2009-02-03 18:55:32 +00:00
return $output ;
2005-10-07 06:11:12 +00:00
}
/**
2006-05-04 09:57:14 +00:00
* Expand weight elements into selects .
2005-10-07 06:11:12 +00:00
*/
2008-07-18 07:06:24 +00:00
function form_process_weight ( $element ) {
2005-10-11 19:44:35 +00:00
for ( $n = ( - 1 * $element [ '#delta' ]); $n <= $element [ '#delta' ]; $n ++ ) {
2005-10-07 06:11:12 +00:00
$weights [ $n ] = $n ;
}
2005-10-11 19:44:35 +00:00
$element [ '#options' ] = $weights ;
$element [ '#type' ] = 'select' ;
2006-05-04 09:57:14 +00:00
$element [ '#is_weight' ] = TRUE ;
2009-02-03 18:55:32 +00:00
$element += element_info ( 'select' );
2006-05-04 09:57:14 +00:00
return $element ;
2005-10-07 06:11:12 +00:00
}
/**
* Format a file upload field .
*
* @ param $title
* The label for the file upload field .
* @ param $name
* The internal name used to refer to the field .
* @ param $size
* A measure of the visible size of the field ( passed directly to HTML ) .
* @ param $description
* Explanatory text to display after the form item .
* @ param $required
* Whether the user must upload a file to the field .
* @ return
* A themed HTML string representing the field .
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
*
2005-10-07 06:11:12 +00:00
* For assistance with handling the uploaded file correctly , see the API
* provided by file . inc .
*/
function theme_file ( $element ) {
2006-04-05 18:12:48 +00:00
_form_set_class ( $element , array ( 'form-file' ));
2009-02-03 18:55:32 +00:00
return '<input type="file" name="' . $element [ '#name' ] . '"' . ( $element [ '#attributes' ] ? ' ' . drupal_attributes ( $element [ '#attributes' ]) : '' ) . ' id="' . $element [ '#id' ] . '" size="' . $element [ '#size' ] . " \" /> \n " ;
2006-05-02 09:26:33 +00:00
}
/**
* Return a themed form element .
*
* @ param element
* An associative array containing the properties of the element .
2009-02-03 18:55:32 +00:00
* Properties used : title , description , id , required , children
2006-05-02 09:26:33 +00:00
* @ return
2007-01-23 19:17:55 +00:00
* A string representing the form element .
2007-12-06 09:58:34 +00:00
*
* @ ingroup themeable
2006-05-02 09:26:33 +00:00
*/
2009-02-03 18:55:32 +00:00
function theme_form_element ( $element ) {
2007-11-11 16:14:45 +00:00
// This is also used in the installer, pre-database setup.
$t = get_t ();
2007-11-13 14:04:08 +00:00
2007-12-22 23:24:26 +00:00
$output = '<div class="form-item"' ;
2007-03-25 19:57:56 +00:00
if ( ! empty ( $element [ '#id' ])) {
2008-04-14 17:48:46 +00:00
$output .= ' id="' . $element [ '#id' ] . '-wrapper"' ;
2007-03-25 19:57:56 +00:00
}
$output .= " > \n " ;
2008-04-14 17:48:46 +00:00
$required = ! empty ( $element [ '#required' ]) ? '<span class="form-required" title="' . $t ( 'This field is required.' ) . '">*</span>' : '' ;
2006-05-02 09:26:33 +00:00
2009-02-03 18:55:32 +00:00
if ( ! empty ( $element [ '#title' ]) && empty ( $element [ '#form_element_skip_title' ])) {
2006-05-02 09:26:33 +00:00
$title = $element [ '#title' ];
if ( ! empty ( $element [ '#id' ])) {
2008-04-14 17:48:46 +00:00
$output .= ' <label for="' . $element [ '#id' ] . '">' . $t ( '!title: !required' , array ( '!title' => filter_xss_admin ( $title ), '!required' => $required )) . " </label> \n " ;
2006-05-02 09:26:33 +00:00
}
else {
2008-04-14 17:48:46 +00:00
$output .= ' <label>' . $t ( '!title: !required' , array ( '!title' => filter_xss_admin ( $title ), '!required' => $required )) . " </label> \n " ;
2006-05-02 09:26:33 +00:00
}
}
2009-02-03 18:55:32 +00:00
$output .= " " . $element [ '#children' ] . " \n " ;
2006-05-02 09:26:33 +00:00
if ( ! empty ( $element [ '#description' ])) {
2008-04-14 17:48:46 +00:00
$output .= ' <div class="description">' . $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
2006-04-05 18:12:48 +00:00
/**
* Sets a form element ' s class attribute .
*
* Adds 'required' and 'error' classes as needed .
*
* @ 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
*/
function _form_set_class ( & $element , $class = array ()) {
if ( $element [ '#required' ]) {
$class [] = 'required' ;
}
2007-04-13 08:56:59 +00:00
if ( form_get_error ( $element )) {
2006-04-05 18:12:48 +00:00
$class [] = 'error' ;
}
if ( isset ( $element [ '#attributes' ][ 'class' ])) {
$class [] = $element [ '#attributes' ][ 'class' ];
}
$element [ '#attributes' ][ 'class' ] = implode ( ' ' , $class );
2005-10-07 06:11:12 +00:00
}
/**
2007-10-25 15:32:56 +00:00
* Prepare an HTML ID attribute string for a form item .
2007-10-24 13:35:26 +00:00
*
* Remove invalid characters and guarantee uniqueness .
2005-10-07 06:11:12 +00:00
*
* @ param $id
2007-01-23 19:17:55 +00:00
* The ID to clean .
2007-10-24 13:35:26 +00:00
* @ param $flush
* If set to TRUE , the function will flush and reset the static array
* which is built to test the uniqueness of element IDs . This is only
* used if a form has completed the validation process . This parameter
* should never be set to TRUE if this function is being called to
* assign an ID to the #ID element.
2005-10-07 06:11:12 +00:00
* @ return
2007-01-23 19:17:55 +00:00
* The cleaned ID .
2005-10-07 06:11:12 +00:00
*/
2009-06-02 13:47:26 +00:00
function form_clean_id ( $id = NULL ) {
$seen_ids = & drupal_static ( __FUNCTION__ , array ());
2006-12-12 09:39:50 +00:00
$id = str_replace ( array ( '][' , '_' , ' ' ), '-' , $id );
2007-10-24 13:35:26 +00:00
// Ensure IDs are unique. The first occurrence is held but left alone.
// Subsequent occurrences get a number appended to them. This incrementing
// will almost certainly break code that relies on explicit HTML IDs in
// forms that appear more than once on the page, but the alternative is
// outputting duplicate IDs, which would break JS code and XHTML
// validity anyways. For now, it's an acceptable stopgap solution.
if ( isset ( $seen_ids [ $id ])) {
2008-04-14 17:48:46 +00:00
$id = $id . '-' . $seen_ids [ $id ] ++ ;
2007-10-24 13:35:26 +00:00
}
else {
$seen_ids [ $id ] = 1 ;
}
2005-10-07 06:11:12 +00:00
return $id ;
}
/**
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
* @ {
* 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
* workflow , but can also be used by non - FAPI scripts ( like update . php )
* or even simple page callbacks ( which should probably be used sparingly ) .
*
* Example :
* @ code
* $batch = array (
* 'title' => t ( 'Exporting' ),
* 'operations' => array (
* array ( 'my_function_1' , array ( $account -> uid , 'story' )),
* array ( 'my_function_2' , array ()),
* ),
* 'finished' => 'my_finished_callback' ,
* );
* batch_set ( $batch );
2007-05-14 13:43:38 +00:00
* // only needed if not inside a form _submit handler :
2007-05-04 09:41:37 +00:00
* batch_process ();
* @ endcode
*
2008-10-14 11:01:08 +00:00
* Note - if the batch 'title' , 'init_message' , 'progress_message' ,
* or 'error_message' could contain any user input , it is the responsibility of
* the code calling batch_set () to sanitize them first with a function like
* check_plain () or filter_xss () .
*
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.
*
2007-05-04 09:41:37 +00:00
* $node = node_load ( array ( 'uid' => $uid , 'type' => $type ));
2008-04-14 17:48:46 +00:00
* $context [ 'results' ][] = $node -> nid . ' : ' . $node -> title ;
2007-05-04 09:41:37 +00:00
* $context [ 'message' ] = $node -> title ;
* }
*
2007-07-02 14:41:37 +00:00
* // More advanced example: multi-step operation - load all nodes, five by five
2007-05-04 09:41:37 +00:00
* function my_function_2 ( & $context ) {
* if ( empty ( $context [ 'sandbox' ])) {
* $context [ 'sandbox' ][ 'progress' ] = 0 ;
* $context [ 'sandbox' ][ 'current_node' ] = 0 ;
2009-03-14 16:27:58 +00:00
* $context [ 'sandbox' ][ 'max' ] = db_query ( 'SELECT COUNT(DISTINCT nid) FROM {node}' ) -> fetchField ();
2007-05-04 09:41:37 +00:00
* }
* $limit = 5 ;
2009-03-14 16:27:58 +00:00
* $result = db_select ( 'node' )
* -> fields ( 'node' , array ( 'nid' ))
* -> condition ( 'nid' , $context [ 'sandbox' ][ 'current_node' ], '>' )
* -> orderBy ( 'nid' )
* -> range ( 0 , $limit )
* -> execute ();
* foreach ( $result as $row ) {
* $node = node_load ( $row -> nid , NULL , TRUE );
2008-04-14 17:48:46 +00:00
* $context [ 'results' ][] = $node -> nid . ' : ' . $node -> title ;
2007-05-04 09:41:37 +00:00
* $context [ 'sandbox' ][ 'progress' ] ++ ;
* $context [ 'sandbox' ][ 'current_node' ] = $node -> nid ;
* $context [ 'message' ] = $node -> title ;
* }
* 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 ) {
* 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
*/
/**
* Open a new batch .
*
* @ param $batch
* An array defining the batch . The following keys can be used :
* 'operations' : an array of function calls to be performed .
* Example :
* @ code
* array (
* array ( 'my_function_1' , array ( $arg1 )),
* array ( 'my_function_2' , array ( $arg2_1 , $arg2_2 )),
* )
* @ endcode
* All the other values below are optional .
* batch_init () provides default values for the messages .
2008-12-27 19:12:09 +00:00
* 'title' : title for the progress page . Only safe strings should be passed .
2007-05-04 09:41:37 +00:00
* Defaults to t ( 'Processing' ) .
* 'init_message' : message displayed while the processing is initialized .
* Defaults to t ( 'Initializing.' ) .
* 'progress_message' : message displayed while processing the batch .
2009-01-12 06:23:57 +00:00
* Available placeholders are @ current , @ remaining , @ total , @ percentage ,
* @ estimate and @ elapsed .
* Defaults to t ( 'Completed @current of @total.' ) .
2007-10-15 15:18:39 +00:00
* 'error_message' : message displayed if an error occurred while processing
* the batch .
2007-05-04 09:41:37 +00:00
* Defaults to t ( 'An error has occurred.' ) .
2007-10-15 15:18:39 +00:00
* 'finished' : the 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' : the path to the file containing the definitions of the
* 'operations' and 'finished' functions , for instance if they don ' t
* reside in the original '.module' file . The path should be relative to
* the base_path (), and thus should be built using drupal_get_path () .
2008-06-24 21:51:03 +00:00
* 'css' : an array of paths to CSS files to be used on the progress page .
2007-05-04 09:41:37 +00:00
*
* Operations are added as new batch sets . Batch sets are used to ensure
2007-07-02 14:41:37 +00:00
* clean code independence , ensuring that several batches submitted by
2007-05-04 09:41:37 +00:00
* different parts of the code ( core / contrib modules ) can be processed
* correctly while not interfering or having to cope with each other . Each
2007-11-26 16:36:44 +00:00
* batch set gets to specify his own UI messages , operates on its own set
* of operations and results , and triggers its own 'finished' callback .
2007-05-04 09:41:37 +00:00
* Batch sets are processed sequentially , with the progress bar starting
* fresh for every new set .
*/
function batch_set ( $batch_definition ) {
if ( $batch_definition ) {
$batch =& batch_get ();
// Initialize the batch
if ( empty ( $batch )) {
$batch = array (
'sets' => array (),
);
}
$init = array (
'sandbox' => array (),
'results' => array (),
'success' => FALSE ,
2009-01-12 06:23:57 +00:00
'start' => microtime ( TRUE ),
'elapsed' => 0 ,
2007-05-04 09:41:37 +00:00
);
// Use get_t() to allow batches at install time.
$t = get_t ();
$defaults = array (
'title' => $t ( 'Processing' ),
'init_message' => $t ( 'Initializing.' ),
2008-10-09 22:54:08 +00:00
'progress_message' => $t ( 'Completed @current of @total.' ),
2007-05-04 09:41:37 +00:00
'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 ;
// Tweak init_message to avoid the bottom of the page flickering down after init phase.
$batch_set [ 'init_message' ] .= '<br/> ' ;
$batch_set [ 'total' ] = count ( $batch_set [ 'operations' ]);
2007-05-14 13:43:38 +00:00
// If the batch is being processed (meaning we are executing a stored submit handler),
2007-05-04 09:41:37 +00:00
// insert the new set after the current one.
if ( isset ( $batch [ 'current_set' ])) {
// array_insert does not exist...
$slice1 = array_slice ( $batch [ 'sets' ], 0 , $batch [ 'current_set' ] + 1 );
$slice2 = array_slice ( $batch [ 'sets' ], $batch [ 'current_set' ] + 1 );
$batch [ 'sets' ] = array_merge ( $slice1 , array ( $batch_set ), $slice2 );
}
else {
$batch [ 'sets' ][] = $batch_set ;
}
}
}
/**
* Process the batch .
2007-05-06 05:47:52 +00:00
*
2007-07-02 14:41:37 +00:00
* Unless the batch has been marked with 'progressive' = FALSE , the function
2007-10-15 15:18:39 +00:00
* issues a drupal_goto and thus ends page execution .
2007-05-04 09:41:37 +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 .
*/
function batch_process ( $redirect = NULL , $url = NULL ) {
$batch =& batch_get ();
if ( isset ( $batch )) {
// Add process information
$url = isset ( $url ) ? $url : 'batch' ;
$process_info = array (
'current_set' => 0 ,
'progressive' => TRUE ,
'url' => isset ( $url ) ? $url : 'batch' ,
'source_page' => $_GET [ 'q' ],
'redirect' => $redirect ,
);
$batch += $process_info ;
if ( $batch [ 'progressive' ]) {
2007-07-20 05:44:13 +00:00
// Clear the way for the drupal_goto redirection to the batch processing
// page, by saving and unsetting the 'destination' if any, on both places
// drupal_goto looks for it.
2007-05-04 09:41:37 +00:00
if ( isset ( $_REQUEST [ 'destination' ])) {
$batch [ 'destination' ] = $_REQUEST [ 'destination' ];
unset ( $_REQUEST [ 'destination' ]);
}
2007-07-20 05:44:13 +00:00
// Initiate db storage in order to get a batch id. We have to provide
// at least an empty string for the (not null) 'token' column.
2008-10-27 10:34:09 +00:00
$batch [ 'id' ] = db_insert ( 'batch' )
-> fields ( array (
'token' => '' ,
'timestamp' => REQUEST_TIME ,
))
-> execute ();
2007-07-20 05:44:13 +00:00
// Now that we have a batch id, we can generate the redirection link in
// the generic error message.
$t = get_t ();
$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' )))));
// Actually store the batch data and the token generated form the batch id.
2008-10-27 10:34:09 +00:00
db_update ( 'batch' )
-> condition ( 'bid' , $batch [ 'id' ])
-> fields ( array (
'token' => drupal_get_token ( $batch [ 'id' ]),
'batch' => serialize ( $batch ),
))
-> execute ();
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 ;
2008-04-14 17:48:46 +00:00
drupal_goto ( $batch [ 'url' ], 'op=start&id=' . $batch [ 'id' ]);
2007-05-04 09:41:37 +00:00
}
else {
// Non-progressive execution: bypass the whole progressbar workflow
// and execute the batch in one pass.
2008-09-20 20:22:25 +00:00
require_once DRUPAL_ROOT . '/includes/batch.inc' ;
2007-05-04 09:41:37 +00:00
_batch_process ();
}
}
}
/**
2007-07-02 14:41:37 +00:00
* Retrieve the current batch .
2007-05-04 09:41:37 +00:00
*/
function & batch_get () {
2009-06-02 13:47:26 +00:00
$batch = & drupal_static ( __FUNCTION__ , array ());
2007-05-04 09:41:37 +00:00
return $batch ;
}
/**
* @ } End of " defgroup batch " .
*/