[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/includes/ -> form.inc (source)

   1  <?php
   2  // $Id: form.inc,v 1.265.2.35 2010/11/04 09:55:29 goba Exp $
   3  
   4  /**
   5   * @defgroup forms Form builder functions
   6   * @{
   7   * Functions that build an abstract representation of a HTML form.
   8   *
   9   * All modules should declare their form builder functions to be in this
  10   * group and each builder function should reference its validate and submit
  11   * functions using \@see. Conversely, validate and submit functions should
  12   * reference the form builder function using \@see. For examples, of this see
  13   * system_modules_uninstall() or user_pass(), the latter of which has the
  14   * following in its doxygen documentation:
  15   *
  16   * \@ingroup forms
  17   * \@see user_pass_validate().
  18   * \@see user_pass_submit().
  19   *
  20   * @} End of "defgroup forms".
  21   */
  22  
  23  /**
  24   * @defgroup form_api Form generation
  25   * @{
  26   * Functions to enable the processing and display of HTML forms.
  27   *
  28   * Drupal uses these functions to achieve consistency in its form processing and
  29   * presentation, while simplifying code and reducing the amount of HTML that
  30   * must be explicitly generated by modules.
  31   *
  32   * The drupal_get_form() function handles retrieving, processing, and
  33   * displaying a rendered HTML form for modules automatically. For example:
  34   *
  35   * @code
  36   * // Display the user registration form.
  37   * $output = drupal_get_form('user_register');
  38   * @endcode
  39   *
  40   * Forms can also be built and submitted programmatically without any user input
  41   * using the drupal_execute() function.
  42   *
  43   * For information on the format of the structured arrays used to define forms,
  44   * and more detailed explanations of the Form API workflow, see the
  45   * @link http://api.drupal.org/api/file/developer/topics/forms_api_reference.html reference @endlink
  46   * and the @link http://api.drupal.org/api/file/developer/topics/forms_api.html quickstart guide. @endlink
  47   */
  48  
  49  /**
  50   * Retrieves a form from a constructor function, or from the cache if
  51   * the form was built in a previous page-load. The form is then passed
  52   * on for processing, after and rendered for display if necessary.
  53   *
  54   * @param $form_id
  55   *   The unique string identifying the desired form. If a function
  56   *   with that name exists, it is called to build the form array.
  57   *   Modules that need to generate the same form (or very similar forms)
  58   *   using different $form_ids can implement hook_forms(), which maps
  59   *   different $form_id values to the proper form constructor function. Examples
  60   *   may be found in node_forms(), search_forms(), and user_forms().
  61   * @param ...
  62   *   Any additional arguments are passed on to the functions called by
  63   *   drupal_get_form(), including the unique form constructor function.
  64   *   For example, the node_edit form requires that a node object be passed
  65   *   in here when it is called. These are available to implementations of
  66   *   hook_form_alter() and hook_form_FORM_ID_alter() as the array
  67   *   $form['#parameters'].
  68   * @return
  69   *   The rendered form.
  70   */
  71  function drupal_get_form($form_id) {
  72    $form_state = array('storage' => NULL, 'submitted' => FALSE);
  73  
  74    $args = func_get_args();
  75    $cacheable = FALSE;
  76  
  77    if (isset($_SESSION['batch_form_state'])) {
  78      // We've been redirected here after a batch processing : the form has
  79      // already been processed, so we grab the post-process $form_state value
  80      // and move on to form display. See _batch_finished() function.
  81      $form_state = $_SESSION['batch_form_state'];
  82      unset($_SESSION['batch_form_state']);
  83    }
  84    else {
  85      // If the incoming $_POST contains a form_build_id, we'll check the
  86      // cache for a copy of the form in question. If it's there, we don't
  87      // have to rebuild the form to proceed. In addition, if there is stored
  88      // form_state data from a previous step, we'll retrieve it so it can
  89      // be passed on to the form processing code.
  90      if (isset($_POST['form_id']) && $_POST['form_id'] == $form_id && !empty($_POST['form_build_id'])) {
  91        $form = form_get_cache($_POST['form_build_id'], $form_state);
  92      }
  93  
  94      // If the previous bit of code didn't result in a populated $form
  95      // object, we're hitting the form for the first time and we need
  96      // to build it from scratch.
  97      if (!isset($form)) {
  98        $form_state['post'] = $_POST;
  99        // Use a copy of the function's arguments for manipulation
 100        $args_temp = $args;
 101        $args_temp[0] = &$form_state;
 102        array_unshift($args_temp, $form_id);
 103  
 104        $form = call_user_func_array('drupal_retrieve_form', $args_temp);
 105        $form_build_id = 'form-'. md5(uniqid(mt_rand(), TRUE));
 106        $form['#build_id'] = $form_build_id;
 107        drupal_prepare_form($form_id, $form, $form_state);
 108        // Store a copy of the unprocessed form for caching and indicate that it
 109        // is cacheable if #cache will be set.
 110        $original_form = $form;
 111        $cacheable = TRUE;
 112        unset($form_state['post']);
 113      }
 114      $form['#post'] = $_POST;
 115  
 116      // Now that we know we have a form, we'll process it (validating,
 117      // submitting, and handling the results returned by its submission
 118      // handlers. Submit handlers accumulate data in the form_state by
 119      // altering the $form_state variable, which is passed into them by
 120      // reference.
 121      drupal_process_form($form_id, $form, $form_state);
 122      if ($cacheable && !empty($form['#cache'])) {
 123        // Caching is done past drupal_process_form so #process callbacks can
 124        // set #cache.
 125        form_set_cache($form_build_id, $original_form, $form_state);
 126      }
 127    }
 128  
 129    // Most simple, single-step forms will be finished by this point --
 130    // drupal_process_form() usually redirects to another page (or to
 131    // a 'fresh' copy of the form) once processing is complete. If one
 132    // of the form's handlers has set $form_state['redirect'] to FALSE,
 133    // the form will simply be re-rendered with the values still in its
 134    // fields.
 135    //
 136    // If $form_state['storage'] or $form_state['rebuild'] has been set
 137    // and input has been processed, we know that we're in a complex
 138    // multi-part process of some sort and the form's workflow is NOT
 139    // complete. We need to construct a fresh copy of the form, passing
 140    // in the latest $form_state in addition to any other variables passed
 141    // into drupal_get_form().
 142  
 143    if ((!empty($form_state['storage']) || !empty($form_state['rebuild'])) && !empty($form_state['process_input']) && !form_get_errors()) {
 144      $form = drupal_rebuild_form($form_id, $form_state, $args);
 145    }
 146  
 147    // If we haven't redirected to a new location by now, we want to
 148    // render whatever form array is currently in hand.
 149    return drupal_render_form($form_id, $form);
 150  }
 151  
 152  /**
 153   * Retrieves a form, caches it and processes it with an empty $_POST.
 154   *
 155   * This function clears $_POST and passes the empty $_POST to the form_builder.
 156   * To preserve some parts from $_POST, pass them in $form_state.
 157   *
 158   * If your AHAH callback simulates the pressing of a button, then your AHAH
 159   * callback will need to do the same as what drupal_get_form would do when the
 160   * button is pressed: get the form from the cache, run drupal_process_form over
 161   * it and then if it needs rebuild, run drupal_rebuild_form over it. Then send
 162   * back a part of the returned form.
 163   * $form_state['clicked_button']['#array_parents'] will help you to find which
 164   * part.
 165   *
 166   * @param $form_id
 167   *   The unique string identifying the desired form. If a function
 168   *   with that name exists, it is called to build the form array.
 169   *   Modules that need to generate the same form (or very similar forms)
 170   *   using different $form_ids can implement hook_forms(), which maps
 171   *   different $form_id values to the proper form constructor function. Examples
 172   *   may be found in node_forms(), search_forms(), and user_forms().
 173   * @param $form_state
 174   *   A keyed array containing the current state of the form. Most
 175   *   important is the $form_state['storage'] collection.
 176   * @param $args
 177   *   Any additional arguments are passed on to the functions called by
 178   *   drupal_get_form(), plus the original form_state in the beginning. If you
 179   *   are getting a form from the cache, use $form['#parameters'] to shift off
 180   *   the $form_id from its beginning then the resulting array can be used as
 181   *   $arg here.
 182   * @param $form_build_id
 183   *   If the AHAH callback calling this function only alters part of the form,
 184   *   then pass in the existing form_build_id so we can re-cache with the same
 185   *   csid.
 186   * @return
 187   *   The newly built form.
 188   */
 189  function drupal_rebuild_form($form_id, &$form_state, $args, $form_build_id = NULL) {
 190    // Remove the first argument. This is $form_id.when called from
 191    // drupal_get_form and the original $form_state when called from some AHAH
 192    // callback. Neither is needed. After that, put in the current state.
 193    $args[0] = &$form_state;
 194    // And the form_id.
 195    array_unshift($args, $form_id);
 196    $form = call_user_func_array('drupal_retrieve_form', $args);
 197  
 198    if (!isset($form_build_id)) {
 199      // We need a new build_id for the new version of the form.
 200      $form_build_id = 'form-'. md5(uniqid(mt_rand(), TRUE));
 201    }
 202    $form['#build_id'] = $form_build_id;
 203    drupal_prepare_form($form_id, $form, $form_state);
 204  
 205    // Now, we cache the form structure so it can be retrieved later for
 206    // validation. If $form_state['storage'] is populated, we'll also cache
 207    // it so that it can be used to resume complex multi-step processes.
 208    form_set_cache($form_build_id, $form, $form_state);
 209  
 210    // Clear out all post data, as we don't want the previous step's
 211    // data to pollute this one and trigger validate/submit handling,
 212    // then process the form for rendering.
 213    $_POST = array();
 214    $form['#post'] = array();
 215    drupal_process_form($form_id, $form, $form_state);
 216    return $form;
 217  }
 218  
 219  /**
 220   * Store a form in the cache.
 221   */
 222  function form_set_cache($form_build_id, $form, $form_state) {
 223    global $user;
 224    // 6 hours cache life time for forms should be plenty.
 225    $expire = 21600;
 226  
 227    if ($user->uid) {
 228      $form['#cache_token'] = drupal_get_token();
 229    }
 230    cache_set('form_'. $form_build_id, $form, 'cache_form', time() + $expire);
 231    if (!empty($form_state['storage'])) {
 232      cache_set('storage_'. $form_build_id, $form_state['storage'], 'cache_form', time() + $expire);
 233    }
 234  }
 235  
 236  /**
 237   * Fetch a form from cache.
 238   */
 239  function form_get_cache($form_build_id, &$form_state) {
 240    global $user;
 241    if ($cached = cache_get('form_'. $form_build_id, 'cache_form')) {
 242      $form = $cached->data;
 243      if ((isset($form['#cache_token']) && drupal_valid_token($form['#cache_token'])) || (!isset($form['#cache_token']) && !$user->uid)) {
 244        if ($cached = cache_get('storage_'. $form_build_id, 'cache_form')) {
 245          $form_state['storage'] = $cached->data;
 246        }
 247        return $form;
 248      }
 249    }
 250  }
 251  
 252  /**
 253   * Retrieves, populates, and processes a form.
 254   *
 255   * This function allows you to supply values for form elements and submit a
 256   * form for processing. Compare to drupal_get_form(), which also builds and
 257   * processes a form, but does not allow you to supply values.
 258   *
 259   * There is no return value, but you can check to see if there are errors by
 260   * calling form_get_errors().
 261   *
 262   * @param $form_id
 263   *   The unique string identifying the desired form. If a function
 264   *   with that name exists, it is called to build the form array.
 265   *   Modules that need to generate the same form (or very similar forms)
 266   *   using different $form_ids can implement hook_forms(), which maps
 267   *   different $form_id values to the proper form constructor function. Examples
 268   *   may be found in node_forms(), search_forms(), and user_forms().
 269   * @param $form_state
 270   *   A keyed array containing the current state of the form. Most
 271   *   important is the $form_state['values'] collection, a tree of data
 272   *   used to simulate the incoming $_POST information from a user's
 273   *   form submission.
 274   * @param ...
 275   *   Any additional arguments are passed on to the functions called by
 276   *   drupal_execute(), including the unique form constructor function.
 277   *   For example, the node_edit form requires that a node object be passed
 278   *   in here when it is called.
 279   * For example:
 280   * @code
 281   * // register a new user
 282   * $form_state = array();
 283   * $form_state['values']['name'] = 'robo-user';
 284   * $form_state['values']['mail'] = 'robouser@example.com';
 285   * $form_state['values']['pass']['pass1'] = 'password';
 286   * $form_state['values']['pass']['pass2'] = 'password';
 287   * $form_state['values']['op'] = t('Create new account');
 288   * drupal_execute('user_register', $form_state);
 289   *
 290   * // Create a new node
 291   * $form_state = array();
 292   * module_load_include('inc', 'node', 'node.pages');
 293   * $node = array('type' => 'story');
 294   * $form_state['values']['title'] = 'My node';
 295   * $form_state['values']['body'] = 'This is the body text!';
 296   * $form_state['values']['name'] = 'robo-user';
 297   * $form_state['values']['op'] = t('Save');
 298   * drupal_execute('story_node_form', $form_state, (object)$node);
 299   * @endcode
 300   */
 301  function drupal_execute($form_id, &$form_state) {
 302    $args = func_get_args();
 303  
 304    // Make sure $form_state is passed around by reference.
 305    $args[1] = &$form_state;
 306    
 307    $form = call_user_func_array('drupal_retrieve_form', $args);
 308    $form['#post'] = $form_state['values'];
 309    drupal_prepare_form($form_id, $form, $form_state);
 310    drupal_process_form($form_id, $form, $form_state);
 311  }
 312  
 313  /**
 314   * Retrieves the structured array that defines a given form.
 315   *
 316   * @param $form_id
 317   *   The unique string identifying the desired form. If a function
 318   *   with that name exists, it is called to build the form array.
 319   *   Modules that need to generate the same form (or very similar forms)
 320   *   using different $form_ids can implement hook_forms(), which maps
 321   *   different $form_id values to the proper form constructor function.
 322   * @param $form_state
 323   *   A keyed array containing the current state of the form.
 324   * @param ...
 325   *   Any additional arguments needed by the unique form constructor
 326   *   function. Generally, these are any arguments passed into the
 327   *   drupal_get_form() or drupal_execute() functions after the first
 328   *   argument. If a module implements hook_forms(), it can examine
 329   *   these additional arguments and conditionally return different
 330   *   builder functions as well.
 331   */
 332  function drupal_retrieve_form($form_id, &$form_state) {
 333    static $forms;
 334  
 335    // We save two copies of the incoming arguments: one for modules to use
 336    // when mapping form ids to constructor functions, and another to pass to
 337    // the constructor function itself. We shift out the first argument -- the
 338    // $form_id itself -- from the list to pass into the constructor function,
 339    // since it's already known.
 340    $args = func_get_args();
 341    $saved_args = $args;
 342    array_shift($args);
 343    if (isset($form_state)) {
 344      array_shift($args);
 345    }
 346  
 347    // We first check to see if there's a function named after the $form_id.
 348    // If there is, we simply pass the arguments on to it to get the form.
 349    if (!function_exists($form_id)) {
 350      // In cases where many form_ids need to share a central constructor function,
 351      // such as the node editing form, modules can implement hook_forms(). It
 352      // maps one or more form_ids to the correct constructor functions.
 353      //
 354      // We cache the results of that hook to save time, but that only works
 355      // for modules that know all their form_ids in advance. (A module that
 356      // adds a small 'rate this comment' form to each comment in a list
 357      // would need a unique form_id for each one, for example.)
 358      //
 359      // So, we call the hook if $forms isn't yet populated, OR if it doesn't
 360      // yet have an entry for the requested form_id.
 361      if (!isset($forms) || !isset($forms[$form_id])) {
 362        $forms = module_invoke_all('forms', $form_id, $args);
 363      }
 364      $form_definition = $forms[$form_id];
 365      if (isset($form_definition['callback arguments'])) {
 366        $args = array_merge($form_definition['callback arguments'], $args);
 367      }
 368      if (isset($form_definition['callback'])) {
 369        $callback = $form_definition['callback'];
 370      }
 371    }
 372  
 373    array_unshift($args, NULL);
 374    $args[0] = &$form_state;
 375  
 376    // If $callback was returned by a hook_forms() implementation, call it.
 377    // Otherwise, call the function named after the form id.
 378    $form = call_user_func_array(isset($callback) ? $callback : $form_id, $args);
 379  
 380    // We store the original function arguments, rather than the final $arg
 381    // value, so that form_alter functions can see what was originally
 382    // passed to drupal_retrieve_form(). This allows the contents of #parameters
 383    // to be saved and passed in at a later date to recreate the form.
 384    $form['#parameters'] = $saved_args;
 385    return $form;
 386  }
 387  
 388  /**
 389   * This function is the heart of form API. The form gets built, validated and in
 390   * appropriate cases, submitted.
 391   *
 392   * @param $form_id
 393   *   The unique string identifying the current form.
 394   * @param $form
 395   *   An associative array containing the structure of the form.
 396   * @param $form_state
 397   *   A keyed array containing the current state of the form. This
 398   *   includes the current persistent storage data for the form, and
 399   *   any data passed along by earlier steps when displaying a
 400   *   multi-step form. Additional information, like the sanitized $_POST
 401   *   data, is also accumulated here.
 402   */
 403  function drupal_process_form($form_id, &$form, &$form_state) {
 404    $form_state['values'] = array();
 405  
 406    $form = form_builder($form_id, $form, $form_state);
 407    // Only process the form if it is programmed or the form_id coming
 408    // from the POST data is set and matches the current form_id.
 409    if ((!empty($form['#programmed'])) || (!empty($form['#post']) && (isset($form['#post']['form_id']) && ($form['#post']['form_id'] == $form_id)))) {
 410      $form_state['process_input'] = TRUE;
 411      drupal_validate_form($form_id, $form, $form_state);
 412  
 413      // form_clean_id() maintains a cache of element IDs it has seen,
 414      // so it can prevent duplicates. We want to be sure we reset that
 415      // cache when a form is processed, so scenerios that result in
 416      // the form being built behind the scenes and again for the
 417      // browser don't increment all the element IDs needlessly.
 418      form_clean_id(NULL, TRUE);
 419  
 420      if ((!empty($form_state['submitted'])) && !form_get_errors() && empty($form_state['rebuild'])) {
 421        $form_state['redirect'] = NULL;
 422        form_execute_handlers('submit', $form, $form_state);
 423  
 424        // We'll clear out the cached copies of the form and its stored data
 425        // here, as we've finished with them. The in-memory copies are still
 426        // here, though.
 427        if (variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED && !empty($form_state['values']['form_build_id'])) {
 428          cache_clear_all('form_'. $form_state['values']['form_build_id'], 'cache_form');
 429          cache_clear_all('storage_'. $form_state['values']['form_build_id'], 'cache_form');
 430        }
 431  
 432        // If batches were set in the submit handlers, we process them now,
 433        // possibly ending execution. We make sure we do not react to the batch
 434        // that is already being processed (if a batch operation performs a
 435        // drupal_execute).
 436        if ($batch =& batch_get() && !isset($batch['current_set'])) {
 437          // The batch uses its own copies of $form and $form_state for
 438          // late execution of submit handers and post-batch redirection.
 439          $batch['form'] = $form;
 440          $batch['form_state'] = $form_state;
 441          $batch['progressive'] = !$form['#programmed'];
 442          batch_process();
 443          // Execution continues only for programmatic forms.
 444          // For 'regular' forms, we get redirected to the batch processing
 445          // page. Form redirection will be handled in _batch_finished(),
 446          // after the batch is processed.
 447        }
 448  
 449        // If no submit handlers have populated the $form_state['storage']
 450        // bundle, and the $form_state['rebuild'] flag has not been set,
 451        // we're finished and should redirect to a new destination page
 452        // if one has been set (and a fresh, unpopulated copy of the form
 453        // if one hasn't). If the form was called by drupal_execute(),
 454        // however, we'll skip this and let the calling function examine
 455        // the resulting $form_state bundle itself.
 456        if (!$form['#programmed'] && empty($form_state['rebuild']) && empty($form_state['storage'])) {
 457          drupal_redirect_form($form, $form_state['redirect']);
 458        }
 459      }
 460    }
 461  }
 462  
 463  /**
 464   * Prepares a structured form array by adding required elements,
 465   * executing any hook_form_alter functions, and optionally inserting
 466   * a validation token to prevent tampering.
 467   *
 468   * @param $form_id
 469   *   A unique string identifying the form for validation, submission,
 470   *   theming, and hook_form_alter functions.
 471   * @param $form
 472   *   An associative array containing the structure of the form.
 473   * @param $form_state
 474   *   A keyed array containing the current state of the form. Passed
 475   *   in here so that hook_form_alter() calls can use it, as well.
 476   */
 477  function drupal_prepare_form($form_id, &$form, &$form_state) {
 478    global $user;
 479  
 480    $form['#type'] = 'form';
 481    $form['#programmed'] = isset($form['#post']);
 482  
 483    if (isset($form['#build_id'])) {
 484      $form['form_build_id'] = array(
 485        '#type' => 'hidden',
 486        '#value' => $form['#build_id'],
 487        '#id' => $form['#build_id'],
 488        '#name' => 'form_build_id',
 489      );
 490    }
 491  
 492    // Add a token, based on either #token or form_id, to any form displayed to
 493    // authenticated users. This ensures that any submitted form was actually
 494    // requested previously by the user and protects against cross site request
 495    // forgeries.
 496    if (isset($form['#token'])) {
 497      if ($form['#token'] === FALSE || $user->uid == 0 || $form['#programmed']) {
 498        unset($form['#token']);
 499      }
 500      else {
 501        $form['form_token'] = array('#type' => 'token', '#default_value' => drupal_get_token($form['#token']));
 502      }
 503    }
 504    else if (isset($user->uid) && $user->uid && !$form['#programmed']) {
 505      $form['#token'] = $form_id;
 506      $form['form_token'] = array(
 507        '#id' => form_clean_id('edit-'. $form_id .'-form-token'),
 508        '#type' => 'token',
 509        '#default_value' => drupal_get_token($form['#token']),
 510      );
 511    }
 512  
 513    if (isset($form_id)) {
 514      $form['form_id'] = array(
 515        '#type' => 'hidden',
 516        '#value' => $form_id,
 517        '#id' => form_clean_id("edit-$form_id"),
 518      );
 519    }
 520    if (!isset($form['#id'])) {
 521      $form['#id'] = form_clean_id($form_id);
 522    }
 523  
 524    $form += _element_info('form');
 525  
 526    if (!isset($form['#validate'])) {
 527      if (function_exists($form_id .'_validate')) {
 528        $form['#validate'] = array($form_id .'_validate');
 529      }
 530    }
 531  
 532    if (!isset($form['#submit'])) {
 533      if (function_exists($form_id .'_submit')) {
 534        // We set submit here so that it can be altered.
 535        $form['#submit'] = array($form_id .'_submit');
 536      }
 537    }
 538  
 539    // Normally, we would call drupal_alter($form_id, $form, $form_state).
 540    // However, drupal_alter() normally supports just one byref parameter. Using
 541    // the __drupal_alter_by_ref key, we can store any additional parameters
 542    // that need to be altered, and they'll be split out into additional params
 543    // for the hook_form_alter() implementations.
 544    // @todo: Remove this in Drupal 7.
 545    $data = &$form;
 546    $data['__drupal_alter_by_ref'] = array(&$form_state);
 547    drupal_alter('form_'. $form_id, $data);
 548  
 549    // __drupal_alter_by_ref is unset in the drupal_alter() function, we need
 550    // to repopulate it to ensure both calls get the data.
 551    $data['__drupal_alter_by_ref'] = array(&$form_state);
 552    drupal_alter('form', $data, $form_id);
 553  }
 554  
 555  
 556  /**
 557   * Validates user-submitted form data from the $form_state using
 558   * the validate functions defined in a structured form array.
 559   *
 560   * @param $form_id
 561   *   A unique string identifying the form for validation, submission,
 562   *   theming, and hook_form_alter functions.
 563   * @param $form
 564   *   An associative array containing the structure of the form.
 565   * @param $form_state
 566   *   A keyed array containing the current state of the form. The current
 567   *   user-submitted data is stored in $form_state['values'], though
 568   *   form validation functions are passed an explicit copy of the
 569   *   values for the sake of simplicity. Validation handlers can also
 570   *   $form_state to pass information on to submit handlers. For example:
 571   *     $form_state['data_for_submision'] = $data;
 572   *   This technique is useful when validation requires file parsing,
 573   *   web service requests, or other expensive requests that should
 574   *   not be repeated in the submission step.
 575   */
 576  function drupal_validate_form($form_id, $form, &$form_state) {
 577    static $validated_forms = array();
 578  
 579    if (isset($validated_forms[$form_id])) {
 580      return;
 581    }
 582  
 583    // If the session token was set by drupal_prepare_form(), ensure that it
 584    // matches the current user's session.
 585    if (isset($form['#token'])) {
 586      if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) {
 587        // Setting this error will cause the form to fail validation.
 588        form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));
 589      }
 590    }
 591  
 592    _form_validate($form, $form_state, $form_id);
 593    $validated_forms[$form_id] = TRUE;
 594  }
 595  
 596  /**
 597   * Renders a structured form array into themed HTML.
 598   *
 599   * @param $form_id
 600   *   A unique string identifying the form for validation, submission,
 601   *   theming, and hook_form_alter functions.
 602   * @param $form
 603   *   An associative array containing the structure of the form.
 604   * @return
 605   *   A string containing the themed HTML.
 606   */
 607  function drupal_render_form($form_id, &$form) {
 608    // Don't override #theme if someone already set it.
 609    if (!isset($form['#theme'])) {
 610      init_theme();
 611      $registry = theme_get_registry();
 612      if (isset($registry[$form_id])) {
 613        $form['#theme'] = $form_id;
 614      }
 615    }
 616  
 617    $output = drupal_render($form);
 618    return $output;
 619  }
 620  
 621  /**
 622   * Redirect the user to a URL after a form has been processed.
 623   *
 624   * @param $form
 625   *   An associative array containing the structure of the form.
 626   * @param $redirect
 627   *   An optional value containing the destination path to redirect
 628   *   to if none is specified by the form.
 629   */
 630  function drupal_redirect_form($form, $redirect = NULL) {
 631    $goto = NULL;
 632    if (isset($redirect)) {
 633      $goto = $redirect;
 634    }
 635    if ($goto !== FALSE && isset($form['#redirect'])) {
 636      $goto = $form['#redirect'];
 637    }
 638    if (!isset($goto) || ($goto !== FALSE)) {
 639      if (isset($goto)) {
 640        if (is_array($goto)) {
 641          call_user_func_array('drupal_goto', $goto);
 642        }
 643        else {
 644          drupal_goto($goto);
 645        }
 646      }
 647      drupal_goto($_GET['q']);
 648    }
 649  }
 650  
 651  /**
 652   * Performs validation on form elements. First ensures required fields are
 653   * completed, #maxlength is not exceeded, and selected options were in the
 654   * list of options given to the user. Then calls user-defined validators.
 655   *
 656   * @param $elements
 657   *   An associative array containing the structure of the form.
 658   * @param $form_state
 659   *   A keyed array containing the current state of the form. The current
 660   *   user-submitted data is stored in $form_state['values'], though
 661   *   form validation functions are passed an explicit copy of the
 662   *   values for the sake of simplicity. Validation handlers can also
 663   *   $form_state to pass information on to submit handlers. For example:
 664   *     $form_state['data_for_submision'] = $data;
 665   *   This technique is useful when validation requires file parsing,
 666   *   web service requests, or other expensive requests that should
 667   *   not be repeated in the submission step.
 668   * @param $form_id
 669   *   A unique string identifying the form for validation, submission,
 670   *   theming, and hook_form_alter functions.
 671   */
 672  function _form_validate($elements, &$form_state, $form_id = NULL) {
 673    static $complete_form;
 674  
 675    // Also used in the installer, pre-database setup.
 676    $t = get_t();
 677  
 678    // Recurse through all children.
 679    foreach (element_children($elements) as $key) {
 680      if (isset($elements[$key]) && $elements[$key]) {
 681        _form_validate($elements[$key], $form_state);
 682      }
 683    }
 684    // Validate the current input.
 685    if (!isset($elements['#validated']) || !$elements['#validated']) {
 686      if (isset($elements['#needs_validation'])) {
 687        // Make sure a value is passed when the field is required.
 688        // A simple call to empty() will not cut it here as some fields, like
 689        // checkboxes, can return a valid value of '0'. Instead, check the
 690        // length if it's a string, and the item count if it's an array.
 691        if ($elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0))) {
 692          form_error($elements, $t('!name field is required.', array('!name' => $elements['#title'])));
 693        }
 694  
 695        // Verify that the value is not longer than #maxlength.
 696        if (isset($elements['#maxlength']) && drupal_strlen($elements['#value']) > $elements['#maxlength']) {
 697          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']))));
 698        }
 699  
 700        if (isset($elements['#options']) && isset($elements['#value'])) {
 701          if ($elements['#type'] == 'select') {
 702            $options = form_options_flatten($elements['#options']);
 703          }
 704          else {
 705            $options = $elements['#options'];
 706          }
 707          if (is_array($elements['#value'])) {
 708            $value = $elements['#type'] == 'checkboxes' ? array_keys(array_filter($elements['#value'])) : $elements['#value'];
 709            foreach ($value as $v) {
 710              if (!isset($options[$v])) {
 711                form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.'));
 712                watchdog('form', 'Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
 713              }
 714            }
 715          }
 716          elseif (!isset($options[$elements['#value']])) {
 717            form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.'));
 718            watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
 719          }
 720        }
 721      }
 722  
 723      // Call user-defined form level validators and store a copy of the full
 724      // form so that element-specific validators can examine the entire structure
 725      // if necessary.
 726      if (isset($form_id)) {
 727        form_execute_handlers('validate', $elements, $form_state);
 728        $complete_form = $elements;
 729      }
 730      // Call any element-specific validators. These must act on the element
 731      // #value data.
 732      elseif (isset($elements['#element_validate'])) {
 733        foreach ($elements['#element_validate'] as $function) {
 734          if (function_exists($function))  {
 735            $function($elements, $form_state, $complete_form);
 736          }
 737        }
 738      }
 739      $elements['#validated'] = TRUE;
 740    }
 741  }
 742  
 743  /**
 744   * A helper function used to execute custom validation and submission
 745   * handlers for a given form. Button-specific handlers are checked
 746   * first. If none exist, the function falls back to form-level handlers.
 747   *
 748   * @param $type
 749   *   The type of handler to execute. 'validate' or 'submit' are the
 750   *   defaults used by Form API.
 751   * @param $form
 752   *   An associative array containing the structure of the form.
 753   * @param $form_state
 754   *   A keyed array containing the current state of the form. If the user
 755   *   submitted the form by clicking a button with custom handler functions
 756   *   defined, those handlers will be stored here.
 757   */
 758  function form_execute_handlers($type, &$form, &$form_state) {
 759    $return = FALSE;
 760    if (isset($form_state[$type .'_handlers'])) {
 761      $handlers = $form_state[$type .'_handlers'];
 762    }
 763    elseif (isset($form['#'. $type])) {
 764      $handlers = $form['#'. $type];
 765    }
 766    else {
 767      $handlers = array();
 768    }
 769  
 770    foreach ($handlers as $function) {
 771      if (function_exists($function))  {
 772        // Check to see if a previous _submit handler has set a batch, but 
 773        // make sure we do not react to a batch that is already being processed 
 774        // (for instance if a batch operation performs a drupal_execute()).
 775        if ($type == 'submit' && ($batch =& batch_get()) && !isset($batch['current_set'])) {
 776          // Some previous _submit handler has set a batch. We store the call
 777          // in a special 'control' batch set, for execution at the correct
 778          // time during the batch processing workflow.
 779          $batch['sets'][] = array('form_submit' => $function);
 780        }
 781        else {
 782          $function($form, $form_state);
 783        }
 784        $return = TRUE;
 785      }
 786    }
 787    return $return;
 788  }
 789  
 790  /**
 791   * File an error against a form element.
 792   *
 793   * @param $name
 794   *   The name of the form element. If the #parents property of your form
 795   *   element is array('foo', 'bar', 'baz') then you may set an error on 'foo'
 796   *   or 'foo][bar][baz'. Setting an error on 'foo' sets an error for every
 797   *   element where the #parents array starts with 'foo'.
 798   * @param $message
 799   *   The error message to present to the user.
 800   * @param $reset
 801   *   Reset the form errors static cache.
 802   * @return
 803   *   Never use the return value of this function, use form_get_errors and
 804   *   form_get_error instead.
 805   */
 806  function form_set_error($name = NULL, $message = '', $reset = FALSE) {
 807    static $form = array();
 808    if ($reset) {
 809      $form = array();
 810    }
 811    if (isset($name) && !isset($form[$name])) {
 812      $form[$name] = $message;
 813      if ($message) {
 814        drupal_set_message($message, 'error');
 815      }
 816    }
 817    return $form;
 818  }
 819  
 820  /**
 821   * Return an associative array of all errors.
 822   */
 823  function form_get_errors() {
 824    $form = form_set_error();
 825    if (!empty($form)) {
 826      return $form;
 827    }
 828  }
 829  
 830  /**
 831   * Return the error message filed against the form with the specified name.
 832   */
 833  function form_get_error($element) {
 834    $form = form_set_error();
 835    $key = $element['#parents'][0];
 836    if (isset($form[$key])) {
 837      return $form[$key];
 838    }
 839    $key = implode('][', $element['#parents']);
 840    if (isset($form[$key])) {
 841      return $form[$key];
 842    }
 843  }
 844  
 845  /**
 846   * Flag an element as having an error.
 847   */
 848  function form_error(&$element, $message = '') {
 849    form_set_error(implode('][', $element['#parents']), $message);
 850  }
 851  
 852  /**
 853   * Walk through the structured form array, adding any required
 854   * properties to each element and mapping the incoming $_POST
 855   * data to the proper elements.
 856   *
 857   * @param $form_id
 858   *   A unique string identifying the form for validation, submission,
 859   *   theming, and hook_form_alter functions.
 860   * @param $form
 861   *   An associative array containing the structure of the form.
 862   * @param $form_state
 863   *   A keyed array containing the current state of the form. In this
 864   *   context, it is used to accumulate information about which button
 865   *   was clicked when the form was submitted, as well as the sanitized
 866   *   $_POST data.
 867   */
 868  function form_builder($form_id, $form, &$form_state) {
 869    static $complete_form, $cache;
 870  
 871    // Initialize as unprocessed.
 872    $form['#processed'] = FALSE;
 873  
 874    // Use element defaults.
 875    if ((!empty($form['#type'])) && ($info = _element_info($form['#type']))) {
 876      // Overlay $info onto $form, retaining preexisting keys in $form.
 877      $form += $info;
 878    }
 879  
 880    if (isset($form['#type']) && $form['#type'] == 'form') {
 881      $cache = NULL;
 882      $complete_form = $form;
 883      if (!empty($form['#programmed'])) {
 884        $form_state['submitted'] = TRUE;
 885      }
 886    }
 887  
 888    if (isset($form['#input']) && $form['#input']) {
 889      _form_builder_handle_input_element($form_id, $form, $form_state, $complete_form);
 890    }
 891    $form['#defaults_loaded'] = TRUE;
 892  
 893    // We start off assuming all form elements are in the correct order.
 894    $form['#sorted'] = TRUE;
 895  
 896    // Recurse through all child elements.
 897    $count = 0;
 898    foreach (element_children($form) as $key) {
 899      $form[$key]['#post'] = $form['#post'];
 900      $form[$key]['#programmed'] = $form['#programmed'];
 901      // Don't squash an existing tree value.
 902      if (!isset($form[$key]['#tree'])) {
 903        $form[$key]['#tree'] = $form['#tree'];
 904      }
 905  
 906      // Deny access to child elements if parent is denied.
 907      if (isset($form['#access']) && !$form['#access']) {
 908        $form[$key]['#access'] = FALSE;
 909      }
 910  
 911      // Don't squash existing parents value.
 912      if (!isset($form[$key]['#parents'])) {
 913        // Check to see if a tree of child elements is present. If so,
 914        // continue down the tree if required.
 915        $form[$key]['#parents'] = $form[$key]['#tree'] && $form['#tree'] ? array_merge($form['#parents'], array($key)) : array($key);
 916        $array_parents = isset($form['#array_parents']) ? $form['#array_parents'] : array();
 917        $array_parents[] = $key;
 918        $form[$key]['#array_parents'] = $array_parents;
 919      }
 920  
 921      // Assign a decimal placeholder weight to preserve original array order.
 922      if (!isset($form[$key]['#weight'])) {
 923        $form[$key]['#weight'] = $count/1000;
 924      }
 925      else {
 926        // If one of the child elements has a weight then we will need to sort
 927        // later.
 928        unset($form['#sorted']);
 929      }
 930      $form[$key] = form_builder($form_id, $form[$key], $form_state);
 931      $count++;
 932    }
 933  
 934    // The #after_build flag allows any piece of a form to be altered
 935    // after normal input parsing has been completed.
 936    if (isset($form['#after_build']) && !isset($form['#after_build_done'])) {
 937      foreach ($form['#after_build'] as $function) {
 938        $form = $function($form, $form_state);
 939        $form['#after_build_done'] = TRUE;
 940      }
 941    }
 942  
 943    // Now that we've processed everything, we can go back to handle the funky
 944    // Internet Explorer button-click scenario.
 945    _form_builder_ie_cleanup($form, $form_state);
 946  
 947    // We shoud keep the buttons array until the IE clean up function
 948    // has recognized the submit button so the form has been marked
 949    // as submitted. If we already know which button was submitted,
 950    // we don't need the array.
 951    if (!empty($form_state['submitted'])) {
 952      unset($form_state['buttons']);
 953    }
 954  
 955    // If some callback set #cache, we need to flip a static flag so later it
 956    // can be found.
 957    if (!empty($form['#cache'])) {
 958      $cache = $form['#cache'];
 959    }
 960    // We are on the top form, we can copy back #cache if it's set.
 961    if (isset($form['#type']) && $form['#type'] == 'form' && isset($cache)) {
 962      $form['#cache'] = TRUE;
 963    }
 964    return $form;
 965  }
 966  
 967  /**
 968   * Populate the #value and #name properties of input elements so they
 969   * can be processed and rendered. Also, execute any #process handlers
 970   * attached to a specific element.
 971   */
 972  function _form_builder_handle_input_element($form_id, &$form, &$form_state, $complete_form) {
 973    if (!isset($form['#name'])) {
 974      $name = array_shift($form['#parents']);
 975      $form['#name'] = $name;
 976      if ($form['#type'] == 'file') {
 977        // To make it easier to handle $_FILES in file.inc, we place all
 978        // file fields in the 'files' array. Also, we do not support
 979        // nested file names.
 980        $form['#name'] = 'files['. $form['#name'] .']';
 981      }
 982      elseif (count($form['#parents'])) {
 983        $form['#name'] .= '['. implode('][', $form['#parents']) .']';
 984      }
 985      array_unshift($form['#parents'], $name);
 986    }
 987    if (!isset($form['#id'])) {
 988      $form['#id'] = form_clean_id('edit-'. implode('-', $form['#parents']));
 989    }
 990  
 991    if (!empty($form['#disabled'])) {
 992      $form['#attributes']['disabled'] = 'disabled';
 993    }
 994  
 995    if (!isset($form['#value']) && !array_key_exists('#value', $form)) {
 996      $function = !empty($form['#value_callback']) ? $form['#value_callback'] : 'form_type_'. $form['#type'] .'_value';
 997      if (($form['#programmed']) || ((!isset($form['#access']) || $form['#access']) && isset($form['#post']) && (isset($form['#post']['form_id']) && $form['#post']['form_id'] == $form_id))) {
 998        $edit = $form['#post'];
 999        foreach ($form['#parents'] as $parent) {
1000          $edit = isset($edit[$parent]) ? $edit[$parent] : NULL;
1001        }
1002        if (!$form['#programmed'] || isset($edit)) {
1003          // Call #type_value to set the form value;
1004          if (function_exists($function)) {
1005            $form['#value'] = $function($form, $edit);
1006          }
1007          if (!isset($form['#value']) && isset($edit)) {
1008            $form['#value'] = $edit;
1009          }
1010        }
1011        // Mark all posted values for validation.
1012        if (isset($form['#value']) || (isset($form['#required']) && $form['#required'])) {
1013          $form['#needs_validation'] = TRUE;
1014        }
1015      }
1016      // Load defaults.
1017      if (!isset($form['#value'])) {
1018        // Call #type_value without a second argument to request default_value handling.
1019        if (function_exists($function)) {
1020          $form['#value'] = $function($form);
1021        }
1022        // Final catch. If we haven't set a value yet, use the explicit default value.
1023        // Avoid image buttons (which come with garbage value), so we only get value
1024        // for the button actually clicked.
1025        if (!isset($form['#value']) && empty($form['#has_garbage_value'])) {
1026          $form['#value'] = isset($form['#default_value']) ? $form['#default_value'] : '';
1027        }
1028      }
1029    }
1030  
1031    // Determine which button (if any) was clicked to submit the form.
1032    // We compare the incoming values with the buttons defined in the form,
1033    // and flag the one that matches. We have to do some funky tricks to
1034    // deal with Internet Explorer's handling of single-button forms, though.
1035    if (!empty($form['#post']) && isset($form['#executes_submit_callback'])) {
1036      // First, accumulate a collection of buttons, divided into two bins:
1037      // those that execute full submit callbacks and those that only validate.
1038      $button_type = $form['#executes_submit_callback'] ? 'submit' : 'button';
1039      $form_state['buttons'][$button_type][] = $form;
1040  
1041      if (_form_button_was_clicked($form)) {
1042        $form_state['submitted'] = $form_state['submitted'] || $form['#executes_submit_callback'];
1043  
1044        // In most cases, we want to use form_set_value() to manipulate
1045        // the global variables. In this special case, we want to make sure that
1046        // the value of this element is listed in $form_variables under 'op'.
1047        $form_state['values'][$form['#name']] = $form['#value'];
1048        $form_state['clicked_button'] = $form;
1049  
1050        if (isset($form['#validate'])) {
1051          $form_state['validate_handlers'] = $form['#validate'];
1052        }
1053        if (isset($form['#submit'])) {
1054          $form_state['submit_handlers'] = $form['#submit'];
1055        }
1056      }
1057    }
1058    // Allow for elements to expand to multiple elements, e.g., radios,
1059    // checkboxes and files.
1060    if (isset($form['#process']) && !$form['#processed']) {
1061      foreach ($form['#process'] as $process) {
1062        if (function_exists($process)) {
1063          $form = $process($form, isset($edit) ? $edit : NULL, $form_state, $complete_form);
1064        }
1065      }
1066      $form['#processed'] = TRUE;
1067    }
1068    form_set_value($form, $form['#value'], $form_state);
1069  }
1070  
1071  /**
1072   * Helper function to handle the sometimes-convoluted logic of button
1073   * click detection.
1074   *
1075   * In Internet Explorer, if ONLY one submit button is present, AND the
1076   * enter key is used to submit the form, no form value is sent for it
1077   * and we'll never detect a match. That special case is handled by
1078   * _form_builder_ie_cleanup().
1079   */
1080  function _form_button_was_clicked($form) {
1081    // First detect normal 'vanilla' button clicks. Traditionally, all
1082    // standard buttons on a form share the same name (usually 'op'),
1083    // and the specific return value is used to determine which was
1084    // clicked. This ONLY works as long as $form['#name'] puts the
1085    // value at the top level of the tree of $_POST data.
1086    if (isset($form['#post'][$form['#name']]) && $form['#post'][$form['#name']] == $form['#value']) {
1087      return TRUE;
1088    }
1089    // When image buttons are clicked, browsers do NOT pass the form element
1090    // value in $_POST. Instead they pass an integer representing the
1091    // coordinates of the click on the button image. This means that image
1092    // buttons MUST have unique $form['#name'] values, but the details of
1093    // their $_POST data should be ignored.
1094    elseif (!empty($form['#has_garbage_value']) && isset($form['#value']) && $form['#value'] !== '') {
1095      return TRUE;
1096    }
1097    return FALSE;
1098  }
1099  
1100  /**
1101   * In IE, if only one submit button is present, AND the enter key is
1102   * used to submit the form, no form value is sent for it and our normal
1103   * button detection code will never detect a match. We call this
1104   * function after all other button-detection is complete to check
1105   * for the proper conditions, and treat the single button on the form
1106   * as 'clicked' if they are met.
1107   */
1108  function _form_builder_ie_cleanup($form, &$form_state) {
1109    // Quick check to make sure we're always looking at the full form
1110    // and not a sub-element.
1111    if (!empty($form['#type']) && $form['#type'] == 'form') {
1112      // If we haven't recognized a submission yet, and there's a single
1113      // submit button, we know that we've hit the right conditions. Grab
1114      // the first one and treat it as the clicked button.
1115      if (empty($form_state['submitted']) && !empty($form_state['buttons']['submit']) && empty($form_state['buttons']['button'])) {
1116        $button = $form_state['buttons']['submit'][0];
1117  
1118        // Set up all the $form_state information that would have been
1119        // populated had the button been recognized earlier.
1120        $form_state['submitted'] = TRUE;
1121        $form_state['submit_handlers'] = empty($button['#submit']) ? NULL : $button['#submit'];
1122        $form_state['validate_handlers'] = empty($button['#validate']) ? NULL : $button['#validate'];
1123        $form_state['values'][$button['#name']] = $button['#value'];
1124        $form_state['clicked_button'] = $button;
1125      }
1126    }
1127  }
1128  
1129  /**
1130   * Helper function to determine the value for an image button form element.
1131   *
1132   * @param $form
1133   *   The form element whose value is being populated.
1134   * @param $edit
1135   *   The incoming POST data to populate the form element. If this is FALSE,
1136   *   the element's default value should be returned.
1137   * @return
1138   *   The data that will appear in the $form_state['values'] collection
1139   *   for this element. Return nothing to use the default.
1140   */
1141  function form_type_image_button_value($form, $edit = FALSE) {
1142    if ($edit !== FALSE) {
1143      if (!empty($edit)) {
1144        // If we're dealing with Mozilla or Opera, we're lucky. It will
1145        // return a proper value, and we can get on with things.
1146        return $form['#return_value'];
1147      }
1148      else {
1149        // Unfortunately, in IE we never get back a proper value for THIS
1150        // form element. Instead, we get back two split values: one for the
1151        // X and one for the Y coordinates on which the user clicked the
1152        // button. We'll find this element in the #post data, and search
1153        // in the same spot for its name, with '_x'.
1154        $post = $form['#post'];
1155        foreach (split('\[', $form['#name']) as $element_name) {
1156          // chop off the ] that may exist.
1157          if (substr($element_name, -1) == ']') {
1158            $element_name = substr($element_name, 0, -1);
1159          }
1160  
1161          if (!isset($post[$element_name])) {
1162            if (isset($post[$element_name .'_x'])) {
1163              return $form['#return_value'];
1164            }
1165            return NULL;
1166          }
1167          $post = $post[$element_name];
1168        }
1169        return $form['#return_value'];
1170      }
1171    }
1172  }
1173  
1174  /**
1175   * Helper function to determine the value for a checkbox form element.
1176   *
1177   * @param $form
1178   *   The form element whose value is being populated.
1179   * @param $edit
1180   *   The incoming POST data to populate the form element. If this is FALSE,
1181   *   the element's default value should be returned.
1182   * @return
1183   *   The data that will appear in the $form_state['values'] collection
1184   *   for this element. Return nothing to use the default.
1185   */
1186  function form_type_checkbox_value($form, $edit = FALSE) {
1187    if ($edit !== FALSE) {
1188      if (empty($form['#disabled'])) {
1189        return !empty($edit) ? $form['#return_value'] : 0;
1190      }
1191      else {
1192        return $form['#default_value'];
1193      }
1194    }
1195  }
1196  
1197  /**
1198   * Helper function to determine the value for a checkboxes form element.
1199   *
1200   * @param $form
1201   *   The form element whose value is being populated.
1202   * @param $edit
1203   *   The incoming POST data to populate the form element. If this is FALSE,
1204   *   the element's default value should be returned.
1205   * @return
1206   *   The data that will appear in the $form_state['values'] collection
1207   *   for this element. Return nothing to use the default.
1208   */
1209  function form_type_checkboxes_value($form, $edit = FALSE) {
1210    if ($edit === FALSE) {
1211      $value = array();
1212      $form += array('#default_value' => array());
1213      foreach ($form['#default_value'] as $key) {
1214        $value[$key] = 1;
1215      }
1216      return $value;
1217    }
1218    elseif (!isset($edit)) {
1219      return array();
1220    }
1221  }
1222  
1223  /**
1224   * Helper function to determine the value for a password_confirm form
1225   * element.
1226   *
1227   * @param $form
1228   *   The form element whose value is being populated.
1229   * @param $edit
1230   *   The incoming POST data to populate the form element. If this is FALSE,
1231   *   the element's default value should be returned.
1232   * @return
1233   *   The data that will appear in the $form_state['values'] collection
1234   *   for this element. Return nothing to use the default.
1235   */
1236  function form_type_password_confirm_value($form, $edit = FALSE) {
1237    if ($edit === FALSE) {
1238      $form += array('#default_value' => array());
1239      return $form['#default_value'] + array('pass1' => '', 'pass2' => '');
1240    }
1241  }
1242  
1243  /**
1244   * Helper function to determine the value for a select form element.
1245   *
1246   * @param $form
1247   *   The form element whose value is being populated.
1248   * @param $edit
1249   *   The incoming POST data to populate the form element. If this is FALSE,
1250   *   the element's default value should be returned.
1251   * @return
1252   *   The data that will appear in the $form_state['values'] collection
1253   *   for this element. Return nothing to use the default.
1254   */
1255  function form_type_select_value($form, $edit = FALSE) {
1256    if ($edit !== FALSE) {
1257      if (isset($form['#multiple']) && $form['#multiple']) {
1258        return (is_array($edit)) ? drupal_map_assoc($edit) : array();
1259      }
1260      else {
1261        return $edit;
1262      }
1263    }
1264  }
1265  
1266  /**
1267   * Helper function to determine the value for a textfield form element.
1268   *
1269   * @param $form
1270   *   The form element whose value is being populated.
1271   * @param $edit
1272   *   The incoming POST data to populate the form element. If this is FALSE,
1273   *   the element's default value should be returned.
1274   * @return
1275   *   The data that will appear in the $form_state['values'] collection
1276   *   for this element. Return nothing to use the default.
1277   */
1278  function form_type_textfield_value($form, $edit = FALSE) {
1279    if ($edit !== FALSE) {
1280      // Equate $edit to the form value to ensure it's marked for
1281      // validation.
1282      return str_replace(array("\r", "\n"), '', $edit);
1283    }
1284  }
1285  
1286  /**
1287   * Helper function to determine the value for form's token value.
1288   *
1289   * @param $form
1290   *   The form element whose value is being populated.
1291   * @param $edit
1292   *   The incoming POST data to populate the form element. If this is FALSE,
1293   *   the element's default value should be returned.
1294   * @return
1295   *   The data that will appear in the $form_state['values'] collection
1296   *   for this element. Return nothing to use the default.
1297   */
1298  function form_type_token_value($form, $edit = FALSE) {
1299    if ($edit !== FALSE) {
1300      return (string)$edit;
1301    }
1302  }
1303  
1304  /**
1305   * Change submitted form values during the form processing cycle.
1306   *
1307   * Use this function to change the submitted value of a form item in the
1308   * validation phase so that it persists in $form_state through to the
1309   * submission handlers in the submission phase.
1310   *
1311   * Since $form_state['values'] can either be a flat array of values, or a tree
1312   * of nested values, some care must be taken when using this function.
1313   * Specifically, $form_item['#parents'] is an array that describes the branch of
1314   * the tree whose value should be updated. For example, if we wanted to update
1315   * $form_state['values']['one']['two'] to 'new value', we'd pass in
1316   * $form_item['#parents'] = array('one', 'two') and $value = 'new value'.
1317   *
1318   * @param $form_item
1319   *   The form item that should have its value updated. Keys used: #parents,
1320   *   #value. In most cases you can just pass in the right element from the $form
1321   *   array.
1322   * @param $value
1323   *   The new value for the form item.
1324   * @param $form_state
1325   *   The array where the value change should be recorded.
1326   */
1327  function form_set_value($form_item, $value, &$form_state) {
1328    _form_set_value($form_state['values'], $form_item, $form_item['#parents'], $value);
1329  }
1330  
1331  /**
1332   * Helper function for form_set_value().
1333   *
1334   * We iterate over $parents and create nested arrays for them
1335   * in $form_state['values'] if needed. Then we insert the value into
1336   * the right array.
1337   */
1338  function _form_set_value(&$form_values, $form_item, $parents, $value) {
1339    $parent = array_shift($parents);
1340    if (empty($parents)) {
1341      $form_values[$parent] = $value;
1342    }
1343    else {
1344      if (!isset($form_values[$parent])) {
1345        $form_values[$parent] = array();
1346      }
1347      _form_set_value($form_values[$parent], $form_item, $parents, $value);
1348    }
1349  }
1350  
1351  /**
1352   * Retrieve the default properties for the defined element type.
1353   */
1354  function _element_info($type, $refresh = NULL) {
1355    static $cache;
1356  
1357    $basic_defaults = array(
1358      '#description' => NULL,
1359      '#attributes' => array(),
1360      '#required' => FALSE,
1361      '#tree' => FALSE,
1362      '#parents' => array()
1363    );
1364    if (!isset($cache) || $refresh) {
1365      $cache = array();
1366      foreach (module_implements('elements') as $module) {
1367        $elements = module_invoke($module, 'elements');
1368        if (isset($elements) && is_array($elements)) {
1369          $cache = array_merge_recursive($cache, $elements);
1370        }
1371      }
1372      if (sizeof($cache)) {
1373        foreach ($cache as $element_type => $info) {
1374          $cache[$element_type] = array_merge_recursive($basic_defaults, $info);
1375        }
1376      }
1377    }
1378  
1379    return $cache[$type];
1380  }
1381  
1382  function form_options_flatten($array, $reset = TRUE) {
1383    static $return;
1384  
1385    if ($reset) {
1386      $return = array();
1387    }
1388  
1389    foreach ($array as $key => $value) {
1390      if (is_object($value)) {
1391        form_options_flatten($value->option, FALSE);
1392      }
1393      else if (is_array($value)) {
1394        form_options_flatten($value, FALSE);
1395      }
1396      else {
1397        $return[$key] = 1;
1398      }
1399    }
1400  
1401    return $return;
1402  }
1403  
1404  /**
1405   * Format a dropdown menu or scrolling selection box.
1406   *
1407   * @param $element
1408   *   An associative array containing the properties of the element.
1409   *   Properties used: title, value, options, description, extra, multiple, required
1410   * @return
1411   *   A themed HTML string representing the form element.
1412   *
1413   * @ingroup themeable
1414   *
1415   * It is possible to group options together; to do this, change the format of
1416   * $options to an associative array in which the keys are group labels, and the
1417   * values are associative arrays in the normal $options format.
1418   */
1419  function theme_select($element) {
1420    $select = '';
1421    $size = $element['#size'] ? ' size="'. $element['#size'] .'"' : '';
1422    _form_set_class($element, array('form-select'));
1423    $multiple = $element['#multiple'];
1424    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>');
1425  }
1426  
1427  function form_select_options($element, $choices = NULL) {
1428    if (!isset($choices)) {
1429      $choices = $element['#options'];
1430    }
1431    // array_key_exists() accommodates the rare event where $element['#value'] is NULL.
1432    // isset() fails in this situation.
1433    $value_valid = isset($element['#value']) || array_key_exists('#value', $element);
1434    $value_is_array = is_array($element['#value']);
1435    $options = '';
1436    foreach ($choices as $key => $choice) {
1437      if (is_array($choice)) {
1438        $options .= '<optgroup label="'. $key .'">';
1439        $options .= form_select_options($element, $choice);
1440        $options .= '</optgroup>';
1441      }
1442      elseif (is_object($choice)) {
1443        $options .= form_select_options($element, $choice->option);
1444      }
1445      else {
1446        $key = (string)$key;
1447        if ($value_valid && (!$value_is_array && (string)$element['#value'] === $key || ($value_is_array && in_array($key, $element['#value'])))) {
1448          $selected = ' selected="selected"';
1449        }
1450        else {
1451          $selected = '';
1452        }
1453        $options .= '<option value="'. check_plain($key) .'"'. $selected .'>'. check_plain($choice) .'</option>';
1454      }
1455    }
1456    return $options;
1457  }
1458  
1459  /**
1460   * Traverses a select element's #option array looking for any values
1461   * that hold the given key. Returns an array of indexes that match.
1462   *
1463   * This function is useful if you need to modify the options that are
1464   * already in a form element; for example, to remove choices which are
1465   * not valid because of additional filters imposed by another module.
1466   * One example might be altering the choices in a taxonomy selector.
1467   * To correctly handle the case of a multiple hierarchy taxonomy,
1468   * #options arrays can now hold an array of objects, instead of a
1469   * direct mapping of keys to labels, so that multiple choices in the
1470   * selector can have the same key (and label). This makes it difficult
1471   * to manipulate directly, which is why this helper function exists.
1472   *
1473   * This function does not support optgroups (when the elements of the
1474   * #options array are themselves arrays), and will return FALSE if
1475   * arrays are found. The caller must either flatten/restore or
1476   * manually do their manipulations in this case, since returning the
1477   * index is not sufficient, and supporting this would make the
1478   * "helper" too complicated and cumbersome to be of any help.
1479   *
1480   * As usual with functions that can return array() or FALSE, do not
1481   * forget to use === and !== if needed.
1482   *
1483   * @param $element
1484   *   The select element to search.
1485   * @param $key
1486   *   The key to look for.
1487   * @return
1488   *   An array of indexes that match the given $key. Array will be
1489   *   empty if no elements were found. FALSE if optgroups were found.
1490   */
1491  function form_get_options($element, $key) {
1492    $keys = array();
1493    foreach ($element['#options'] as $index => $choice) {
1494      if (is_array($choice)) {
1495        return FALSE;
1496      }
1497      else if (is_object($choice)) {
1498        if (isset($choice->option[$key])) {
1499          $keys[] = $index;
1500        }
1501      }
1502      else if ($index == $key) {
1503        $keys[] = $index;
1504      }
1505    }
1506    return $keys;
1507  }
1508  
1509  /**
1510   * Format a group of form items.
1511   *
1512   * @param $element
1513   *   An associative array containing the properties of the element.
1514   *   Properties used: attributes, title, value, description, children, collapsible, collapsed
1515   * @return
1516   *   A themed HTML string representing the form item group.
1517   *
1518   * @ingroup themeable
1519   */
1520  function theme_fieldset($element) {
1521    if (!empty($element['#collapsible'])) {
1522      drupal_add_js('misc/collapse.js');
1523  
1524      if (!isset($element['#attributes']['class'])) {
1525        $element['#attributes']['class'] = '';
1526      }
1527  
1528      $element['#attributes']['class'] .= ' collapsible';
1529      if (!empty($element['#collapsed'])) {
1530        $element['#attributes']['class'] .= ' collapsed';
1531      }
1532    }
1533  
1534    return '<fieldset'. drupal_attributes($element['#attributes']) .'>'. ($element['#title'] ? '<legend>'. $element['#title'] .'</legend>' : '') . (isset($element['#description']) && $element['#description'] ? '<div class="description">'. $element['#description'] .'</div>' : '') . (!empty($element['#children']) ? $element['#children'] : '') . (isset($element['#value']) ? $element['#value'] : '') ."</fieldset>\n";
1535  }
1536  
1537  /**
1538   * Format a radio button.
1539   *
1540   * @param $element
1541   *   An associative array containing the properties of the element.
1542   *   Properties used: required, return_value, value, attributes, title, description
1543   * @return
1544   *   A themed HTML string representing the form item group.
1545   *
1546   * @ingroup themeable
1547   */
1548  function theme_radio($element) {
1549    _form_set_class($element, array('form-radio'));
1550    $output = '<input type="radio" ';
1551    $output .= 'id="'. $element['#id'] .'" ';
1552    $output .= 'name="'. $element['#name'] .'" ';
1553    $output .= 'value="'. $element['#return_value'] .'" ';
1554    $output .= (check_plain($element['#value']) == $element['#return_value']) ? ' checked="checked" ' : ' ';
1555    $output .= drupal_attributes($element['#attributes']) .' />';
1556    if (!is_null($element['#title'])) {
1557      $output = '<label class="option" for="'. $element['#id'] .'">'. $output .' '. $element['#title'] .'</label>';
1558    }
1559  
1560    unset($element['#title']);
1561    return theme('form_element', $element, $output);
1562  }
1563  
1564  /**
1565   * Format a set of radio buttons.
1566   *
1567   * @param $element
1568   *   An associative array containing the properties of the element.
1569   *   Properties used: title, value, options, description, required and attributes.
1570   * @return
1571   *   A themed HTML string representing the radio button set.
1572   *
1573   * @ingroup themeable
1574   */
1575  function theme_radios($element) {
1576    $class = 'form-radios';
1577    if (isset($element['#attributes']['class'])) {
1578      $class .= ' '. $element['#attributes']['class'];
1579    }
1580    $element['#children'] = '<div class="'. $class .'">'. (!empty($element['#children']) ? $element['#children'] : '') .'</div>';
1581    if ($element['#title'] || $element['#description']) {
1582      unset($element['#id']);
1583      return theme('form_element', $element, $element['#children']);
1584    }
1585    else {
1586      return $element['#children'];
1587    }
1588  }
1589  
1590  /**
1591   * Format a password_confirm item.
1592   *
1593   * @param $element
1594   *   An associative array containing the properties of the element.
1595   *   Properties used: title, value, id, required, error.
1596   * @return
1597   *   A themed HTML string representing the form item.
1598   *
1599   * @ingroup themeable
1600   */
1601  function theme_password_confirm($element) {
1602    return theme('form_element', $element, $element['#children']);
1603  }
1604  
1605  /**
1606   * Expand a password_confirm field into two text boxes.
1607   */
1608  function expand_password_confirm($element) {
1609    $element['pass1'] =  array(
1610      '#type' => 'password',
1611      '#title' => t('Password'),
1612      '#value' => empty($element['#value']) ? NULL : $element['#value']['pass1'],
1613      '#required' => $element['#required'],
1614      '#attributes' => array('class' => 'password-field'),
1615    );
1616    $element['pass2'] =  array(
1617      '#type' => 'password',
1618      '#title' => t('Confirm password'),
1619      '#value' => empty($element['#value']) ? NULL : $element['#value']['pass2'],
1620      '#required' => $element['#required'],
1621      '#attributes' => array('class' => 'password-confirm'),
1622    );
1623    $element['#element_validate'] = array('password_confirm_validate');
1624    $element['#tree'] = TRUE;
1625  
1626    if (isset($element['#size'])) {
1627      $element['pass1']['#size'] = $element['pass2']['#size'] = $element['#size'];
1628    }
1629  
1630    return $element;
1631  }
1632  
1633  /**
1634   * Validate password_confirm element.
1635   */
1636  function password_confirm_validate($form, &$form_state) {
1637    $pass1 = trim($form['pass1']['#value']);
1638    if (!empty($pass1)) {
1639      $pass2 = trim($form['pass2']['#value']);
1640      if (strcmp($pass1, $pass2)) {
1641        form_error($form, t('The specified passwords do not match.'));
1642      }
1643    }
1644    elseif ($form['#required'] && !empty($form['#post'])) {
1645      form_error($form, t('Password field is required.'));
1646    }
1647  
1648    // Password field must be converted from a two-element array into a single
1649    // string regardless of validation results.
1650    form_set_value($form['pass1'], NULL, $form_state);
1651    form_set_value($form['pass2'], NULL, $form_state);
1652    form_set_value($form, $pass1, $form_state);
1653  
1654    return $form;
1655  
1656  }
1657  
1658  /**
1659   * Format a date selection element.
1660   *
1661   * @param $element
1662   *   An associative array containing the properties of the element.
1663   *   Properties used: title, value, options, description, required and attributes.
1664   * @return
1665   *   A themed HTML string representing the date selection boxes.
1666   *
1667   * @ingroup themeable
1668   */
1669  function theme_date($element) {
1670    return theme('form_element', $element, '<div class="container-inline">'. $element['#children'] .'</div>');
1671  }
1672  
1673  /**
1674   * Roll out a single date element.
1675   */
1676  function expand_date($element) {
1677    // Default to current date
1678    if (empty($element['#value'])) {
1679      $element['#value'] = array('day' => format_date(time(), 'custom', 'j'),
1680                              'month' => format_date(time(), 'custom', 'n'),
1681                              'year' => format_date(time(), 'custom', 'Y'));
1682    }
1683  
1684    $element['#tree'] = TRUE;
1685  
1686    // Determine the order of day, month, year in the site's chosen date format.
1687    $format = variable_get('date_format_short', 'm/d/Y - H:i');
1688    $sort = array();
1689    $sort['day'] = max(strpos($format, 'd'), strpos($format, 'j'));
1690    $sort['month'] = max(strpos($format, 'm'), strpos($format, 'M'));
1691    $sort['year'] = strpos($format, 'Y');
1692    asort($sort);
1693    $order = array_keys($sort);
1694  
1695    // Output multi-selector for date.
1696    foreach ($order as $type) {
1697      switch ($type) {
1698        case 'day':
1699          $options = drupal_map_assoc(range(1, 31));
1700          break;
1701        case 'month':
1702          $options = drupal_map_assoc(range(1, 12), 'map_month');
1703          break;
1704        case 'year':
1705          $options = drupal_map_assoc(range(1900, 2050));
1706          break;
1707      }
1708      $parents = $element['#parents'];
1709      $parents[] = $type;
1710      $element[$type] = array(
1711        '#type' => 'select',
1712        '#value' => $element['#value'][$type],
1713        '#attributes' => $element['#attributes'],
1714        '#options' => $options,
1715      );
1716    }
1717  
1718    return $element;
1719  }
1720  
1721  /**
1722   * Validates the date type to stop dates like February 30, 2006.
1723   */
1724  function date_validate($form) {
1725    if (!checkdate($form['#value']['month'], $form['#value']['day'], $form['#value']['year'])) {
1726      form_error($form, t('The specified date is invalid.'));
1727    }
1728  }
1729  
1730  /**
1731   * Helper function for usage with drupal_map_assoc to display month names.
1732   */
1733  function map_month($month) {
1734    return format_date(gmmktime(0, 0, 0, $month, 2, 1970), 'custom', 'M', 0);
1735  }
1736  
1737  /**
1738   * If no default value is set for weight select boxes, use 0.
1739   */
1740  function weight_value(&$form) {
1741    if (isset($form['#default_value'])) {
1742      $form['#value'] = $form['#default_value'];
1743    }
1744    else {
1745      $form['#value'] = 0;
1746    }
1747  }
1748  
1749  /**
1750   * Roll out a single radios element to a list of radios,
1751   * using the options array as index.
1752   */
1753  function expand_radios($element) {
1754    if (count($element['#options']) > 0) {
1755      foreach ($element['#options'] as $key => $choice) {
1756        if (!isset($element[$key])) {
1757          // Generate the parents as the autogenerator does, so we will have a
1758          // unique id for each radio button.
1759          $parents_for_id = array_merge($element['#parents'], array($key));
1760          $element[$key] = array(
1761            '#type' => 'radio',
1762            '#title' => $choice,
1763            '#return_value' => check_plain($key),
1764            '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : NULL,
1765            '#attributes' => $element['#attributes'],
1766            '#parents' => $element['#parents'],
1767            '#id' => form_clean_id('edit-'. implode('-', $parents_for_id)),
1768            '#ahah' => isset($element['#ahah']) ? $element['#ahah'] : NULL,
1769          );
1770        }
1771      }
1772    }
1773    return $element;
1774  }
1775  
1776  /**
1777   * Add AHAH information about a form element to the page to communicate with
1778   * javascript. If #ahah[path] is set on an element, this additional javascript is
1779   * added to the page header to attach the AHAH behaviors. See ahah.js for more
1780   * information.
1781   *
1782   * @param $element
1783   *   An associative array containing the properties of the element.
1784   *   Properties used: ahah_event, ahah_path, ahah_wrapper, ahah_parameters,
1785   *   ahah_effect.
1786   * @return
1787   *   None. Additional code is added to the header of the page using
1788   *   drupal_add_js.
1789   */
1790  function form_expand_ahah($element) {
1791    static $js_added = array();
1792    // Add a reasonable default event handler if none specified.
1793    if (isset($element['#ahah']['path']) && !isset($element['#ahah']['event'])) {
1794      switch ($element['#type']) {
1795        case 'submit':
1796        case 'button':
1797        case 'image_button':
1798          // Use the mousedown instead of the click event because form
1799          // submission via pressing the enter key triggers a click event on
1800          // submit inputs, inappropriately triggering AHAH behaviors.
1801          $element['#ahah']['event'] = 'mousedown';
1802          // Attach an additional event handler so that AHAH behaviours
1803          // can be triggered still via keyboard input.
1804          $element['#ahah']['keypress'] = TRUE;
1805          break;
1806        case 'password':
1807        case 'textfield':
1808        case 'textarea':
1809          $element['#ahah']['event'] = 'blur';
1810          break;
1811        case 'radio':
1812        case 'checkbox':
1813        case 'select':
1814          $element['#ahah']['event'] = 'change';
1815          break;
1816      }
1817    }
1818  
1819    // Adding the same javascript settings twice will cause a recursion error,
1820    // we avoid the problem by checking if the javascript has already been added.
1821    if (isset($element['#ahah']['path']) && isset($element['#ahah']['event']) && !isset($js_added[$element['#id']])) {
1822      drupal_add_js('misc/jquery.form.js');
1823      drupal_add_js('misc/ahah.js');
1824  
1825      $ahah_binding = array(
1826        'url'      => url($element['#ahah']['path']),
1827        'event'    => $element['#ahah']['event'],
1828        'keypress' => empty($element['#ahah']['keypress']) ? NULL : $element['#ahah']['keypress'],
1829        'wrapper'  => empty($element['#ahah']['wrapper']) ? NULL : $element['#ahah']['wrapper'],
1830        'selector' => empty($element['#ahah']['selector']) ? '#'. $element['#id'] : $element['#ahah']['selector'],
1831        'effect'   => empty($element['#ahah']['effect']) ? 'none' : $element['#ahah']['effect'],
1832        'method'   => empty($element['#ahah']['method']) ? 'replace' : $element['#ahah']['method'],
1833        'progress' => empty($element['#ahah']['progress']) ? array('type' => 'throbber') : $element['#ahah']['progress'],
1834        'button'   => isset($element['#executes_submit_callback']) ? array($element['#name'] => $element['#value']) : FALSE,
1835      );
1836  
1837      // Convert a simple #ahah[progress] type string into an array.
1838      if (is_string($ahah_binding['progress'])) {
1839        $ahah_binding['progress'] = array('type' => $ahah_binding['progress']);
1840      }
1841      // Change progress path to a full url.
1842      if (isset($ahah_binding['progress']['path'])) {
1843        $ahah_binding['progress']['url'] = url($ahah_binding['progress']['path']);
1844      }
1845  
1846      // Add progress.js if we're doing a bar display.
1847      if ($ahah_binding['progress']['type'] == 'bar') {
1848        drupal_add_js('misc/progress.js');
1849      }
1850  
1851      drupal_add_js(array('ahah' => array($element['#id'] => $ahah_binding)), 'setting');
1852  
1853      $js_added[$element['#id']] = TRUE;
1854      $element['#cache'] = TRUE;
1855    }
1856    return $element;
1857  }
1858  
1859  /**
1860   * Format a form item.
1861   *
1862   * @param $element
1863   *   An associative array containing the properties of the element.
1864   *   Properties used:  title, value, description, required, error
1865   * @return
1866   *   A themed HTML string representing the form item.
1867   *
1868   * @ingroup themeable
1869   */
1870  function theme_item($element) {
1871    return theme('form_element', $element, $element['#value'] . (!empty($element['#children']) ? $element['#children'] : ''));
1872  }
1873  
1874  /**
1875   * Format a checkbox.
1876   *
1877   * @param $element
1878   *   An associative array containing the properties of the element.
1879   *   Properties used:  title, value, return_value, description, required
1880   * @return
1881   *   A themed HTML string representing the checkbox.
1882   *
1883   * @ingroup themeable
1884   */
1885  function theme_checkbox($element) {
1886    _form_set_class($element, array('form-checkbox'));
1887    $checkbox = '<input ';
1888    $checkbox .= 'type="checkbox" ';
1889    $checkbox .= 'name="'. $element['#name'] .'" ';
1890    $checkbox .= 'id="'. $element['#id'] .'" ' ;
1891    $checkbox .= 'value="'. $element['#return_value'] .'" ';
1892    $checkbox .= $element['#value'] ? ' checked="checked" ' : ' ';
1893    $checkbox .= drupal_attributes($element['#attributes']) .' />';
1894  
1895    if (!is_null($element['#title'])) {
1896      $checkbox = '<label class="option" for="'. $element['#id'] .'">'. $checkbox .' '. $element['#title'] .'</label>';
1897    }
1898  
1899    unset($element['#title']);
1900    return theme('form_element', $element, $checkbox);
1901  }
1902  
1903  /**
1904   * Format a set of checkboxes.
1905   *
1906   * @param $element
1907   *   An associative array containing the properties of the element.
1908   * @return
1909   *   A themed HTML string representing the checkbox set.
1910   *
1911   * @ingroup themeable
1912   */
1913  function theme_checkboxes($element) {
1914    $class = 'form-checkboxes';
1915    if (isset($element['#attributes']['class'])) {
1916      $class .= ' '. $element['#attributes']['class'];
1917    }
1918    $element['#children'] = '<div class="'. $class .'">'. (!empty($element['#children']) ? $element['#children'] : '') .'</div>';
1919    if ($element['#title'] || $element['#description']) {
1920      unset($element['#id']);
1921      return theme('form_element', $element, $element['#children']);
1922    }
1923    else {
1924      return $element['#children'];
1925    }
1926  }
1927  
1928  function expand_checkboxes($element) {
1929    $value = is_array($element['#value']) ? $element['#value'] : array();
1930    $element['#tree'] = TRUE;
1931    if (count($element['#options']) > 0) {
1932      if (!isset($element['#default_value']) || $element['#default_value'] == 0) {
1933        $element['#default_value'] = array();
1934      }
1935      foreach ($element['#options'] as $key => $choice) {
1936        if (!isset($element[$key])) {
1937          $element[$key] = array(
1938            '#type' => 'checkbox',
1939            '#processed' => TRUE,
1940            '#title' => $choice,
1941            '#return_value' => $key,
1942            '#default_value' => isset($value[$key]),
1943            '#attributes' => $element['#attributes'],
1944            '#ahah' => isset($element['#ahah']) ? $element['#ahah'] : NULL,
1945          );
1946        }
1947      }
1948    }
1949    return $element;
1950  }
1951  
1952  /**
1953   * Theme a form submit button.
1954   *
1955   * @ingroup themeable
1956   */
1957  function theme_submit($element) {
1958    return theme('button', $element);
1959  }
1960  
1961  /**
1962   * Theme a form button.
1963   *
1964   * @ingroup themeable
1965   */
1966  function theme_button($element) {
1967    // Make sure not to overwrite classes.
1968    if (isset($element['#attributes']['class'])) {
1969      $element['#attributes']['class'] = 'form-'. $element['#button_type'] .' '. $element['#attributes']['class'];
1970    }
1971    else {
1972      $element['#attributes']['class'] = 'form-'. $element['#button_type'];
1973    }
1974  
1975    return '<input type="submit" '. (empty($element['#name']) ? '' : 'name="'. $element['#name'] .'" ') .'id="'. $element['#id'] .'" value="'. check_plain($element['#value']) .'" '. drupal_attributes($element['#attributes']) ." />\n";
1976  }
1977  
1978  /**
1979   * Theme a form image button.
1980   *
1981   * @ingroup themeable
1982   */
1983  function theme_image_button($element) {
1984    // Make sure not to overwrite classes.
1985    if (isset($element['#attributes']['class'])) {
1986      $element['#attributes']['class'] = 'form-'. $element['#button_type'] .' '. $element['#attributes']['class'];
1987    }
1988    else {
1989      $element['#attributes']['class'] = 'form-'. $element['#button_type'];
1990    }
1991  
1992    return '<input type="image" name="'. $element['#name'] .'" '.
1993      (!empty($element['#value']) ? ('value="'. check_plain($element['#value']) .'" ') : '') .
1994      'id="'. $element['#id'] .'" '.
1995      drupal_attributes($element['#attributes']) .
1996      ' src="'. base_path() . $element['#src'] .'" '.
1997      (!empty($element['#title']) ? 'alt="'. check_plain($element['#title']) .'" title="'. check_plain($element['#title']) .'" ' : '' ) .
1998      "/>\n";
1999  }
2000  
2001  /**
2002   * Format a hidden form field.
2003   *
2004   * @param $element
2005   *   An associative array containing the properties of the element.
2006   *   Properties used:  value, edit
2007   * @return
2008   *   A themed HTML string representing the hidden form field.
2009   *
2010   * @ingroup themeable
2011   */
2012  function theme_hidden($element) {
2013    return '<input type="hidden" name="'. $element['#name'] .'" id="'. $element['#id'] .'" value="'. check_plain($element['#value']) ."\" ". drupal_attributes($element['#attributes']) ." />\n";
2014  }
2015  
2016  /**
2017   * Format a form token.
2018   *
2019   * @ingroup themeable
2020   */
2021  function theme_token($element) {
2022    return theme('hidden', $element);
2023  }
2024  
2025  /**
2026   * Format a textfield.
2027   *
2028   * @param $element
2029   *   An associative array containing the properties of the element.
2030   *   Properties used:  title, value, description, size, maxlength, required, attributes autocomplete_path
2031   * @return
2032   *   A themed HTML string representing the textfield.
2033   *
2034   * @ingroup themeable
2035   */
2036  function theme_textfield($element) {
2037    $size = empty($element['#size']) ? '' : ' size="'. $element['#size'] .'"';
2038    $maxlength = empty($element['#maxlength']) ? '' : ' maxlength="'. $element['#maxlength'] .'"';
2039    $class = array('form-text');
2040    $extra = '';
2041    $output = '';
2042  
2043    if ($element['#autocomplete_path'] && menu_valid_path(array('link_path' => $element['#autocomplete_path']))) {
2044      drupal_add_js('misc/autocomplete.js');
2045      $class[] = 'form-autocomplete';
2046      $extra =  '<input class="autocomplete" type="hidden" id="'. $element['#id'] .'-autocomplete" value="'. check_url(url($element['#autocomplete_path'], array('absolute' => TRUE))) .'" disabled="disabled" />';
2047    }
2048    _form_set_class($element, $class);
2049  
2050    if (isset($element['#field_prefix'])) {
2051      $output .= '<span class="field-prefix">'. $element['#field_prefix'] .'</span> ';
2052    }
2053  
2054    $output .= '<input type="text"'. $maxlength .' name="'. $element['#name'] .'" id="'. $element['#id'] .'"'. $size .' value="'. check_plain($element['#value']) .'"'. drupal_attributes($element['#attributes']) .' />';
2055  
2056    if (isset($element['#field_suffix'])) {
2057      $output .= ' <span class="field-suffix">'. $element['#field_suffix'] .'</span>';
2058    }
2059  
2060    return theme('form_element', $element, $output) . $extra;
2061  }
2062  
2063  /**
2064   * Format a form.
2065   *
2066   * @param $element
2067   *   An associative array containing the properties of the element.
2068   *   Properties used: action, method, attributes, children
2069   * @return
2070   *   A themed HTML string representing the form.
2071   *
2072   * @ingroup themeable
2073   */
2074  function theme_form($element) {
2075    // Anonymous div to satisfy XHTML compliance.
2076    $action = $element['#action'] ? 'action="'. check_url($element['#action']) .'" ' : '';
2077    return '<form '. $action .' accept-charset="UTF-8" method="'. $element['#method'] .'" id="'. $element['#id'] .'"'. drupal_attributes($element['#attributes']) .">\n<div>". $element['#children'] ."\n</div></form>\n";
2078  }
2079  
2080  /**
2081   * Format a textarea.
2082   *
2083   * @param $element
2084   *   An associative array containing the properties of the element.
2085   *   Properties used: title, value, description, rows, cols, required, attributes
2086   * @return
2087   *   A themed HTML string representing the textarea.
2088   *
2089   * @ingroup themeable
2090   */
2091  function theme_textarea($element) {
2092    $class = array('form-textarea');
2093  
2094    // Add teaser behavior (must come before resizable)
2095    if (!empty($element['#teaser'])) {
2096      drupal_add_js('misc/teaser.js');
2097      // Note: arrays are merged in drupal_get_js().
2098      drupal_add_js(array('teaserCheckbox' => array($element['#id'] => $element['#teaser_checkbox'])), 'setting');
2099      drupal_add_js(array('teaser' => array($element['#id'] => $element['#teaser'])), 'setting');
2100      $class[] = 'teaser';
2101    }
2102  
2103    // Add resizable behavior
2104    if ($element['#resizable'] !== FALSE) {
2105      drupal_add_js('misc/textarea.js');
2106      $class[] = 'resizable';
2107    }
2108  
2109    _form_set_class($element, $class);
2110    return theme('form_element', $element, '<textarea cols="'. $element['#cols'] .'" rows="'. $element['#rows'] .'" name="'. $element['#name'] .'" id="'. $element['#id'] .'" '. drupal_attributes($element['#attributes']) .'>'. check_plain($element['#value']) .'</textarea>');
2111  }
2112  
2113  /**
2114   * Format HTML markup for use in forms.
2115   *
2116   * This is used in more advanced forms, such as theme selection and filter format.
2117   *
2118   * @param $element
2119   *   An associative array containing the properties of the element.
2120   *   Properties used: value, children.
2121   * @return
2122   *   A themed HTML string representing the HTML markup.
2123   *
2124   * @ingroup themeable
2125   */
2126  
2127  function theme_markup($element) {
2128    return (isset($element['#value']) ? $element['#value'] : '') . (isset($element['#children']) ? $element['#children'] : '');
2129  }
2130  
2131  /**
2132   * Format a password field.
2133   *
2134   * @param $element
2135   *   An associative array containing the properties of the element.
2136   *   Properties used:  title, value, description, size, maxlength, required, attributes
2137   * @return
2138   *   A themed HTML string representing the form.
2139   *
2140   * @ingroup themeable
2141   */
2142  function theme_password($element) {
2143    $size = $element['#size'] ? ' size="'. $element['#size'] .'" ' : '';
2144    $maxlength = $element['#maxlength'] ? ' maxlength="'. $element['#maxlength'] .'" ' : '';
2145  
2146    _form_set_class($element, array('form-text'));
2147    $output = '<input type="password" name="'. $element['#name'] .'" id="'. $element['#id'] .'" '. $maxlength . $size . drupal_attributes($element['#attributes']) .' />';
2148    return theme('form_element', $element, $output);
2149  }
2150  
2151  /**
2152   * Expand weight elements into selects.
2153   */
2154  function process_weight($element) {
2155    for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) {
2156      $weights[$n] = $n;
2157    }
2158    $element['#options'] = $weights;
2159    $element['#type'] = 'select';
2160    $element['#is_weight'] = TRUE;
2161    $element += _element_info('select');
2162    return $element;
2163  }
2164  
2165  /**
2166   * Format a file upload field.
2167   *
2168   * @param $title
2169   *   The label for the file upload field.
2170   * @param $name
2171   *   The internal name used to refer to the field.
2172   * @param $size
2173   *   A measure of the visible size of the field (passed directly to HTML).
2174   * @param $description
2175   *   Explanatory text to display after the form item.
2176   * @param $required
2177   *   Whether the user must upload a file to the field.
2178   * @return
2179   *   A themed HTML string representing the field.
2180   *
2181   * @ingroup themeable
2182   *
2183   * For assistance with handling the uploaded file correctly, see the API
2184   * provided by file.inc.
2185   */
2186  function theme_file($element) {
2187    _form_set_class($element, array('form-file'));
2188    return theme('form_element', $element, '<input type="file" name="'. $element['#name'] .'"'. ($element['#attributes'] ? ' '. drupal_attributes($element['#attributes']) : '') .' id="'. $element['#id'] .'" size="'. $element['#size'] ."\" />\n");
2189  }
2190  
2191  /**
2192   * Return a themed form element.
2193   *
2194   * @param element
2195   *   An associative array containing the properties of the element.
2196   *   Properties used: title, description, id, required
2197   * @param $value
2198   *   The form element's data.
2199   * @return
2200   *   A string representing the form element.
2201   *
2202   * @ingroup themeable
2203   */
2204  function theme_form_element($element, $value) {
2205    // This is also used in the installer, pre-database setup.
2206    $t = get_t();
2207  
2208    $output = '<div class="form-item"';
2209    if (!empty($element['#id'])) {
2210      $output .= ' id="'. $element['#id'] .'-wrapper"';
2211    }
2212    $output .= ">\n";
2213    $required = !empty($element['#required']) ? '<span class="form-required" title="'. $t('This field is required.') .'">*</span>' : '';
2214  
2215    if (!empty($element['#title'])) {
2216      $title = $element['#title'];
2217      if (!empty($element['#id'])) {
2218        $output .= ' <label for="'. $element['#id'] .'">'. $t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) ."</label>\n";
2219      }
2220      else {
2221        $output .= ' <label>'. $t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) ."</label>\n";
2222      }
2223    }
2224  
2225    $output .= " $value\n";
2226  
2227    if (!empty($element['#description'])) {
2228      $output .= ' <div class="description">'. $element['#description'] ."</div>\n";
2229    }
2230  
2231    $output .= "</div>\n";
2232  
2233    return $output;
2234  }
2235  
2236  /**
2237   * Sets a form element's class attribute.
2238   *
2239   * Adds 'required' and 'error' classes as needed.
2240   *
2241   * @param &$element
2242   *   The form element.
2243   * @param $name
2244   *   Array of new class names to be added.
2245   */
2246  function _form_set_class(&$element, $class = array()) {
2247    if ($element['#required']) {
2248      $class[] = 'required';
2249    }
2250    if (form_get_error($element)) {
2251      $class[] = 'error';
2252    }
2253    if (isset($element['#attributes']['class'])) {
2254      $class[] = $element['#attributes']['class'];
2255    }
2256    $element['#attributes']['class'] = implode(' ', $class);
2257  }
2258  
2259  /**
2260   * Prepare an HTML ID attribute string for a form item.
2261   *
2262   * Remove invalid characters and guarantee uniqueness.
2263   *
2264   * @param $id
2265   *   The ID to clean.
2266   * @param $flush
2267   *   If set to TRUE, the function will flush and reset the static array
2268   *   which is built to test the uniqueness of element IDs. This is only
2269   *   used if a form has completed the validation process. This parameter
2270   *   should never be set to TRUE if this function is being called to
2271   *   assign an ID to the #ID element.
2272   * @return
2273   *   The cleaned ID.
2274   */
2275  function form_clean_id($id = NULL, $flush = FALSE) {
2276    static $seen_ids = array();
2277  
2278    if ($flush) {
2279      $seen_ids = array();
2280      return;
2281    }
2282    $id = str_replace(array('][', '_', ' '), '-', $id);
2283  
2284    // Ensure IDs are unique. The first occurrence is held but left alone.
2285    // Subsequent occurrences get a number appended to them. This incrementing
2286    // will almost certainly break code that relies on explicit HTML IDs in
2287    // forms that appear more than once on the page, but the alternative is
2288    // outputting duplicate IDs, which would break JS code and XHTML
2289    // validity anyways. For now, it's an acceptable stopgap solution.
2290    if (isset($seen_ids[$id])) {
2291      $id = $id .'-'. $seen_ids[$id]++;
2292    }
2293    else {
2294      $seen_ids[$id] = 1;
2295    }
2296  
2297    return $id;
2298  }
2299  
2300  /**
2301   * @} End of "defgroup form_api".
2302   */
2303  
2304  /**
2305   * @defgroup batch Batch operations
2306   * @{
2307   * Functions allowing forms processing to be spread out over several page
2308   * requests, thus ensuring that the processing does not get interrupted
2309   * because of a PHP timeout, while allowing the user to receive feedback
2310   * on the progress of the ongoing operations.
2311   *
2312   * The API is primarily designed to integrate nicely with the Form API
2313   * workflow, but can also be used by non-FAPI scripts (like update.php)
2314   * or even simple page callbacks (which should probably be used sparingly).
2315   *
2316   * Example:
2317   * @code
2318   * $batch = array(
2319   *   'title' => t('Exporting'),
2320   *   'operations' => array(
2321   *     array('my_function_1', array($account->uid, 'story')),
2322   *     array('my_function_2', array()),
2323   *   ),
2324   *   'finished' => 'my_finished_callback',
2325   *   'file' => 'path_to_file_containing_myfunctions',
2326   * );
2327   * batch_set($batch);
2328   * // only needed if not inside a form _submit handler :
2329   * batch_process();
2330   * @endcode
2331   *
2332   * Note: if the batch 'title', 'init_message', 'progress_message', or
2333   * 'error_message' could contain any user input, it is the responsibility of
2334   * the code calling batch_set() to sanitize them first with a function like
2335   * check_plain() or filter_xss().
2336   *
2337   * Sample batch operations:
2338   * @code
2339   * // Simple and artificial: load a node of a given type for a given user
2340   * function my_function_1($uid, $type, &$context) {
2341   *   // The $context array gathers batch context information about the execution (read),
2342   *   // as well as 'return values' for the current operation (write)
2343   *   // The following keys are provided :
2344   *   // 'results' (read / write): The array of results gathered so far by
2345   *   //   the batch processing, for the current operation to append its own.
2346   *   // 'message' (write): A text message displayed in the progress page.
2347   *   // The following keys allow for multi-step operations :
2348   *   // 'sandbox' (read / write): An array that can be freely used to
2349   *   //   store persistent data between iterations. It is recommended to
2350   *   //   use this instead of $_SESSION, which is unsafe if the user
2351   *   //   continues browsing in a separate window while the batch is processing.
2352   *   // 'finished' (write): A float number between 0 and 1 informing
2353   *   //   the processing engine of the completion level for the operation.
2354   *   //   1 (or no value explicitly set) means the operation is finished
2355   *   //   and the batch processing can continue to the next operation.
2356   *
2357   *   $node = node_load(array('uid' => $uid, 'type' => $type));
2358   *   $context['results'][] = $node->nid .' : '. $node->title;
2359   *   $context['message'] = $node->title;
2360   * }
2361   *
2362   * // More advanced example: multi-step operation - load all nodes, five by five
2363   * function my_function_2(&$context) {
2364   *   if (empty($context['sandbox'])) {
2365   *     $context['sandbox']['progress'] = 0;
2366   *     $context['sandbox']['current_node'] = 0;
2367   *     $context['sandbox']['max'] = db_result(db_query('SELECT COUNT(DISTINCT nid) FROM {node}'));
2368   *   }
2369   *   $limit = 5;
2370   *   $result = db_query_range("SELECT nid FROM {node} WHERE nid > %d ORDER BY nid ASC", $context['sandbox']['current_node'], 0, $limit);
2371   *   while ($row = db_fetch_array($result)) {
2372   *     $node = node_load($row['nid'], NULL, TRUE);
2373   *     $context['results'][] = $node->nid .' : '. $node->title;
2374   *     $context['sandbox']['progress']++;
2375   *     $context['sandbox']['current_node'] = $node->nid;
2376   *     $context['message'] = $node->title;
2377   *   }
2378   *   if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
2379   *     $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
2380   *   }
2381   * }
2382   * @endcode
2383   *
2384   * Sample 'finished' callback:
2385   * @code
2386   * function batch_test_finished($success, $results, $operations) {
2387   *   if ($success) {
2388   *     $message = format_plural(count($results), 'One post processed.', '@count posts processed.');
2389   *   }
2390   *   else {
2391   *     $message = t('Finished with an error.');
2392   *   }
2393   *   drupal_set_message($message);
2394   *   // Providing data for the redirected page is done through $_SESSION.
2395   *   foreach ($results as $result) {
2396   *     $items[] = t('Loaded node %title.', array('%title' => $result));
2397   *   }
2398   *   $_SESSION['my_batch_results'] = $items;
2399   * }
2400   * @endcode
2401   */
2402  
2403  /**
2404   * Opens a new batch.
2405   *
2406   * @param $batch
2407   *   An array defining the batch. The following keys can be used -- only
2408   *   'operations' is required, and batch_init() provides default values for
2409   *   the messages.
2410   *   - 'operations': Array of function calls to be performed.
2411   *     Example:
2412   *     @code
2413   *     array(
2414   *       array('my_function_1', array($arg1)),
2415   *       array('my_function_2', array($arg2_1, $arg2_2)),
2416   *     )
2417   *     @endcode
2418   *   - 'title': Title for the progress page. Only safe strings should be passed.
2419   *     Defaults to t('Processing').
2420   *   - 'init_message': Message displayed while the processing is initialized.
2421   *     Defaults to t('Initializing.').
2422   *   - 'progress_message': Message displayed while processing the batch.
2423   *     Available placeholders are @current, @remaining, @total, @percentage,
2424   *     @estimate and @elapsed. Defaults to t('Completed @current of @total.').
2425   *   - 'error_message': Message displayed if an error occurred while processing
2426   *     the batch. Defaults to t('An error has occurred.').
2427   *   - 'finished': Name of a function to be executed after the batch has
2428   *     completed. This should be used to perform any result massaging that
2429   *     may be needed, and possibly save data in $_SESSION for display after
2430   *     final page redirection.
2431   *   - 'file': Path to the file containing the definitions of the
2432   *     'operations' and 'finished' functions, for instance if they don't
2433   *     reside in the main .module file. The path should be relative to
2434   *     base_path(), and thus should be built using drupal_get_path().
2435   *
2436   * Operations are added as new batch sets. Batch sets are used to ensure
2437   * clean code independence, ensuring that several batches submitted by
2438   * different parts of the code (core / contrib modules) can be processed
2439   * correctly while not interfering or having to cope with each other. Each
2440   * batch set gets to specify his own UI messages, operates on its own set
2441   * of operations and results, and triggers its own 'finished' callback.
2442   * Batch sets are processed sequentially, with the progress bar starting
2443   * fresh for every new set.
2444   */
2445  function batch_set($batch_definition) {
2446    if ($batch_definition) {
2447      $batch =& batch_get();
2448      // Initialize the batch
2449      if (empty($batch)) {
2450        $batch = array(
2451          'sets' => array(),
2452        );
2453      }
2454  
2455      $init = array(
2456        'sandbox' => array(),
2457        'results' => array(),
2458        'success' => FALSE,
2459      );
2460      // Use get_t() to allow batches at install time.
2461      $t = get_t();
2462      $defaults = array(
2463        'title' => $t('Processing'),
2464        'init_message' => $t('Initializing.'),
2465        'progress_message' => $t('Remaining @remaining of @total.'),
2466        'error_message' => $t('An error has occurred.'),
2467      );
2468      $batch_set = $init + $batch_definition + $defaults;
2469  
2470      // Tweak init_message to avoid the bottom of the page flickering down after init phase.
2471      $batch_set['init_message'] .= '<br/>&nbsp;';
2472      $batch_set['total'] = count($batch_set['operations']);
2473  
2474      // If the batch is being processed (meaning we are executing a stored submit handler),
2475      // insert the new set after the current one.
2476      if (isset($batch['current_set'])) {
2477        // array_insert does not exist...
2478        $slice1 = array_slice($batch['sets'], 0, $batch['current_set'] + 1);
2479        $slice2 = array_slice($batch['sets'], $batch['current_set'] + 1);
2480        $batch['sets'] = array_merge($slice1, array($batch_set), $slice2);
2481      }
2482      else {
2483        $batch['sets'][] = $batch_set;
2484      }
2485    }
2486  }
2487  
2488  /**
2489   * Processes the batch.
2490   *
2491   * Unless the batch has been marked with 'progressive' = FALSE, the function
2492   * issues a drupal_goto and thus ends page execution.
2493   *
2494   * This function is not needed in form submit handlers; Form API takes care
2495   * of batches that were set during form submission.
2496   *
2497   * @param $redirect
2498   *   (optional) Path to redirect to when the batch has finished processing.
2499   * @param $url
2500   *   (optional - should only be used for separate scripts like update.php)
2501   *   URL of the batch processing page.
2502   */
2503  function batch_process($redirect = NULL, $url = NULL) {
2504    $batch =& batch_get();
2505  
2506    if (isset($batch)) {
2507      // Add process information
2508      $url = isset($url) ? $url : 'batch';
2509      $process_info = array(
2510        'current_set' => 0,
2511        'progressive' => TRUE,
2512        'url' => isset($url) ? $url : 'batch',
2513        'source_page' => $_GET['q'],
2514        'redirect' => $redirect,
2515      );
2516      $batch += $process_info;
2517  
2518      if ($batch['progressive']) {
2519        // Clear the way for the drupal_goto redirection to the batch processing
2520        // page, by saving and unsetting the 'destination' if any, on both places
2521        // drupal_goto looks for it.
2522        if (isset($_REQUEST['destination'])) {
2523          $batch['destination'] = $_REQUEST['destination'];
2524          unset($_REQUEST['destination']);
2525        }
2526        elseif (isset($_REQUEST['edit']['destination'])) {
2527          $batch['destination'] = $_REQUEST['edit']['destination'];
2528          unset($_REQUEST['edit']['destination']);
2529        }
2530  
2531        // Initiate db storage in order to get a batch id. We have to provide
2532        // at least an empty string for the (not null) 'token' column.
2533        db_query("INSERT INTO {batch} (token, timestamp) VALUES ('', %d)", time());
2534        $batch['id'] = db_last_insert_id('batch', 'bid');
2535  
2536        // Now that we have a batch id, we can generate the redirection link in
2537        // the generic error message.
2538        $t = get_t();
2539        $batch['error_message'] = $t('Please continue to <a href="@error_url">the error page</a>', array('@error_url' => url($url, array('query' => array('id' => $batch['id'], 'op' => 'finished')))));
2540  
2541        // Actually store the batch data and the token generated form the batch id.
2542        db_query("UPDATE {batch} SET token = '%s', batch = '%s' WHERE bid = %d", drupal_get_token($batch['id']), serialize($batch), $batch['id']);
2543  
2544        drupal_goto($batch['url'], 'op=start&id='. $batch['id']);
2545      }
2546      else {
2547        // Non-progressive execution: bypass the whole progressbar workflow
2548        // and execute the batch in one pass.
2549        require_once  './includes/batch.inc';
2550        _batch_process();
2551      }
2552    }
2553  }
2554  
2555  /**
2556   * Retrieves the current batch.
2557   */
2558  function &batch_get() {
2559    static $batch = array();
2560    return $batch;
2561  }
2562  
2563  /**
2564   * @} End of "defgroup batch".
2565   */


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7