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