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
/**
* @ defgroup form Form generation
* @ {
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 .
*
* The drupal_get_form () function handles retrieving , processing , and
* displaying a rendered HTML form for modules automatically . For example :
*
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' );
*
* Forms can also be built and submitted programmatically without any user input
2006-11-26 22:35:20 +00:00
* using the drupal_execute () 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
* @ link http :// api . drupal . org / api / HEAD / file / developer / topics / forms_api_reference . html reference @ endlink
* and the @ link http :// api . drupal . org / api / HEAD / file / developer / topics / forms_api . html quickstart guide . @ endlink
2005-10-07 06:11:12 +00:00
*/
/**
2007-05-14 13:43:38 +00:00
* Retrieves a form from a constructor function , or from the cache if
* the form was built in a previous page - load . The form is then passesed
* on for processing , after and rendered for display if necessary .
2005-10-07 06:11:12 +00:00
*
* @ param $form_id
2006-08-18 18:58:47 +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
2007-05-14 13:43:38 +00:00
* different $form_id values to the proper form constructor function . Examples
2006-08-18 18:58:47 +00:00
* may be found in node_forms (), search_forms (), and user_forms () .
* @ param ...
2007-05-14 13:43:38 +00:00
* Any additional arguments needed by the form constructor function .
2006-08-18 18:58:47 +00:00
* @ return
* The rendered form .
*/
function drupal_get_form ( $form_id ) {
2007-05-14 13:43:38 +00:00
$form_state = array ( 'storage' => NULL , 'submitted' => FALSE );
$expire = max ( ini_get ( 'session.cookie_lifetime' ), 86400 );
$args = func_get_args ();
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 {
2007-05-14 13:43:38 +00:00
// If the incoming $_POST contains a form_build_id, we'll check the
// cache for a copy of the form in question. If it's there, we don't
// have to rebuild the form to proceed. In addition, if there is stored
// form_state data from a previous step, we'll retrieve it so it can
// be passed on to the form processing code.
if ( isset ( $_POST [ 'form_id' ]) && $_POST [ 'form_id' ] == $form_id && ! empty ( $_POST [ 'form_build_id' ])) {
if ( $cached = cache_get ( 'form_' . $_POST [ 'form_build_id' ], 'cache_form' )) {
$form = $cached -> data ;
if ( $cached = cache_get ( 'storage_' . $_POST [ 'form_build_id' ], 'cache_form' )) {
$form_state [ 'storage' ] = $cached -> data ;
}
}
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 )) {
2007-05-25 15:39:34 +00:00
$form_state [ 'post' ] = $_POST ;
2007-06-04 07:22:23 +00:00
array_shift ( $args );
array_unshift ( $args , $form_state );
array_unshift ( $args , $form_id );
2007-05-14 13:43:38 +00:00
$form = call_user_func_array ( 'drupal_retrieve_form' , $args );
2007-05-28 18:53:29 +00:00
$form_build_id = 'form-' . md5 ( mt_rand ());
2006-08-25 08:15:24 +00:00
$form [ '#build_id' ] = $form_build_id ;
2007-05-14 13:43:38 +00:00
drupal_prepare_form ( $form_id , $form , $form_state );
if ( ! empty ( $form [ '#cache' ])) {
cache_set ( 'form_' . $form_build_id , $form , 'cache_form' , $expire );
}
2007-05-25 15:39:34 +00:00
unset ( $form_state [ 'post' ]);
2006-08-25 08:15:24 +00:00
}
2007-05-14 13:43:38 +00:00
$form [ '#post' ] = $_POST ;
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 );
}
// 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.
//
// If $form_state['storage'] or $form_state['rebuild'] have been
// set by any submit or validate handlers, however, 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 [ 'rebuild' ]) || ! empty ( $form_state [ 'storage' ])) {
2007-06-04 07:22:23 +00:00
array_shift ( $args );
array_unshift ( $args , $form_state );
array_unshift ( $args , $form_id );
2007-05-14 13:43:38 +00:00
$form = call_user_func_array ( 'drupal_retrieve_form' , $args );
2006-08-25 08:15:24 +00:00
2007-05-14 13:43:38 +00:00
// We need a new build_id for the new version of the form.
2007-05-28 18:53:29 +00:00
$form_build_id = 'form-' . md5 ( mt_rand ());
2007-05-14 13:43:38 +00:00
$form [ '#build_id' ] = $form_build_id ;
drupal_prepare_form ( $form_id , $form , $form_state );
// Now, we cache the form structure so it can be retrieved later for
// validation. If $form_state['storage'] is populated, we'll also cache
// it so that it can be used to resume complex multi-step processes.
cache_set ( 'form_' . $form_build_id , $form , 'cache_form' , $expire );
if ( ! empty ( $form_state [ 'storage' ])) {
cache_set ( 'storage_' . $form_build_id , $form_state [ 'storage' ], 'cache_form' , $expire );
2006-12-06 15:49:45 +00:00
}
2007-05-14 13:43:38 +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.
$_POST = array ();
$form [ '#post' ] = array ();
drupal_process_form ( $form_id , $form , $form_state );
2006-12-06 15:49:45 +00:00
}
2007-05-14 13:43:38 +00:00
// If we haven't redirected to a new location by now, we want to
// render whatever form array is currently in hand.
return drupal_render_form ( $form_id , $form );
}
2006-12-06 15:49:45 +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-05-14 13:43:38 +00:00
* Any additional arguments needed by the form constructor function .
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' ;
* drupal_execute ( '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 ();
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' ;
* drupal_execute ( 'story_node_form' , $form_state , $node );
2006-08-31 14:59:28 +00:00
*/
2007-05-14 13:43:38 +00:00
function drupal_execute ( $form_id , & $form_state ) {
2006-08-31 14:59:28 +00:00
$args = func_get_args ();
2007-05-14 13:43:38 +00:00
// We do a bit of juggling here because drupal_retrieve_form() expects
// the $form_state to be the last parameter, while drupal_execute()
// always takes it in as the second parameter.
$args = array_slice ( $args , 3 );
2007-06-04 07:22:23 +00:00
$args [ 1 ] = $form_state ;
2006-08-31 14:59:28 +00:00
2007-05-14 13:43:38 +00:00
$form = call_user_func_array ( 'drupal_retrieve_form' , $args );
$form [ '#post' ] = $form_state [ 'values' ];
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-05-14 13:43:38 +00:00
* Any additional arguments needed by the form constructor function .
2006-08-18 18:58:47 +00:00
*/
2007-06-04 07:22:23 +00:00
function drupal_retrieve_form ( $form_id , & $form_state ) {
2006-08-18 18:58:47 +00:00
static $forms ;
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
// the constructor function itself. We shift out the first argument -- the
// $form_id itself -- from the list to pass into the constructor function,
2006-10-12 20:36:51 +00:00
// since it's already known.
2006-08-18 18:58:47 +00:00
$args = func_get_args ();
2006-10-12 20:36:51 +00:00
$saved_args = $args ;
2006-08-18 18:58:47 +00:00
array_shift ( $args );
2007-06-04 07:22:23 +00:00
if ( isset ( $form_state )) {
array_shift ( $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.
2006-08-18 18:58:47 +00:00
if ( ! 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' ];
}
}
2007-06-04 07:22:23 +00:00
array_unshift ( $args , $form_state );
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 );
// We store the original function arguments, rather than the final $arg
// value, so that form_alter functions can see what was originally
// passed to drupal_retrieve_form(). This allows the contents of #parameters
// to be saved and passed in at a later date to recreate the form.
2006-10-12 20:36:51 +00:00
$form [ '#parameters' ] = $saved_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
* includes the current persistant storage data for the form , and
* 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 ();
$form = form_builder ( $form_id , $form , $form_state );
if (( ! empty ( $form [ '#programmed' ])) || ( ! empty ( $form [ '#post' ]) && (( $form [ '#post' ][ 'form_id' ] == $form_id )))) {
drupal_validate_form ( $form_id , $form , $form_state );
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.
if ( variable_get ( 'cache' , CACHE_DISABLED ) == CACHE_DISABLED ) {
cache_clear_all ( 'form_' . $form_state [ 'values' ][ 'form_build_id' ], 'cache_form' );
cache_clear_all ( 'storage_' . $form_state [ 'values' ][ 'form_build_id' ], 'cache_form' );
}
// If batches were set in the submit handlers, we process them now,
// possibly ending execution.
2007-05-04 09:41:37 +00:00
if ( $batch =& batch_get ()) {
2007-05-14 13:43:38 +00:00
// The batch uses its own copies of $form and $form_state for
// late execution of submit handers and post-batch redirection.
$batch [ 'form' ] = $form ;
$batch [ 'form_state' ] = $form_state ;
2007-05-04 09:41:37 +00:00
$batch [ 'progressive' ] = ! $form [ '#programmed' ];
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
// if one hasn't). If the form was called by drupal_execute(),
// however, we'll skip this and let the calling function examine
// the resulting $form_state bundle itself.
if ( ! $form [ '#programmed' ] && empty ( $form_state [ 'rebuild' ]) && empty ( $form_state [ 'storage' ])) {
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' ;
2007-05-14 13:43:38 +00:00
$form [ '#programmed' ] = isset ( $form [ '#post' ]);
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' ])) {
2006-10-31 08:06:18 +00:00
if ( $form [ '#token' ] === FALSE || $user -> uid == 0 || $form [ '#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
}
2007-03-27 05:13:55 +00:00
else if ( isset ( $user -> uid ) && $user -> uid && ! $form [ '#programmed' ]) {
2006-10-31 08:06:18 +00:00
$form [ '#token' ] = $form_id ;
$form [ 'form_token' ] = array (
2006-12-12 09:39:50 +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
2006-03-26 17:41:06 +00:00
$form += _element_info ( 'form' );
2005-10-07 06:11:12 +00:00
2005-11-22 21:31:15 +00:00
if ( ! isset ( $form [ '#validate' ])) {
if ( function_exists ( $form_id . '_validate' )) {
2007-05-14 13:43:38 +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' ])) {
if ( function_exists ( $form_id . '_submit' )) {
2007-01-23 19:17:55 +00:00
// We set submit here so that it can be altered.
2007-05-14 13:43:38 +00:00
$form [ '#submit' ] = array ( $form_id . '_submit' );
2005-11-22 21:31:15 +00:00
}
}
2007-05-14 13:43:38 +00:00
drupal_alter ( 'form_' . $form_id , $form , $form_state );
2007-05-28 06:08:47 +00:00
drupal_alter ( 'form' , $form , $form_state , $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 ) {
2006-04-07 13:00:37 +00:00
static $validated_forms = array ();
if ( isset ( $validated_forms [ $form_id ])) {
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
/**
* Renders a structured form array into themed HTML .
*
* @ 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 .
* @ return
* A string containing the path of the page to display when processing
* is complete .
*/
2006-08-18 18:58:47 +00:00
function drupal_render_form ( $form_id , & $form ) {
2006-07-22 19:26:58 +00:00
// Don't override #theme if someone already set it.
if ( ! isset ( $form [ '#theme' ])) {
2007-04-06 13:27:23 +00:00
init_theme ();
$registry = theme_get_registry ();
if ( isset ( $registry [ $form_id ])) {
2006-07-22 19:26:58 +00:00
$form [ '#theme' ] = $form_id ;
}
}
2006-08-10 15:42:33 +00:00
$output = drupal_render ( $form );
2006-07-22 19:26:58 +00:00
return $output ;
}
/**
* 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-01-31 15:49:26 +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 );
}
2006-07-22 19:26:58 +00:00
}
2007-01-31 15:49:26 +00:00
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 ) {
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
}
}
2005-10-07 06:11:12 +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' ])) {
2006-03-05 02:46:55 +00:00
// An empty textfield returns '' so we use empty(). An empty checkbox
// and a textfield could return '0' and empty('0') returns TRUE so we
// need a special check for the '0' string.
if ( $elements [ '#required' ] && empty ( $elements [ '#value' ]) && $elements [ '#value' ] !== '0' ) {
2006-08-18 12:17:00 +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' ]) {
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' ]))));
}
2007-01-23 19:17:55 +00:00
// Add legal choice check if element has #options. Can be skipped, but
// then you must validate your own element.
2006-03-05 02:46:55 +00:00
if ( isset ( $elements [ '#options' ]) && isset ( $elements [ '#value' ]) && ! isset ( $elements [ '#DANGEROUS_SKIP_CHECK' ])) {
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 ])) {
form_error ( $elements , t ( 'An illegal choice has been detected. Please contact the site administrator.' ));
2007-05-14 13:43:38 +00:00
watchdog ( 'form' , t ( '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' ]])) {
form_error ( $elements , t ( 'An illegal choice has been detected. Please contact the site administrator.' ));
2007-05-14 13:43:38 +00:00
watchdog ( 'form' , t ( '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
}
}
2007-05-14 13:43:38 +00:00
// Call user-defined form level validators.
if ( isset ( $form_id )) {
form_execute_handlers ( 'validate' , $elements , $form_state );
}
// Call any element-specific validators. These must act on the element
// #value data.
elseif ( isset ( $elements [ '#element_validate' ])) {
foreach ( $elements [ '#element_validate' ] as $function ) {
2005-12-03 09:44:50 +00:00
if ( function_exists ( $function )) {
2007-05-14 13:43:38 +00:00
$function ( $elements , $form_state );
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 ;
if ( isset ( $form_state [ $type . '_handlers' ])) {
$handlers = $form_state [ $type . '_handlers' ];
}
elseif ( isset ( $form [ '#' . $type ])) {
$handlers = $form [ '#' . $type ];
}
else {
$handlers = array ();
}
foreach ( $handlers as $function ) {
if ( function_exists ( $function )) {
if ( $type == 'submit' && ( $batch =& batch_get ())) {
// 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
/**
* File an error against a form element . If the name of the element is
* edit [ foo ][ bar ] then you may pass either foo or foo ][ bar as $name
* foo will set an error for all its children .
*/
2006-01-24 10:15:03 +00:00
function form_set_error ( $name = NULL , $message = '' ) {
2005-10-13 10:02:31 +00:00
static $form = array ();
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 ;
}
/**
* 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-11 19:44:35 +00:00
$element [ '#error' ] = TRUE ;
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
* data to the proper elements .
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 ) {
2006-06-23 08:31:23 +00:00
// Initialize as unprocessed.
$form [ '#processed' ] = FALSE ;
2005-10-07 06:11:12 +00:00
/* Use element defaults */
2005-10-11 19:44:35 +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 ;
}
2006-01-24 08:20:45 +00:00
if ( isset ( $form [ '#input' ]) && $form [ '#input' ]) {
2007-05-14 13:43:38 +00:00
_form_builder_handle_input_element ( $form_id , $form , $form_state );
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 ) {
2006-08-18 18:58:47 +00:00
$form [ $key ][ '#post' ] = $form [ '#post' ];
$form [ $key ][ '#programmed' ] = $form [ '#programmed' ];
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 );
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
// Internet Explorer button-click scenerio.
_form_builder_ie_cleanup ( $form , $form_state );
return $form ;
}
/**
* Populate the #value and #name properties of input elements so they
* can be processed and rendered . Also , execute any #process handlers
* attached to a specific element .
*/
function _form_builder_handle_input_element ( $form_id , & $form , & $form_state ) {
2007-05-28 06:20:28 +00:00
if ( isset ( $form [ '#type' ]) && $form [ '#type' ] == 'form' && ! empty ( $form [ '#programmed' ])) {
2007-05-14 13:43:38 +00:00
$form_state [ 'submitted' ] = TRUE ;
}
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.
$form [ '#name' ] = 'files[' . $form [ '#name' ] . ']' ;
}
elseif ( count ( $form [ '#parents' ])) {
$form [ '#name' ] .= '[' . implode ( '][' , $form [ '#parents' ]) . ']' ;
}
array_unshift ( $form [ '#parents' ], $name );
}
if ( ! isset ( $form [ '#id' ])) {
$form [ '#id' ] = form_clean_id ( 'edit-' . implode ( '-' , $form [ '#parents' ]));
}
if ( ! empty ( $form [ '#disabled' ])) {
$form [ '#attributes' ][ 'disabled' ] = 'disabled' ;
}
if ( ! isset ( $form [ '#value' ]) && ! array_key_exists ( '#value' , $form )) {
if (( $form [ '#programmed' ]) || (( ! isset ( $form [ '#access' ]) || $form [ '#access' ]) && isset ( $form [ '#post' ]) && ( isset ( $form [ '#post' ][ 'form_id' ]) && $form [ '#post' ][ 'form_id' ] == $form_id ))) {
$edit = $form [ '#post' ];
foreach ( $form [ '#parents' ] as $parent ) {
$edit = isset ( $edit [ $parent ]) ? $edit [ $parent ] : NULL ;
}
if ( ! $form [ '#programmed' ] || isset ( $edit )) {
switch ( $form [ '#type' ]) {
case 'checkbox' :
$form [ '#value' ] = ! empty ( $edit ) ? $form [ '#return_value' ] : 0 ;
break ;
case 'select' :
if ( isset ( $form [ '#multiple' ]) && $form [ '#multiple' ]) {
if ( isset ( $edit ) && is_array ( $edit )) {
$form [ '#value' ] = drupal_map_assoc ( $edit );
}
else {
$form [ '#value' ] = array ();
}
}
elseif ( isset ( $edit )) {
$form [ '#value' ] = $edit ;
}
break ;
case 'textfield' :
if ( isset ( $edit )) {
// Equate $edit to the form value to ensure it's marked for
// validation.
$edit = str_replace ( array ( " \r " , " \n " ), '' , $edit );
$form [ '#value' ] = $edit ;
}
break ;
case 'token' :
$form [ '#value' ] = ( string ) $edit ;
break ;
default :
if ( isset ( $edit )) {
$form [ '#value' ] = $edit ;
}
}
// Mark all posted values for validation.
if (( isset ( $form [ '#value' ]) && $form [ '#value' ] === $edit ) || ( isset ( $form [ '#required' ]) && $form [ '#required' ])) {
$form [ '#needs_validation' ] = TRUE ;
}
}
}
if ( ! isset ( $form [ '#value' ])) {
$function = 'form_' . $form [ '#type' ] . '_value' ;
2006-04-20 07:11:37 +00:00
if ( function_exists ( $function )) {
2007-05-14 13:43:38 +00:00
$function ( $form );
}
else {
$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.
if ( ! empty ( $form [ '#post' ]) && isset ( $form [ '#executes_submit_callback' ])) {
// 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 ;
// See if a submit button was clicked. 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. In most cases, though, the following code will properly handle
// finding the clicked button and storing any custom validate and
// submit handlers it has defined.
if ( isset ( $form [ '#post' ][ $form [ '#name' ]]) && $form [ '#post' ][ $form [ '#name' ]] == $form [ '#value' ]) {
$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' ];
if ( isset ( $form [ '#validate' ])) {
$form_state [ 'validate_handlers' ] = $form [ '#validate' ];
}
if ( isset ( $form [ '#submit' ])) {
$form_state [ 'submit_handlers' ] = $form [ '#submit' ];
}
}
}
// 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 ( function_exists ( $process )) {
$args = array_merge ( array ( $form ), array ( isset ( $edit ) ? $edit : NULL ), array ( $form_state ));
$form = call_user_func_array ( $process , $args );
}
}
$form [ '#processed' ] = TRUE ;
}
form_set_value ( $form , $form [ '#value' ], $form_state );
}
/**
* Handle the special Internet Explorer one - button - form hit - enter -
* instead - of - clicking scenerio .
*/
function _form_builder_ie_cleanup ( $form , & $form_state ) {
if ( ! empty ( $form [ '#type' ]) && $form [ '#type' ] == 'form' ) {
// If the 'submitted' flag isn't tripped, but there is only one submit button...
if ( empty ( $form_state [ 'submitted' ]) && ! empty ( $form_state [ 'buttons' ][ 'submit' ]) && empty ( $form_state [ 'buttons' ][ 'button' ])) {
$button = $form_state [ 'buttons' ][ 'submit' ][ 0 ];
$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' ];
}
// After handling the special IE case, we no longer need the buttons collection.
unset ( $form_state [ 'buttons' ]);
}
2005-10-07 06:11:12 +00:00
}
2006-04-15 21:52:44 +00:00
/**
2006-04-17 20:48:26 +00:00
* Use this function to make changes to form values in the form validate
2007-05-14 13:43:38 +00:00
* phase , so they will be available in the submit phase in $form_state .
2006-04-15 21:52:44 +00:00
*
* Specifically , if $form [ '#parents' ] is array ( 'foo' , 'bar' )
* and $value is 'baz' then this function will make
2007-06-04 07:22:23 +00:00
* $form_state [ 'values' ][ 'foo' ][ 'bar' ] to be 'baz' .
2006-04-15 21:52:44 +00:00
*
* @ param $form
* The form item . Keys used : #parents, #value
* @ param $value
* The value for the form item .
*/
2007-05-14 13:43:38 +00:00
function form_set_value ( $form , $value , & $form_state ) {
_form_set_value ( $form_state [ 'values' ], $form , $form [ '#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 .
*/
function _form_set_value ( & $form_values , $form , $parents , $value ) {
$parent = array_shift ( $parents );
if ( empty ( $parents )) {
$form_values [ $parent ] = $value ;
}
else {
if ( ! isset ( $form_values [ $parent ])) {
$form_values [ $parent ] = array ();
}
_form_set_value ( $form_values [ $parent ], $form , $parents , $value );
}
}
2005-10-07 06:11:12 +00:00
/**
* Retrieve the default properties for the defined element type .
*/
2006-07-05 11:45:51 +00:00
function _element_info ( $type , $refresh = NULL ) {
2005-10-07 06:11:12 +00:00
static $cache ;
2005-10-26 01:24:09 +00:00
2005-10-07 06:11:12 +00:00
$basic_defaults = array (
2005-10-11 19:44:35 +00:00
'#description' => NULL ,
'#attributes' => array (),
'#required' => FALSE ,
2005-10-26 01:24:09 +00:00
'#tree' => FALSE ,
2006-01-15 16:55:35 +00:00
'#parents' => array ()
2005-10-07 06:11:12 +00:00
);
2006-01-15 16:55:35 +00:00
if ( ! isset ( $cache ) || $refresh ) {
2005-10-07 06:11:12 +00:00
$cache = array ();
foreach ( module_implements ( 'elements' ) as $module ) {
$elements = module_invoke ( $module , 'elements' );
2005-12-14 20:10:45 +00:00
if ( isset ( $elements ) && is_array ( $elements )) {
2005-11-28 16:37:11 +00:00
$cache = array_merge_recursive ( $cache , $elements );
2005-10-07 06:11:12 +00:00
}
}
if ( sizeof ( $cache )) {
foreach ( $cache as $element_type => $info ) {
2005-11-28 16:37:11 +00:00
$cache [ $element_type ] = array_merge_recursive ( $basic_defaults , $info );
2005-10-07 06:11:12 +00:00
}
}
}
return $cache [ $type ];
}
2006-01-06 07:15:47 +00:00
function form_options_flatten ( $array , $reset = TRUE ) {
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 );
}
else if ( 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 .
*
* 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 = '' ;
2007-04-13 08:56:59 +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' ];
2006-05-02 09:26:33 +00:00
return theme ( 'form_element' , $element , '<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 )) {
2006-01-19 09:22:16 +00:00
$options .= '<optgroup label="' . $key . '">' ;
$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 = '' ;
}
2006-01-19 09:22:16 +00:00
$options .= '<option value="' . $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 ;
}
else if ( is_object ( $choice )) {
if ( isset ( $choice -> option [ $key ])) {
$keys [] = $index ;
}
}
else if ( $index == $key ) {
$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 .
*/
function theme_fieldset ( $element ) {
2005-10-11 19:44:35 +00:00
if ( $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' ;
if ( $element [ '#collapsed' ]) {
$element [ '#attributes' ][ 'class' ] .= ' collapsed' ;
2005-10-07 06:11:12 +00:00
}
}
2007-04-13 08:56:59 +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' ] : '' ) . $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 .
*/
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" ' ;
2007-04-13 08:56:59 +00:00
$output .= 'name="' . $element [ '#name' ] . '" ' ;
2005-10-11 19:44:35 +00:00
$output .= 'value="' . $element [ '#return_value' ] . '" ' ;
$output .= ( $element [ '#value' ] == $element [ '#return_value' ]) ? ' checked="checked" ' : ' ' ;
$output .= drupal_attributes ( $element [ '#attributes' ]) . ' />' ;
if ( ! is_null ( $element [ '#title' ])) {
$output = '<label class="option">' . $output . ' ' . $element [ '#title' ] . '</label>' ;
2005-10-07 06:11:12 +00:00
}
2006-05-02 09:26:33 +00:00
unset ( $element [ '#title' ]);
return theme ( 'form_element' , $element , $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 .
*/
function theme_radios ( $element ) {
2006-12-12 10:01:38 +00:00
$class = 'form-radios' ;
if ( isset ( $element [ '#attributes' ][ 'class' ])) {
$class .= ' ' . $element [ '#attributes' ][ 'class' ];
}
2007-01-31 15:49:26 +00:00
$element [ '#children' ] = '<div class="' . $class . '">' . ( ! empty ( $element [ '#children' ]) ? $element [ '#children' ] : '' ) . '</div>' ;
2005-10-11 19:44:35 +00:00
if ( $element [ '#title' ] || $element [ '#description' ]) {
2006-05-02 09:26:33 +00:00
unset ( $element [ '#id' ]);
return theme ( 'form_element' , $element , $element [ '#children' ]);
2005-10-07 06:11:12 +00:00
}
else {
2005-10-11 19:44:35 +00:00
return $element [ '#children' ];
2005-10-07 06:11:12 +00:00
}
}
2006-01-02 08:35:59 +00:00
/**
* Format a password_confirm item .
*
* @ param $element
* An associative array containing the properties of the element .
* Properties used : title , value , id , required , error .
* @ return
* A themed HTML string representing the form item .
*/
function theme_password_confirm ( $element ) {
2006-08-03 14:08:30 +00:00
return theme ( 'form_element' , $element , $element [ '#children' ]);
2006-01-02 08:35:59 +00:00
}
2006-04-20 16:35:29 +00:00
/*
* Expand a password_confirm field into two text boxes .
*/
function expand_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' ],
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' ],
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' ]);
2006-01-02 08:35:59 +00:00
if ( $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
}
2006-08-29 09:12:03 +00:00
elseif ( $form [ '#required' ] && ! empty ( $form [ '#post' ])) {
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 .
2005-10-07 06:11:12 +00:00
*/
function theme_date ( $element ) {
2006-05-02 09:26:33 +00:00
return theme ( 'form_element' , $element , '<div class="container-inline">' . $element [ '#children' ] . '</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
*/
function expand_date ( $element ) {
// Default to current date
2007-05-07 07:29:23 +00:00
if ( empty ( $element [ '#value' ])) {
2005-10-11 19:44:35 +00:00
$element [ '#value' ] = array ( 'day' => format_date ( time (), 'custom' , 'j' ),
2005-10-07 06:11:12 +00:00
'month' => format_date ( time (), 'custom' , 'n' ),
'year' => format_date ( time (), 'custom' , 'Y' ));
}
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 ;
}
2005-12-14 13:22:19 +00:00
$parents = $element [ '#parents' ];
$parents [] = $type ;
$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 ) {
return format_date ( gmmktime ( 0 , 0 , 0 , $month , 2 , 1970 ), 'custom' , 'M' , 0 );
}
2005-10-07 06:11:12 +00:00
2005-12-26 11:14:14 +00:00
/**
2007-01-23 19:17:55 +00:00
* Helper function to load value from default value for checkboxes .
2005-12-26 11:14:14 +00:00
*/
2007-05-14 13:43:38 +00:00
function form_checkboxes_value ( & $form ) {
2005-12-26 11:14:14 +00:00
$value = array ();
2007-01-31 15:49:26 +00:00
$form += array ( '#default_value' => array ());
foreach ( $form [ '#default_value' ] as $key ) {
2005-12-26 11:14:14 +00:00
$value [ $key ] = 1 ;
}
$form [ '#value' ] = $value ;
}
2007-01-31 15:49:26 +00:00
function password_confirm_value ( & $form ) {
$form += array ( '#default_value' => array ());
$form [ '#value' ] = $form [ '#default_value' ] + array ( 'pass1' => '' , 'pass2' => '' );
}
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 ;
}
}
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
*/
function expand_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-03-27 05:13:55 +00:00
$element [ $key ] = array (
'#type' => 'radio' ,
'#title' => $choice ,
'#return_value' => $key ,
'#default_value' => isset ( $element [ '#default_value' ]) ? $element [ '#default_value' ] : NULL ,
'#attributes' => $element [ '#attributes' ],
'#parents' => $element [ '#parents' ],
);
2005-10-07 06:11:12 +00:00
}
}
}
return $element ;
}
/**
* Format a form item .
*
* @ param $element
* An associative array containing the properties of the element .
2006-01-18 19:04:12 +00:00
* Properties used : title , value , description , required , error
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the form item .
*/
function theme_item ( $element ) {
2007-01-31 15:49:26 +00:00
return theme ( 'form_element' , $element , $element [ '#value' ] . ( ! empty ( $element [ '#children' ]) ? $element [ '#children' ] : '' ));
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 .
*/
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" ' ;
2005-10-11 19:44:35 +00:00
$checkbox .= 'name="' . $element [ '#name' ] . '" ' ;
2007-04-13 08:56:59 +00:00
$checkbox .= 'id="' . $element [ '#id' ] . '" ' ;
2005-10-11 19:44:35 +00:00
$checkbox .= 'value="' . $element [ '#return_value' ] . '" ' ;
2006-04-13 19:50:13 +00:00
$checkbox .= $element [ '#value' ] ? ' checked="checked" ' : ' ' ;
2007-04-13 08:56:59 +00:00
$checkbox .= drupal_attributes ( $element [ '#attributes' ]) . ' />' ;
2005-10-11 19:44:35 +00:00
if ( ! is_null ( $element [ '#title' ])) {
$checkbox = '<label class="option">' . $checkbox . ' ' . $element [ '#title' ] . '</label>' ;
2005-10-07 06:11:12 +00:00
}
2006-05-02 09:26:33 +00:00
unset ( $element [ '#title' ]);
return theme ( 'form_element' , $element , $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 .
*/
function theme_checkboxes ( $element ) {
2006-12-12 10:01:38 +00:00
$class = 'form-checkboxes' ;
if ( isset ( $element [ '#attributes' ][ 'class' ])) {
$class .= ' ' . $element [ '#attributes' ][ 'class' ];
}
2007-01-31 15:49:26 +00:00
$element [ '#children' ] = '<div class="' . $class . '">' . ( ! empty ( $element [ '#children' ]) ? $element [ '#children' ] : '' ) . '</div>' ;
2005-10-11 19:44:35 +00:00
if ( $element [ '#title' ] || $element [ '#description' ]) {
2006-05-02 09:26:33 +00:00
unset ( $element [ '#id' ]);
return theme ( 'form_element' , $element , $element [ '#children' ]);
2005-10-07 06:11:12 +00:00
}
else {
2005-10-11 19:44:35 +00:00
return $element [ '#children' ];
2005-10-07 06:11:12 +00:00
}
}
function expand_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 ])) {
2006-01-08 16:04:58 +00:00
$element [ $key ] = array ( '#type' => 'checkbox' , '#processed' => TRUE , '#title' => $choice , '#return_value' => $key , '#default_value' => isset ( $value [ $key ]), '#attributes' => $element [ '#attributes' ]);
2005-10-07 06:11:12 +00:00
}
}
}
return $element ;
}
function theme_submit ( $element ) {
return theme ( 'button' , $element );
}
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' ])) {
$element [ '#attributes' ][ 'class' ] = 'form-' . $element [ '#button_type' ] . ' ' . $element [ '#attributes' ][ 'class' ];
}
else {
$element [ '#attributes' ][ 'class' ] = 'form-' . $element [ '#button_type' ];
}
2006-03-29 23:29:41 +00:00
2007-04-13 08:56:59 +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
}
/**
* Format a hidden form field .
*
* @ param $element
* An associative array containing the properties of the element .
2006-01-18 19:04:12 +00:00
* Properties used : value , edit
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the hidden form field .
*/
function theme_hidden ( $element ) {
2007-04-13 08:56:59 +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
}
2006-10-31 08:06:18 +00:00
function theme_token ( $element ) {
return theme ( 'hidden' , $element );
}
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 .
*/
function theme_textfield ( $element ) {
2007-04-13 08:56:59 +00:00
$size = $element [ '#size' ] ? ' size="' . $element [ '#size' ] . '"' : '' ;
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 = '' ;
2005-10-11 19:44:35 +00:00
if ( $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' ;
2007-02-15 11:40:19 +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' ])) {
$output .= '<span class="field-prefix">' . $element [ '#field_prefix' ] . '</span> ' ;
}
$output .= '<input type="text" maxlength="' . $element [ '#maxlength' ] . '" name="' . $element [ '#name' ] . '" id="' . $element [ '#id' ] . '" ' . $size . ' value="' . check_plain ( $element [ '#value' ]) . '"' . drupal_attributes ( $element [ '#attributes' ]) . ' />' ;
if ( isset ( $element [ '#field_suffix' ])) {
$output .= ' <span class="field-suffix">' . $element [ '#field_suffix' ] . '</span>' ;
}
2007-04-13 08:56:59 +00:00
return theme ( 'form_element' , $element , $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 .
*/
function theme_form ( $element ) {
2006-04-11 11:33:15 +00:00
// Anonymous div to satisfy XHTML compliance.
2007-04-13 08:56:59 +00:00
$action = $element [ '#action' ] ? 'action="' . check_url ( $element [ '#action' ]) . '" ' : '' ;
return '<form ' . $action . ' 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 .
*/
function theme_textarea ( $element ) {
2006-04-07 13:22:12 +00:00
$class = array ( 'form-textarea' );
2007-04-09 13:58:03 +00:00
// Add teaser behaviour (must come before resizable)
if ( ! empty ( $element [ '#teaser' ])) {
drupal_add_js ( 'misc/teaser.js' );
// Note: arrays are merged in drupal_get_js().
drupal_add_js ( array ( 'teaserButton' => array ( t ( 'Join summary' ), t ( 'Split summary at cursor' ))), 'setting' );
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' ;
}
// Add resizable behaviour
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
}
2005-10-11 19:44:35 +00:00
$cols = $element [ '#cols' ] ? ' cols="' . $element [ '#cols' ] . '"' : '' ;
2006-04-05 18:12:48 +00:00
_form_set_class ( $element , $class );
2006-05-02 09:26:33 +00:00
return theme ( 'form_element' , $element , '<textarea' . $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 .
*
* This is used in more advanced forms , such as theme selection and filter format .
*
* @ param $element
* An associative array containing the properties of the element .
2006-05-02 09:26:33 +00:00
* Properties used : value , children .
2005-10-07 06:11:12 +00:00
* @ return
* A themed HTML string representing the HTML markup .
*/
function theme_markup ( $element ) {
2006-11-16 09:06:59 +00:00
return ( isset ( $element [ '#value' ]) ? $element [ '#value' ] : '' ) . ( isset ( $element [ '#children' ]) ? $element [ '#children' ] : '' );
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 .
*/
2005-10-07 06:11:12 +00:00
function theme_password ( $element ) {
2005-10-11 19:44:35 +00:00
$size = $element [ '#size' ] ? ' size="' . $element [ '#size' ] . '" ' : '' ;
2006-10-18 11:43:27 +00:00
$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' ));
2006-10-18 11:43:27 +00:00
$output = '<input type="password" name="' . $element [ '#name' ] . '" id="' . $element [ '#id' ] . '" ' . $maxlength . $size . drupal_attributes ( $element [ '#attributes' ]) . ' />' ;
2006-05-02 09:26:33 +00:00
return theme ( 'form_element' , $element , $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
*/
2006-05-04 09:57:14 +00:00
function 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 ;
2007-01-31 15:49:26 +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 .
*
* 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' ));
2006-12-12 09:39:50 +00:00
return theme ( 'form_element' , $element , '<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 .
* Properties used : title , description , id , required
* @ param $value
2007-01-23 19:17:55 +00:00
* The form element ' s data .
2006-05-02 09:26:33 +00:00
* @ return
2007-01-23 19:17:55 +00:00
* A string representing the form element .
2006-05-02 09:26:33 +00:00
*/
function theme_form_element ( $element , $value ) {
2007-03-25 19:57:56 +00:00
$output = '<div class="form-item"' ;
if ( ! empty ( $element [ '#id' ])) {
$output .= ' id="' . $element [ '#id' ] . '-wrapper"' ;
}
$output .= " > \n " ;
2006-05-02 09:26:33 +00:00
$required = ! empty ( $element [ '#required' ]) ? '<span class="form-required" title="' . t ( 'This field is required.' ) . '">*</span>' : '' ;
if ( ! empty ( $element [ '#title' ])) {
$title = $element [ '#title' ];
if ( ! empty ( $element [ '#id' ])) {
2006-12-12 09:39:50 +00:00
$output .= ' <label for="' . $element [ '#id' ] . '">' . t ( '!title: !required' , array ( '!title' => $title , '!required' => $required )) . " </label> \n " ;
2006-05-02 09:26:33 +00:00
}
else {
2006-08-18 12:17:00 +00:00
$output .= ' <label>' . t ( '!title: !required' , array ( '!title' => $title , '!required' => $required )) . " </label> \n " ;
2006-05-02 09:26:33 +00:00
}
}
$output .= " $value\n " ;
if ( ! empty ( $element [ '#description' ])) {
$output .= ' <div class="description">' . $element [ '#description' ] . " </div> \n " ;
}
$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
}
/**
2006-01-18 19:04:12 +00:00
* Remove invalid characters from an HTML ID attribute string .
2005-10-07 06:11:12 +00:00
*
* @ param $id
2007-01-23 19:17:55 +00:00
* The ID to clean .
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
*/
function form_clean_id ( $id = NULL ) {
2006-12-12 09:39:50 +00:00
$id = str_replace ( array ( '][' , '_' , ' ' ), '-' , $id );
2005-10-07 06:11:12 +00:00
return $id ;
}
/**
* @ } End of " defgroup form " .
*/
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
*
* 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
* // the batch processing, for the curent operation to append its own.
* // '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 ));
* $context [ 'results' ][] = $node -> nid . ' : ' . $node -> title ;
* $context [ 'message' ] = $node -> title ;
* }
*
* // More advanced example: mutli-step operation - load all nodes, five by five
* function my_function_2 ( & $context ) {
* if ( empty ( $context [ 'sandbox' ])) {
* $context [ 'sandbox' ][ 'progress' ] = 0 ;
* $context [ 'sandbox' ][ 'current_node' ] = 0 ;
* $context [ 'sandbox' ][ 'max' ] = db_result ( db_query ( 'SELECT COUNT(DISTINCT nid) FROM {node}' ));
* }
* $limit = 5 ;
* $result = db_query_range ( " SELECT nid FROM { node} WHERE nid > %d ORDER BY nid ASC " , $context [ 'sandbox' ][ 'current_node' ], 0 , $limit );
* while ( $row = db_fetch_array ( $result )) {
* $node = node_load ( $row [ 'nid' ], NULL , TRUE );
* $context [ 'results' ][] = $node -> nid . ' : ' . $node -> title ;
* $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 ) {
* $message = format_plural ( count ( $results ), 'One node processed.' , '@count nodes processed.' );
* }
* else {
* $message = t ( 'Finished with an error.' );
* }
* drupal_set_message ( $message );
* // Provinding data for the redirected page is done through $_SESSION.
* foreach ( $results as $result ) {
* $items [] = t ( 'Loaded node %title.' , array ( '%title' => $result ));
* }
* $_SESSION [ 'my_batch_results' ] = $items ;
* }
* @ 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 .
* 'title' : title for the progress page .
* 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 .
* Available placeholders are @ current , @ remaining , @ total and @ percent .
* Defaults to t ( 'Remaining @remaining of @total.' ) .
* 'error_message' : message displayed if an error occurred while processing the batch .
* Defaults to t ( 'An error has occurred.' ) .
* '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 .
*
* Operations are added as new batch sets . Batch sets are used to ensure
* clean code independency , ensuring that several batches submitted by
* different parts of the code ( core / contrib modules ) can be processed
* correctly while not interfering or having to cope with each other . Each
* batch set gets to specify his own UI messages , operates on it ' s own set
* of operations and results , and triggers it 's own ' finished ' callback .
* 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 (
'id' => db_next_id ( '{batch}_bid' ),
'sets' => array (),
);
}
$init = array (
'sandbox' => array (),
'results' => array (),
'success' => FALSE ,
);
// Use get_t() to allow batches at install time.
$t = get_t ();
$defaults = array (
'title' => $t ( 'Processing' ),
'init_message' => $t ( 'Initializing.' ),
'progress_message' => $t ( 'Remaining @remaining of @total.' ),
'error_message' => $t ( 'An error has occurred.' ),
);
$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-05-04 09:41:37 +00:00
* Unless the batch has been markes with 'progressive' = FALSE , the function
* isses a drupal_goto and thus ends page execution .
*
2007-05-14 13:43:38 +00:00
* This function is not needed in form submit handlers ; Form API takes care
2007-05-04 09:41:37 +00:00
* of batches issued during form submission .
*
* @ param $redirect
* ( optional ) Path to redirect to when the batch has finished processing .
* @ param $url
* ( optional - should ony be used for separate scripts like update . php )
* URL of the batch processing page .
*/
function batch_process ( $redirect = NULL , $url = NULL ) {
$batch =& batch_get ();
if ( isset ( $batch )) {
// Add process information
$t = get_t ();
$url = isset ( $url ) ? $url : 'batch' ;
$process_info = array (
'current_set' => 0 ,
'progressive' => TRUE ,
'url' => isset ( $url ) ? $url : 'batch' ,
'source_page' => $_GET [ 'q' ],
'redirect' => $redirect ,
2007-05-07 10:15:57 +00:00
'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' => 'error' ))))),
2007-05-04 09:41:37 +00:00
);
$batch += $process_info ;
if ( $batch [ 'progressive' ]) {
// Save and unset the destination if any. drupal_goto looks for redirection
// in $_REQUEST['destination'] and $_REQUEST['edit']['destination'].
if ( isset ( $_REQUEST [ 'destination' ])) {
$batch [ 'destination' ] = $_REQUEST [ 'destination' ];
unset ( $_REQUEST [ 'destination' ]);
}
elseif ( isset ( $_REQUEST [ 'edit' ][ 'destination' ])) {
$batch [ 'destination' ] = $_REQUEST [ 'edit' ][ 'destination' ];
unset ( $_REQUEST [ 'edit' ][ 'destination' ]);
}
2007-05-16 07:56:19 +00:00
db_query ( " INSERT INTO { batch} (bid, token, timestamp, batch) VALUES (%d, %d, %d, '%s') " , $batch [ 'id' ], drupal_get_token ( $batch [ 'id' ]), time (), serialize ( $batch ));
2007-05-04 09:41:37 +00:00
drupal_goto ( $batch [ 'url' ], 'op=start&id=' . $batch [ 'id' ]);
}
else {
// Non-progressive execution: bypass the whole progressbar workflow
// and execute the batch in one pass.
require_once './includes/batch.inc' ;
_batch_process ();
}
}
}
/**
* Retrive the current batch .
*/
function & batch_get () {
static $batch = array ();
return $batch ;
}
/**
* @ } End of " defgroup batch " .
*/