home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / CMS / drupal-6.0.exe / drupal-6.0 / includes / form.inc < prev    next >
Encoding:
Text File  |  2008-02-11  |  90.4 KB  |  2,509 lines

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