[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

/includes/ -> form.inc (source)

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