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
*/
/**
2006-08-18 18:58:47 +00:00
* Retrieves a form from a builder function , passes it on for
* processing , and renders the form or redirects to its destination
2006-12-05 05:47:37 +00:00
* as appropriate . In multi - step form scenarios , it handles properly
2006-08-25 08:15:24 +00:00
* processing the values using the previous step ' s form definition ,
* then rendering the requested step for display .
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
* different $form_id values to the proper form building function . Examples
* may be found in node_forms (), search_forms (), and user_forms () .
* @ param ...
* Any additional arguments needed by the form building function .
* @ return
* The rendered form .
*/
function drupal_get_form ( $form_id ) {
2006-12-05 05:47:37 +00:00
// In multi-step form scenarios, the incoming $_POST values are not
2006-08-25 08:15:24 +00:00
// necessarily intended for the current form. We need to build
// a copy of the previously built form for validation and processing,
// then go on to the one that was requested if everything works.
$form_build_id = md5 ( mt_rand ());
2006-12-06 15:49:45 +00:00
if ( isset ( $_POST [ 'form_build_id' ]) && isset ( $_SESSION [ 'form' ][ $_POST [ 'form_build_id' ]][ 'args' ]) && $_POST [ 'form_id' ] == $form_id ) {
2006-08-25 08:15:24 +00:00
// There's a previously stored multi-step form. We should handle
// IT first.
$stored = TRUE ;
2006-12-06 15:49:45 +00:00
$args = $_SESSION [ 'form' ][ $_POST [ 'form_build_id' ]][ 'args' ];
2006-08-25 08:15:24 +00:00
$form = call_user_func_array ( 'drupal_retrieve_form' , $args );
2007-01-29 19:10:22 +00:00
$form [ '#build_id' ] = $_POST [ 'form_build_id' ];
2006-08-25 08:15:24 +00:00
}
else {
// We're coming in fresh; build things as they would be. If the
// form's #multistep flag is set, store the build parameters so
// the same form can be reconstituted for validation.
$args = func_get_args ();
$form = call_user_func_array ( 'drupal_retrieve_form' , $args );
if ( isset ( $form [ '#multistep' ]) && $form [ '#multistep' ]) {
2006-12-06 15:49:45 +00:00
// Clean up old multistep form session data.
_drupal_clean_form_sessions ();
$_SESSION [ 'form' ][ $form_build_id ] = array ( 'timestamp' => time (), 'args' => $args );
2006-08-25 08:15:24 +00:00
$form [ '#build_id' ] = $form_build_id ;
}
$stored = FALSE ;
}
// Process the form, submit it, and store any errors if necessary.
drupal_process_form ( $args [ 0 ], $form );
if ( $stored && ! form_get_errors ()) {
// If it's a stored form and there were no errors, we processed the
// stored form successfully. Now we need to build the form that was
// actually requested. We always pass in the current $_POST values
// to the builder function, as values from one stage of a multistep
// form can determine how subsequent steps are displayed.
$args = func_get_args ();
2006-08-29 09:12:03 +00:00
$args [] = $_POST ;
2006-08-25 08:15:24 +00:00
$form = call_user_func_array ( 'drupal_retrieve_form' , $args );
unset ( $_SESSION [ 'form' ][ $_POST [ 'form_build_id' ]]);
if ( isset ( $form [ '#multistep' ]) && $form [ '#multistep' ]) {
2006-12-12 04:52:38 +00:00
$_SESSION [ 'form' ][ $form_build_id ] = array ( 'timestamp' => time (), 'args' => $args );
2006-08-25 08:15:24 +00:00
$form [ '#build_id' ] = $form_build_id ;
}
2006-10-02 11:49:50 +00:00
drupal_prepare_form ( $args [ 0 ], $form );
2006-08-25 08:15:24 +00:00
}
return drupal_render_form ( $args [ 0 ], $form );
2006-08-18 18:58:47 +00:00
}
2006-08-25 08:15:24 +00:00
2006-12-06 15:49:45 +00:00
/**
* Remove form information that ' s at least a day old from the
* $_SESSION [ 'form' ] array .
*/
function _drupal_clean_form_sessions () {
if ( isset ( $_SESSION [ 'form' ])) {
foreach ( $_SESSION [ 'form' ] as $build_id => $data ) {
if ( $data [ 'timestamp' ] < ( time () - 84600 )) {
unset ( $_SESSION [ 'form' ][ $build_id ]);
}
}
}
}
2006-08-31 14:59:28 +00:00
/**
* Retrieves a form using a form_id , populates it with $form_values ,
* 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
* different $form_id values to the proper form building function . Examples
* may be found in node_forms (), search_forms (), and user_forms () .
* @ param $form_values
* An array of values mirroring the values returned by a given form
* when it is submitted by a user .
* @ param ...
* Any additional arguments needed by the form building function .
* @ return
* Any form validation errors encountered .
2006-11-26 22:35:20 +00:00
*
* For example :
*
* // register a new user
* $values [ 'name' ] = 'robo-user' ;
* $values [ 'mail' ] = 'robouser@example.com' ;
* $values [ 'pass' ] = 'password' ;
* drupal_execute ( 'user_register' , $values );
*
* // Create a new node
* $node = array ( 'type' => 'story' );
* $values [ 'title' ] = 'My node' ;
* $values [ 'body' ] = 'This is the body text!' ;
* $values [ 'name' ] = 'robo-user' ;
* drupal_execute ( 'story_node_form' , $values , $node );
2006-08-31 14:59:28 +00:00
*/
function drupal_execute ( $form_id , $form_values ) {
$args = func_get_args ();
$form_id = array_shift ( $args );
$form_values = array_shift ( $args );
array_unshift ( $args , $form_id );
if ( isset ( $form_values )) {
$form = call_user_func_array ( 'drupal_retrieve_form' , $args );
$form [ '#post' ] = $form_values ;
return drupal_process_form ( $form_id , $form );
}
}
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
* different $form_id values to the proper form building function .
* @ param ...
* Any additional arguments needed by the form building function .
*/
function drupal_retrieve_form ( $form_id ) {
static $forms ;
2006-10-12 20:36:51 +00:00
// We save two copies of the incoming arguments: one for modules to use
// when mapping form ids to builder functions, and another to pass to
// the builder function itself. We shift out the first argument -- the
// $form_id itself -- from the list to pass into the builder function,
// 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 );
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 )) {
2006-10-12 20:36:51 +00:00
// In cases where many form_ids need to share a central builder function,
// such as the node editing form, modules can implement hook_forms(). It
// maps one or more form_ids to the correct builder functions.
//
// 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 ])) {
$forms = module_invoke_all ( 'forms' , $saved_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' ];
}
}
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 .
2006-08-18 18:58:47 +00:00
* @ return
* The path to redirect the user to upon completion .
2005-10-07 06:11:12 +00:00
*/
2006-08-18 18:58:47 +00:00
function drupal_process_form ( $form_id , & $form ) {
2006-04-26 18:12:24 +00:00
global $form_values , $form_submitted , $user , $form_button_counter ;
2006-07-02 20:24:17 +00:00
static $saved_globals = array ();
2006-12-05 05:47:37 +00:00
// In some scenarios, this function can be called recursively. Pushing any pre-existing
2006-08-18 18:58:47 +00:00
// $form_values and form submission data lets us start fresh without clobbering work done
// in earlier recursive calls.
2006-07-02 20:24:17 +00:00
array_push ( $saved_globals , array ( $form_values , $form_submitted , $form_button_counter ));
2005-10-07 06:11:12 +00:00
$form_values = array ();
2005-11-22 21:31:15 +00:00
$form_submitted = FALSE ;
2006-04-26 18:12:24 +00:00
$form_button_counter = array ( 0 , 0 );
2005-10-26 01:24:09 +00:00
2006-08-18 18:58:47 +00:00
drupal_prepare_form ( $form_id , $form );
2007-03-17 18:30:14 +00:00
if (( $form [ '#programmed' ]) || ( ! empty ( $_POST ) && (( $_POST [ 'form_id' ] == $form_id )))) {
2006-08-18 18:58:47 +00:00
drupal_validate_form ( $form_id , $form );
2006-07-22 19:26:58 +00:00
// IE does not send a button value when there is only one submit button (and no non-submit buttons)
// and you submit by pressing enter.
// In that case we accept a submission without button values.
2006-08-18 18:58:47 +00:00
if ((( $form [ '#programmed' ]) || $form_submitted || ( ! $form_button_counter [ 0 ] && $form_button_counter [ 1 ])) && ! form_get_errors ()) {
$redirect = drupal_submit_form ( $form_id , $form );
2007-05-04 09:41:37 +00:00
if ( $batch =& batch_get ()) {
$batch [ 'progressive' ] = ! $form [ '#programmed' ];
batch_process ();
// Progressive batch processing redirects to the progress page.
// Execution continues only if programmed form.
}
2006-08-22 19:12:05 +00:00
if ( ! $form [ '#programmed' ]) {
drupal_redirect_form ( $form , $redirect );
}
2006-07-22 19:26:58 +00:00
}
}
2006-08-18 18:58:47 +00:00
// We've finished calling functions that alter the global values, so we can
// restore the ones that were there before this function was called.
2006-07-22 19:26:58 +00:00
list ( $form_values , $form_submitted , $form_button_counter ) = array_pop ( $saved_globals );
2007-01-31 15:49:26 +00:00
if ( isset ( $redirect )) {
return $redirect ;
}
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 .
*/
2006-08-18 18:58:47 +00:00
function drupal_prepare_form ( $form_id , & $form ) {
2006-10-31 08:06:18 +00:00
global $user ;
2005-10-11 19:44:35 +00:00
$form [ '#type' ] = 'form' ;
2007-03-07 12:59:22 +00:00
if ( ! isset ( $form [ '#skip_duplicate_check' ])) {
$form [ '#skip_duplicate_check' ] = FALSE ;
}
2006-08-18 18:58:47 +00:00
if ( ! isset ( $form [ '#post' ])) {
$form [ '#post' ] = $_POST ;
$form [ '#programmed' ] = FALSE ;
}
else {
$form [ '#programmed' ] = TRUE ;
}
2006-12-05 05:47:37 +00:00
// In multi-step form scenarios, this id is used to identify
2006-08-25 08:15:24 +00:00
// a unique instance of a particular form for retrieval.
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-07-22 19:26:58 +00:00
2006-02-27 14:46:49 +00:00
if ( isset ( $form_id )) {
2006-12-12 09:39:50 +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' )) {
2005-12-03 09:44:50 +00:00
$form [ '#validate' ] = array ( $form_id . '_validate' => array ());
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.
2006-03-27 18:07:32 +00:00
$form [ '#submit' ] = array ( $form_id . '_submit' => array ());
2005-11-22 21:31:15 +00:00
}
}
2007-03-26 00:35:59 +00:00
drupal_alter ( 'form' , $form , $form_id );
2005-10-07 06:11:12 +00:00
2006-04-06 15:30:19 +00:00
$form = form_builder ( $form_id , $form );
2005-10-07 06:11:12 +00:00
}
2006-07-22 19:26:58 +00:00
/**
* Validates user - submitted form data from a global variable using
* 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 .
*
*/
2006-08-18 18:58:47 +00:00
function drupal_validate_form ( $form_id , $form ) {
2005-10-07 06:11:12 +00:00
global $form_values ;
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' ])) {
2006-10-31 08:06:18 +00:00
if ( ! drupal_valid_token ( $form_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-02-27 12:45:13 +00:00
if ( ! $form [ '#programmed' ] && ! $form [ '#skip_duplicate_check' ] && isset ( $_SESSION [ 'last_submitted' ][ 'hash' ]) && $_SESSION [ 'last_submitted' ][ 'hash' ] == md5 ( serialize ( $form [ 'form_id' ][ '#post' ]))) {
// This is a repeat submission.
drupal_redirect_form ( NULL , $_SESSION [ 'last_submitted' ][ 'destination' ]);
}
2005-12-03 09:44:50 +00:00
_form_validate ( $form , $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
/**
* Processes user - submitted form data from a global variable using
* the submit 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 .
* @ 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_submit_form ( $form_id , $form ) {
2006-03-27 18:07:32 +00:00
global $form_values ;
$default_args = array ( $form_id , & $form_values );
2007-02-27 12:45:13 +00:00
$submitted = FALSE ;
$goto = NULL ;
2005-10-07 06:11:12 +00:00
2005-12-02 15:21:01 +00:00
if ( isset ( $form [ '#submit' ])) {
2005-12-03 09:44:50 +00:00
foreach ( $form [ '#submit' ] as $function => $args ) {
if ( function_exists ( $function )) {
2006-03-27 18:07:32 +00:00
$args = array_merge ( $default_args , ( array ) $args );
2007-01-23 19:17:55 +00:00
// Since we can only redirect to one page, only the last redirect
// will work.
2007-05-04 09:41:37 +00:00
if ( $batch =& batch_get ()) {
// Some previous _submit callback 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.
$batch [ 'sets' ][] = array ( 'form submit' => array ( $function , $args ));
}
else {
$redirect = call_user_func_array ( $function , $args );
if ( $batch =& batch_get ()) {
// The _submit callback has opened a batch: store the needed form info.
$batch [ 'form_redirect' ] = isset ( $form [ '#redirect' ]) ? $form [ '#redirect' ] : NULL ;
}
}
2007-02-27 12:45:13 +00:00
$submitted = TRUE ;
2006-02-23 10:30:03 +00:00
if ( isset ( $redirect )) {
$goto = $redirect ;
}
2005-11-22 21:31:15 +00:00
}
}
2005-10-07 06:11:12 +00:00
}
2007-02-27 12:45:13 +00:00
// Successful submit. Hash this form's POST and store the hash in the
// session. We'll use this hash later whenever this user submits another
// form to make sure no identical forms get submitted twice.
if ( $submitted && ! $form [ '#skip_duplicate_check' ]) {
$_SESSION [ 'last_submitted' ] = array ( 'destination' => $goto , 'hash' => md5 ( serialize ( $form [ 'form_id' ][ '#post' ])));
}
2007-01-31 15:49:26 +00:00
if ( isset ( $goto )) {
return $goto ;
}
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.
2006-08-18 18:58:47 +00:00
2006-07-22 19:26:58 +00:00
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 ;
}
}
if ( isset ( $form [ '#pre_render' ])) {
foreach ( $form [ '#pre_render' ] as $function ) {
if ( function_exists ( $function )) {
$function ( $form_id , $form );
}
}
}
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
* An optional string containing the destination path to redirect
* to if none is specified by the form .
*
*/
function drupal_redirect_form ( $form , $redirect = NULL ) {
if ( isset ( $redirect )) {
$goto = $redirect ;
}
if ( isset ( $form [ '#redirect' ])) {
$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 .
* @ param $form_id
* A unique string identifying the form for validation , submission ,
* theming , and hook_form_alter functions .
*/
2005-12-03 09:44:50 +00:00
function _form_validate ( $elements , $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 ]) {
_form_validate ( $elements [ $key ]);
}
}
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-04-24 13:53:15 +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' ]])) {
form_error ( $elements , t ( 'An illegal choice has been detected. Please contact the site administrator.' ));
2007-04-24 13:53:15 +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
}
}
2007-01-23 19:17:55 +00:00
// Call user-defined validators.
2005-11-22 21:31:15 +00:00
if ( isset ( $elements [ '#validate' ])) {
2005-12-03 09:44:50 +00:00
foreach ( $elements [ '#validate' ] as $function => $args ) {
$args = array_merge ( array ( $elements ), $args );
2007-01-23 19:17:55 +00:00
// For the full form we hand over a copy of $form_values.
2005-12-03 09:44:50 +00:00
if ( isset ( $form_id )) {
$args = array_merge ( array ( $form_id , $GLOBALS [ 'form_values' ]), $args );
2005-10-07 06:11:12 +00:00
}
2005-12-03 09:44:50 +00:00
if ( function_exists ( $function )) {
call_user_func_array ( $function , $args );
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
}
}
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
}
/**
2006-02-17 10:51:57 +00:00
* Adds some required properties to each form element , which are used
2007-01-23 19:17:55 +00:00
* internally in the form API . This function also automatically assigns
2006-02-17 10:51:57 +00:00
* the value property from the $edit array , provided the element doesn ' t
* already have an assigned value .
*
* @ 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 .
2005-10-07 06:11:12 +00:00
*/
2006-04-06 15:30:19 +00:00
function form_builder ( $form_id , $form ) {
2006-04-26 18:12:24 +00:00
global $form_values , $form_submitted , $form_button_counter ;
2006-04-06 15:30:19 +00:00
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' ]) {
2005-11-18 13:48:09 +00:00
if ( ! isset ( $form [ '#name' ])) {
2006-08-29 09:12:03 +00:00
$name = array_shift ( $form [ '#parents' ]);
$form [ '#name' ] = $name ;
2006-08-31 07:30:49 +00:00
if ( $form [ '#type' ] == 'file' ) {
2007-01-23 19:17:55 +00:00
// To make it easier to handle $_FILES in file.inc, we place all
2006-08-31 07:30:49 +00:00
// file fields in the 'files' array. Also, we do not support
2007-01-23 19:17:55 +00:00
// nested file names.
2006-08-31 07:30:49 +00:00
$form [ '#name' ] = 'files[' . $form [ '#name' ] . ']' ;
}
elseif ( count ( $form [ '#parents' ])) {
2006-08-29 09:12:03 +00:00
$form [ '#name' ] .= '[' . implode ( '][' , $form [ '#parents' ]) . ']' ;
}
array_unshift ( $form [ '#parents' ], $name );
2005-11-18 13:48:09 +00:00
}
if ( ! isset ( $form [ '#id' ])) {
2006-12-12 09:39:50 +00:00
$form [ '#id' ] = form_clean_id ( 'edit-' . implode ( '-' , $form [ '#parents' ]));
2005-11-18 13:48:09 +00:00
}
2005-10-07 06:11:12 +00:00
2006-08-27 12:54:01 +00:00
if ( isset ( $form [ '#disabled' ]) && $form [ '#disabled' ]) {
$form [ '#attributes' ][ 'disabled' ] = 'disabled' ;
}
2006-03-25 18:02:52 +00:00
if ( ! isset ( $form [ '#value' ]) && ! array_key_exists ( '#value' , $form )) {
2006-11-16 09:06:59 +00:00
if (( $form [ '#programmed' ]) || (( ! isset ( $form [ '#access' ]) || $form [ '#access' ]) && isset ( $form [ '#post' ]) && ( isset ( $form [ '#post' ][ 'form_id' ]) && $form [ '#post' ][ 'form_id' ] == $form_id ))) {
2006-08-29 09:12:03 +00:00
$edit = $form [ '#post' ];
2006-08-23 05:04:05 +00:00
foreach ( $form [ '#parents' ] as $parent ) {
$edit = isset ( $edit [ $parent ]) ? $edit [ $parent ] : NULL ;
}
2006-08-25 07:58:48 +00:00
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 ();
}
2006-05-16 01:55:57 +00:00
}
2006-08-25 07:58:48 +00:00
elseif ( isset ( $edit )) {
$form [ '#value' ] = $edit ;
2006-05-16 01:55:57 +00:00
}
2006-08-25 07:58:48 +00:00
break ;
case 'textfield' :
if ( isset ( $edit )) {
2007-01-23 19:17:55 +00:00
// Equate $edit to the form value to ensure it's marked for
// validation.
2006-08-25 07:58:48 +00:00
$edit = str_replace ( array ( " \r " , " \n " ), '' , $edit );
$form [ '#value' ] = $edit ;
}
break ;
2006-10-31 08:06:18 +00:00
case 'token' :
$form [ '#value' ] = ( string ) $edit ;
break ;
2006-08-25 07:58:48 +00:00
default :
if ( isset ( $edit )) {
$form [ '#value' ] = $edit ;
}
}
2007-01-23 19:17:55 +00:00
// Mark all posted values for validation.
2006-08-25 07:58:48 +00:00
if (( isset ( $form [ '#value' ]) && $form [ '#value' ] === $edit ) || ( isset ( $form [ '#required' ]) && $form [ '#required' ])) {
$form [ '#needs_validation' ] = TRUE ;
}
2005-11-03 15:42:31 +00:00
}
}
if ( ! isset ( $form [ '#value' ])) {
2007-04-13 08:56:59 +00:00
$function = $form [ '#type' ] . '_value' ;
2005-12-26 11:14:14 +00:00
if ( function_exists ( $function )) {
$function ( $form );
}
else {
2006-11-16 09:06:59 +00:00
$form [ '#value' ] = isset ( $form [ '#default_value' ]) ? $form [ '#default_value' ] : '' ;
2005-12-26 11:14:14 +00:00
}
2005-11-03 15:42:31 +00:00
}
2005-10-11 19:44:35 +00:00
}
2006-04-26 18:12:24 +00:00
if ( isset ( $form [ '#executes_submit_callback' ])) {
2007-01-23 19:17:55 +00:00
// Count submit and non-submit buttons.
2006-04-26 18:12:24 +00:00
$form_button_counter [ $form [ '#executes_submit_callback' ]] ++ ;
2007-01-23 19:17:55 +00:00
// See if a submit button was pressed.
2006-08-29 09:12:03 +00:00
if ( isset ( $form [ '#post' ][ $form [ '#name' ]]) && $form [ '#post' ][ $form [ '#name' ]] == $form [ '#value' ]) {
2006-04-25 20:46:57 +00:00
$form_submitted = $form_submitted || $form [ '#executes_submit_callback' ];
2006-08-29 09:12:03 +00:00
2007-01-23 19:17:55 +00:00
// 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'.
2006-08-29 09:12:03 +00:00
$form_values [ $form [ '#name' ]] = $form [ '#value' ];
2005-10-07 06:11:12 +00:00
}
}
}
2007-01-23 19:17:55 +00:00
// Allow for elements to expand to multiple elements, e.g., radios,
// checkboxes and files.
2005-11-28 16:37:11 +00:00
if ( isset ( $form [ '#process' ]) && ! $form [ '#processed' ]) {
2005-12-03 09:44:50 +00:00
foreach ( $form [ '#process' ] as $process => $args ) {
if ( function_exists ( $process )) {
2007-01-31 15:49:26 +00:00
$args = array_merge ( array ( $form ), array ( isset ( $edit ) ? $edit : NULL ), $args );
2005-12-05 08:41:29 +00:00
$form = call_user_func_array ( $process , $args );
2005-11-28 16:37:11 +00:00
}
}
2005-10-11 19:44:35 +00:00
$form [ '#processed' ] = TRUE ;
2005-10-07 06:11:12 +00:00
}
2005-12-19 14:43:42 +00:00
// Set the $form_values key that gets passed to validate and submit.
// We call this after #process gets called so that #process has a
// chance to update #value if desired.
2006-01-24 08:20:45 +00:00
if ( isset ( $form [ '#input' ]) && $form [ '#input' ]) {
2006-04-15 21:52:44 +00:00
form_set_value ( $form , $form [ '#value' ]);
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' ]);
}
2006-04-06 15:30:19 +00:00
$form [ $key ] = form_builder ( $form_id , $form [ $key ]);
2005-10-07 06:11:12 +00:00
$count ++ ;
}
2006-04-20 07:11:37 +00:00
if ( isset ( $form [ '#after_build' ]) && ! isset ( $form [ '#after_build_done' ])) {
foreach ( $form [ '#after_build' ] as $function ) {
if ( function_exists ( $function )) {
$form = $function ( $form , $form_values );
}
}
2005-11-21 18:10:26 +00:00
$form [ '#after_build_done' ] = TRUE ;
2005-10-26 01:24:09 +00:00
}
2006-04-06 15:30:19 +00:00
return $form ;
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
2006-04-15 21:52:44 +00:00
* phase , so they will be available in the submit phase in $form_values .
*
* Specifically , if $form [ '#parents' ] is array ( 'foo' , 'bar' )
* and $value is 'baz' then this function will make
2006-04-17 20:48:26 +00:00
* $form_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 .
*/
function form_set_value ( $form , $value ) {
global $form_values ;
_form_set_value ( $form_values , $form , $form [ '#parents' ], $value );
}
/**
* Helper function for form_set_value () .
*
2007-01-23 19:17:55 +00:00
* We iterate over $parents and create nested arrays for them
* in $form_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 );
}
return $form ;
}
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' ),
'#value' => $element [ '#value' ][ 'pass1' ],
);
$element [ 'pass2' ] = array (
'#type' => 'password' ,
'#title' => t ( 'Confirm password' ),
'#value' => $element [ '#value' ][ 'pass2' ],
);
2006-04-20 16:35:29 +00:00
$element [ '#validate' ] = array ( 'password_confirm_validate' => array ());
$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
*/
2006-01-24 10:15:03 +00:00
function password_confirm_validate ( $form ) {
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.
form_set_value ( $form [ 'pass1' ], NULL );
form_set_value ( $form [ 'pass2' ], NULL );
form_set_value ( $form , $pass1 );
2006-01-02 08:35:59 +00:00
return $form ;
}
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
*/
function checkboxes_value ( & $form ) {
$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 );
* // only needed if not inside a form _submit callback :
* 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.
* // 1 means the operation is finished and processing can continue
* // to the next operation. This value always be 1 if not specified
* // by the batch operation (a single-step operation), so that will
* // be considered as finished.
* $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' ]);
// If the batch is being processed (meaning we are executing a stored submit callback),
// 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 .
*
* This function is not needed in form submit callbacks ; Form API takes care
* 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 ) {
global $form_values , $user ;
$batch =& batch_get ();
// batch_process should not be called inside form _submit callbacks, or while a
// batch is already running. Neutralize the call if it is the case.
if ( isset ( $batch [ 'current_set' ]) || ( isset ( $form_values ) && ! isset ( $batch [ 'progressive' ]))) {
return ;
}
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 ,
'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' ))))),
);
$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' ]);
}
db_query ( " INSERT INTO { batch} (bid, sid, timestamp, batch) VALUES (%d, %d, %d, '%s') " , $batch [ 'id' ], $user -> sid , time (), serialize ( $batch ));
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 " .
*/