Skip to content
Snippets Groups Projects
Select Git revision
  • 26dce525a2928967aaa34280e5e36d9a46226d75
  • 11.x default protected
  • 11.2.x protected
  • 10.5.x protected
  • 10.6.x protected
  • 11.1.x protected
  • 10.4.x protected
  • 11.0.x protected
  • 10.3.x protected
  • 7.x protected
  • 10.2.x protected
  • 10.1.x protected
  • 9.5.x protected
  • 10.0.x protected
  • 9.4.x protected
  • 9.3.x protected
  • 9.2.x protected
  • 9.1.x protected
  • 8.9.x protected
  • 9.0.x protected
  • 8.8.x protected
  • 10.5.1 protected
  • 11.2.2 protected
  • 11.2.1 protected
  • 11.2.0 protected
  • 10.5.0 protected
  • 11.2.0-rc2 protected
  • 10.5.0-rc1 protected
  • 11.2.0-rc1 protected
  • 10.4.8 protected
  • 11.1.8 protected
  • 10.5.0-beta1 protected
  • 11.2.0-beta1 protected
  • 11.2.0-alpha1 protected
  • 10.4.7 protected
  • 11.1.7 protected
  • 10.4.6 protected
  • 11.1.6 protected
  • 10.3.14 protected
  • 10.4.5 protected
  • 11.0.13 protected
41 results

form.inc

Blame
  • Dries Buytaert's avatar
    - Rollback of #447816 -- didn't meant to commit that just yet.
    Dries Buytaert authored
    26dce525
    History
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    form.inc 154.24 KiB
    <?php
    // $Id$
    
    /**
     * @defgroup forms Form builder functions
     * @{
     * Functions that build an abstract representation of a HTML form.
     *
     * All modules should declare their form builder functions to be in this
     * group and each builder function should reference its validate and submit
     * functions using \@see. Conversely, validate and submit functions should
     * reference the form builder function using \@see. For examples, of this see
     * system_modules_uninstall() or user_pass(), the latter of which has the
     * following in its doxygen documentation:
     *
     * \@ingroup forms
     * \@see user_pass_validate().
     * \@see user_pass_submit().
     *
     * @} End of "defgroup forms".
     */
    
    /**
     * @defgroup form_api Form generation
     * @{
     * Functions to enable the processing and display of HTML forms.
     *
     * Drupal uses these functions to achieve consistency in its form processing and
     * presentation, while simplifying code and reducing the amount of HTML that
     * must be explicitly generated by modules.
     *
     * The primary function used with forms is drupal_get_form(), which is
     * used for forms presented interactively to a user. Forms can also be built and
     * submitted programmatically without any user input using the
     * drupal_form_submit() function.
     *
     * drupal_get_form() handles retrieving, processing, and displaying a rendered
     * HTML form for modules automatically.
     *
     * Here is an example of how to use drupal_get_form() and a form builder
     * function:
     * @code
     * $form = drupal_get_form('my_module_example_form');
     * ...
     * function my_module_example_form($form, &$form_state) {
     *   $form['submit'] = array(
     *     '#type' => 'submit',
     *     '#value' => t('Submit'),
     *   );
     *   return $form;
     * }
     * function my_module_example_form_validate($form, &$form_state) {
     *   // Validation logic.
     * }
     * function my_module_example_form_submit($form, &$form_state) {
     *   // Submission logic.
     * }
     * @endcode
     *
     * Or with any number of additional arguments:
     * @code
     * $extra = "extra";
     * $form = drupal_get_form('my_module_example_form', $extra);
     * ...
     * function my_module_example_form($form, &$form_state, $extra) {
     *   $form['submit'] = array(
     *     '#type' => 'submit',
     *     '#value' => $extra,
     *   );
     *   return $form;
     * }
     * @endcode
     *
     * The $form argument to form-related functions is a structured array containing
     * the elements and properties of the form. For information on the array
     * components and format, and more detailed explanations of the Form API
     * workflow, see the
     * @link http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html Form API reference @endlink
     * and the
     * @link http://drupal.org/node/37775 Form API section of the handbook. @endlink
     * In addition, there is a set of Form API tutorials in
     * @link form_example_tutorial.inc the Form Example Tutorial @endlink which
     * provide basics all the way up through multistep forms.
     *
     * In the form builder, validation, submission, and other form functions,
     * $form_state is the primary influence on the processing of the form and is
     * passed by reference to most functions, so they use it to communicate with
     * the form system and each other.
     *
     * The $form_state keys are:
     * - 'values': An associative array of values submitted to the form. The
     *   validation functions and submit functions use this array for nearly all
     *   their decision making. (Note that
     *   @link http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7#tree #tree @endlink
     *   determines whether the values are a flat array or an array whose structure
     *   parallels the $form array.)
     * - 'rebuild': If the submit function sets $form_state['rebuild'] to TRUE,
     *   submission is not completed and instead the form is rebuilt using any
     *   information that the submit function has made available to the form builder
     *   function via $form_state. This is commonly used for wizard-style
     *   multi-step forms, add-more buttons, and the like. For further information
     *   see drupal_build_form().
     * - 'redirect': a URL that will be used to redirect the form on submission.
     *   See drupal_redirect_form() for complete information.
     * - 'storage': $form_state['storage'] is not a special key, and no specific
     *   support is provided for it in the Form API, but by tradition it was
     *   the location where application-specific data was stored for communication
     *   between the submit, validation, and form builder functions, especially
     *   in a multi-step-style form. Form implementations may use any key(s) within
     *   $form_state (other than the keys listed here and other reserved ones used
     *   by Form API internals) for this kind of storage. The recommended way to
     *   ensure that the chosen key doesn't conflict with ones used by the Form API
     *   or other modules is to use the module name as the key name or a prefix for
     *   the key name. For example, the Node module uses $form_state['node'] in node
     *   editing forms to store information about the node being edited, and this
     *   information stays available across successive clicks of the "Preview"
     *   button as well as when the "Save" button is finally clicked.
     * - 'temporary': Since values for all non-reserved keys in $form_state persist
     *   throughout a multistep form sequence, the Form API provides the 'temporary'
     *   key for modules to use for communicating information across form-related
     *   functions during a single page request only. There is no use-case for this
     *   functionality in core.
     * - 'triggering_element': (read-only) The form element that triggered
     *   submission. This is the same as the deprecated
     *   $form_state['clicked_button']. It is the element that caused submission,
     *   which may or may not be a button (in the case of AJAX forms.) This is
     *   often used to distinguish between various buttons in a submit handler,
     *   and is also used in AJAX handlers.
     * - 'cache': The typical form workflow involves two page requests. During the
     *   first page request, a form is built and returned for the user to fill in.
     *   Then the user fills the form in and submits it, triggering a second page
     *   request in which the form must be built and processed. By default, $form
     *   and $form_state are built from scratch during each of these page requests.
     *   In some special use-cases, it is necessary or desired to persist the $form
     *   and $form_state variables from the initial page request to the one that
     *   processes the submission. A form builder function can set 'cache' to TRUE
     *   to do this. One example where this is needed is to handle AJAX submissions,
     *   so ajax_process_form() sets this for all forms that include an element with
     *   a #ajax property. (In AJAX, the handler has no way to build the form
     *   itself, so must rely on the cached version created on each page load, so
     *   it's a classic example of this use case.) Note that the persistence of
     *   $form and $form_state across successive submissions of a multi-step form
     *   happens automatically regardless of the value for 'cache'.
     * - 'input': The array of values as they were submitted by the user. These are
     *   raw and unvalidated, so should not be used without a thorough understanding
     *   of security implications. In almost all cases, code should use the data in
     *   the 'values' array exclusively. The most common use of this key is for
     *   multi-step forms that need to clear some of the user input when setting
     *   'rebuild'.
     */
    
    /**
     * Wrapper for drupal_build_form() for use when $form_state is not needed.
     *
     * @param $form_id
     *   The unique string identifying the desired form. If a function with that
     *   name exists, it is called to build the form array. Modules that need to
     *   generate the same form (or very similar forms) using different $form_ids
     *   can implement hook_forms(), which maps different $form_id values to the
     *   proper form constructor function. Examples may be found in node_forms(),
     *   search_forms(), and user_forms().
     * @param ...
     *   Any additional arguments are passed on to the functions called by
     *   drupal_get_form(), including the unique form constructor function. For
     *   example, the node_edit form requires that a node object is passed in here
     *   when it is called.
     *
     * @return
     *   The form array.
     *
     * @see drupal_build_form()
     */
    function drupal_get_form($form_id) {
      $form_state = array();
    
      $args = func_get_args();
      // Remove $form_id from the arguments.
      array_shift($args);
      $form_state['build_info']['args'] = $args;
    
      return drupal_build_form($form_id, $form_state);
    }
    
    /**
     * Build and process a form based on a form id.
     *
     * The form may also be retrieved from the cache if the form was built in a
     * previous page-load. The form is then passed on for processing, validation
     * and submission if there is proper input.
     *
     * @param $form_id
     *   The unique string identifying the desired form. If a function with that
     *   name exists, it is called to build the form array. Modules that need to
     *   generate the same form (or very similar forms) using different $form_ids
     *   can implement hook_forms(), which maps different $form_id values to the
     *   proper form constructor function. Examples may be found in node_forms(),
     *   search_forms(), and user_forms().
     * @param &$form_state
     *   An array which stores information about the form. This is passed as a
     *   reference so that the caller can use it to examine what in the form changed
     *   when the form submission process is complete. Furthermore, it may be used
     *   to store information related to the processed data in the form, which will
     *   persist across page requests when the 'cache' or 'rebuild' flag is set.
     *   The following parameters may be set in $form_state to affect how the form
     *   is rendered:
     *   - build_info: A keyed array of build information that is necessary to
     *     rebuild the form from cache when the original context may no longer be
     *     available:
     *     - args: An array of arguments to pass to the form builder.
     *     - files: An optional array defining include files that need to be loaded
     *       for building the form. Each array entry may be the path to a file or
     *       another array containing values for the parameters 'type', 'module' and
     *       'name' as needed by module_load_include(). The files listed here are
     *       automatically loaded by form_get_cache(). Defaults to the current menu
     *       router item's 'file' definition, if existent.
     *   - rebuild: Normally, after the entire form processing is completed and
     *     submit handlers ran, a form is considered to be done and
     *     drupal_redirect_form() will redirect the user to a new page using a GET
     *     request (so a browser refresh does not re-submit the form). However, if
     *     'rebuild' has been set to TRUE, then a new copy of the form is
     *     immediately built and sent to the browser; instead of a redirect. This is
     *     used for multi-step forms, such as wizards and confirmation forms. Also,
     *     if a form validation handler has set 'rebuild' to TRUE and a validation
     *     error occurred, then the form is rebuilt prior to being returned,
     *     enabling form elements to be altered, as appropriate to the particular
     *     validation error.
     *   - input: An array of input that corresponds to $_POST or $_GET, depending
     *     on the 'method' chosen (see below).
     *   - method: The HTTP form method to use for finding the input for this form.
     *     May be 'post' or 'get'. Defaults to 'post'. Note that 'get' method
     *     forms do not use form ids so are always considered to be submitted, which
     *     can have unexpected effects. The 'get' method should only be used on
     *     forms that do not change data, as that is exclusively the domain of post.
     *   - no_redirect: If set to TRUE the form will NOT perform a drupal_goto(),
     *     even if 'redirect' is set.
     *   - cache: If set to TRUE the original, unprocessed form structure will be
     *     cached, which allows to rebuild the entire form from cache.
     *   - no_cache: If set to TRUE the form will NOT be cached, even if 'cache' is
     *     set.
     *   - always_process: If TRUE and the method is GET, a form_id is not
     *     necessary. This should only be used on RESTful GET forms that do NOT
     *     write data, as this could lead to security issues. It is useful so that
     *     searches do not need to have a form_id in their query arguments to
     *     trigger the search.
     *   - must_validate: Ordinarily, a form is only validated once but there are
     *     times when a form is resubmitted internally and should be validated
     *     again. Setting this to TRUE will force that to happen. This is most
     *     likely to occur during AHAH or AJAX operations.
     *   - temporary: An array holding temporary data accessible during the current
     *     page request only. It may be used to temporary save any data that doesn't
     *     need to or shouldn't be cached during the whole form workflow, e.g. data
     *     that needs to be accessed during the current form build process only.
     *   - wrapper_callback: Modules that wish to pre-populate certain forms with
     *     common elements, such as back/next/save buttons in multi-step form
     *     wizards, may define a form builder function name that returns a form
     *     structure, which is passed on to the actual form builder function.
     *     Such implementations may either define the 'wrapper_callback' via
     *     hook_forms() or have to invoke drupal_build_form() (instead of
     *     drupal_get_form()) on their own in a custom menu callback to prepare
     *     $form_state accordingly.
     *   Further $form_state properties controlling the redirection behavior after
     *   form submission may be found in drupal_redirect_form().
     *
     * @return
     *   The rendered form or NULL, depending upon the $form_state flags that were set.
     *
     * @see drupal_redirect_form()
     */
    function drupal_build_form($form_id, &$form_state) {
      // Ensure some defaults; if already set they will not be overridden.
      $form_state += form_state_defaults();
    
      if (!isset($form_state['input'])) {
        $form_state['input'] = $form_state['method'] == 'get' ? $_GET : $_POST;
      }
    
      if (isset($_SESSION['batch_form_state'])) {
        // We've been redirected here after a batch processing : the form has
        // already been processed, so we grab the post-process $form_state value
        // and move on to form display. See _batch_finished() function.
        $form_state = $_SESSION['batch_form_state'];
        unset($_SESSION['batch_form_state']);
      }
      else {
        // If the incoming input contains a form_build_id, we'll check the
        // cache for a copy of the form in question. If it's there, we don't
        // have to rebuild the form to proceed. In addition, if there is stored
        // form_state data from a previous step, we'll retrieve it so it can
        // be passed on to the form processing code.
        $check_cache = isset($form_state['input']['form_id']) && $form_state['input']['form_id'] == $form_id && !empty($form_state['input']['form_build_id']);
        if ($check_cache) {
          $form_build_id = $form_state['input']['form_build_id'];
          $form = form_get_cache($form_build_id, $form_state);
        }
    
        // If the previous bit of code didn't result in a populated $form
        // object, we're hitting the form for the first time and we need
        // to build it from scratch.
        if (!isset($form)) {
          // Record the filepath of the include file containing the original form,
          // so the form builder callbacks can be loaded when the form is being
          // rebuilt from cache on a different path (such as 'system/ajax'). See
          // form_get_cache().
          // $menu_get_item() is not available at installation time.
          if (!isset($form_state['build_info']['files']['menu']) && !defined('MAINTENANCE_MODE')) {
            $item = menu_get_item();
            if (!empty($item['include_file'])) {
              $form_state['build_info']['files']['menu'] = $item['include_file'];
            }
          }
    
          // If we attempted to serve the form from cache, uncacheable $form_state
          // keys need to be removed after retrieving and preparing the form, except
          // any that were already set prior to retrieving the form.
          if ($check_cache) {
            $form_state_before_retrieval = $form_state;
          }
    
          $form = drupal_retrieve_form($form_id, $form_state);
          $form_build_id = 'form-' . drupal_hash_base64(uniqid(mt_rand(), TRUE) . mt_rand());
          $form['#build_id'] = $form_build_id;
    
          // Fix the form method, if it is 'get' in $form_state, but not in $form.
          if ($form_state['method'] == 'get' && !isset($form['#method'])) {
            $form['#method'] = 'get';
          }
    
          drupal_prepare_form($form_id, $form, $form_state);
          // Store a copy of the unprocessed form to cache in case
          // $form_state['cache'] is set.
          $original_form = $form;
    
          // form_set_cache() removes uncacheable $form_state keys defined in
          // form_state_keys_no_cache() in order for multi-step forms to work
          // properly. This means that form processing logic for single-step forms
          // using $form_state['cache'] may depend on data stored in those keys
          // during drupal_retrieve_form()/drupal_prepare_form(), but form
          // processing should not depend on whether the form is cached or not, so
          // $form_state is adjusted to match what it would be after a
          // form_set_cache()/form_get_cache() sequence. These exceptions are
          // allowed to survive here:
          // - always_process: Does not make sense in conjunction with form caching
          //   in the first place, since passing form_build_id as a GET parameter is
          //   not desired.
          // - temporary: Any assigned data is expected to survives within the same
          //   page request.
          if ($check_cache) {
            $form_state = array_diff_key($form_state, array_flip(array_diff(form_state_keys_no_cache(), array('always_process', 'temporary')))) + $form_state_before_retrieval;
          }
        }
    
        // Now that we know we have a form, we'll process it (validating,
        // submitting, and handling the results returned by its submission
        // handlers. Submit handlers accumulate data in the form_state by
        // altering the $form_state variable, which is passed into them by
        // reference.
        drupal_process_form($form_id, $form, $form_state);
      }
    
      // Most simple, single-step forms will be finished by this point --
      // drupal_process_form() usually redirects to another page (or to
      // a 'fresh' copy of the form) once processing is complete. If one
      // of the form's handlers has set $form_state['redirect'] to FALSE,
      // the form will simply be re-rendered with the values still in its
      // fields.
      //
      // If $form_state['rebuild'] has been set and input has been processed, we
      // know that we're in a multi-part process of some sort and the form's
      // workflow is not complete. We need to construct a fresh copy of the form,
      // passing in the latest $form_state in addition to any other variables passed
      // into drupal_get_form().
      if ($form_state['rebuild'] && $form_state['process_input'] && !form_get_errors()) {
        $form = drupal_rebuild_form($form_id, $form_state);
      }
      // After processing the form, the form builder or a #process callback may
      // have set $form_state['cache'] to indicate that the original form and the
      // $form_state shall be cached. But the form may only be cached if the
      // special 'no_cache' property is not set to TRUE and we are not rebuilding.
      elseif (isset($form_build_id) && $form_state['cache'] && empty($form_state['no_cache'])) {
        // Cache the original, unprocessed form upon initial build of the form.
        if (isset($original_form)) {
          form_set_cache($form_build_id, $original_form, $form_state);
        }
        // After processing a cached form, only update the cached form state.
        else {
          form_set_cache($form_build_id, NULL, $form_state);
        }
      }
    
      // Don't override #theme if someone already set it.
      if (!isset($form['#theme'])) {
        drupal_theme_initialize();
        $registry = theme_get_registry();
        if (isset($registry[$form_id])) {
          $form['#theme'] = $form_id;
        }
      }
    
      return $form;
    }
    
    /**
     * Retrieve default values for the $form_state array.
     */
    function form_state_defaults() {
      return array(
        'rebuild' => FALSE,
        'redirect' => NULL,
        'build_info' => array('args' => array()),
        'temporary' => array(),
        'submitted' => FALSE,
        'programmed' => FALSE,
        'cache'=> FALSE,
        'method' => 'post',
        'groups' => array(),
        'buttons' => array(),
      );
    }
    
    /**
     * Retrieves a form, caches it and processes it again.
     *
     * If your AJAX callback simulates the pressing of a button, then your AJAX
     * callback will need to do the same as what drupal_get_form() would do when the
     * button is pressed: get the form from the cache, run drupal_process_form over
     * it and then if it needs rebuild, run drupal_rebuild_form() over it. Then send
     * back a part of the returned form.
     * $form_state['triggering_element']['#array_parents'] will help you to find
     * which part.
     * @see ajax_form_callback() for an example.
     *
     * @param $form_id
     *   The unique string identifying the desired form. If a function
     *   with that name exists, it is called to build the form array.
     *   Modules that need to generate the same form (or very similar forms)
     *   using different $form_ids can implement hook_forms(), which maps
     *   different $form_id values to the proper form constructor function. Examples
     *   may be found in node_forms(), search_forms(), and user_forms().
     * @param $form_state
     *   A keyed array containing the current state of the form.
     * @param $old_form
     *   (optional) A previously built $form. Used to retain the #build_id and
     *   #action properties in AJAX callbacks and similar partial form rebuilds.
     *   Should not be passed for regular rebuilds, for which the entire $form
     *   should be rebuilt freshly.
     *
     * @return
     *   The newly built form.
     */
    function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) {
      // AJAX and other contexts may call drupal_rebuild_form() even when
      // $form_state['rebuild'] isn't set, but _form_builder_handle_input_element()
      // needs to distinguish a rebuild from an initial build in order to process
      // user input correctly. Form constructors and form processing functions may
      // also need to handle a rebuild differently than an initial build.
      $form_state['rebuild'] = TRUE;
    
      $form = drupal_retrieve_form($form_id, $form_state);
    
      // If only parts of the form will be returned to the browser (e.g. AJAX or
      // RIA clients), re-use the old #build_id to not require client-side code to
      // manually update the hidden 'build_id' input element.
      // Otherwise, a new #build_id is generated, to not clobber the previous
      // build's data in the form cache; also allowing the user to go back to an
      // earlier build, make changes, and re-submit.
      $form['#build_id'] = isset($old_form['#build_id']) ? $old_form['#build_id'] : 'form-' . drupal_hash_base64(uniqid(mt_rand(), TRUE) . mt_rand());
    
      // #action defaults to request_uri(), but in case of AJAX and other partial
      // rebuilds, the form is submitted to an alternate URL, and the original
      // #action needs to be retained.
      if (isset($old_form['#action'])) {
        $form['#action'] = $old_form['#action'];
      }
    
      drupal_prepare_form($form_id, $form, $form_state);
    
      if (empty($form_state['no_cache'])) {
        // We cache the form structure and the form state so it can be retrieved
        // later for validation.
        form_set_cache($form['#build_id'], $form, $form_state);
      }
    
      // Clear out all group associations as these might be different when
      // re-rendering the form.
      $form_state['groups'] = array();
    
      // Do not call drupal_process_form(), since it would prevent the rebuilt form
      // to submit.
      $form = form_builder($form_id, $form, $form_state);
      return $form;
    }
    
    /**
     * Fetch a form from cache.
     */
    function form_get_cache($form_build_id, &$form_state) {
      if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) {
        $form = $cached->data;
    
        global $user;
        if ((isset($form['#cache_token']) && drupal_valid_token($form['#cache_token'])) || (!isset($form['#cache_token']) && !$user->uid)) {
          if ($cached = cache_get('form_state_' . $form_build_id, 'cache_form')) {
            // Re-populate $form_state for subsequent rebuilds.
            $form_state = $cached->data + $form_state;
    
            // If the original form is contained in include files, load the files.
            // See drupal_build_form().
            $form_state['build_info'] += array('files' => array());
            foreach ($form_state['build_info']['files'] as $file) {
              if (is_array($file)) {
                $file += array('type' => 'inc', 'name' => $file['module']);
                module_load_include($file['type'], $file['module'], $file['name']);
              }
              elseif (file_exists($file)) {
                require_once DRUPAL_ROOT . '/' . $file;
              }
            }
          }
          return $form;
        }
      }
    }
    
    /**
     * Store a form in the cache.
     */
    function form_set_cache($form_build_id, $form, $form_state) {
      // 6 hours cache life time for forms should be plenty.
      $expire = 21600;
    
      // Cache form structure.
      if (isset($form)) {
        if ($GLOBALS['user']->uid) {
          $form['#cache_token'] = drupal_get_token();
        }
        cache_set('form_' . $form_build_id, $form, 'cache_form', REQUEST_TIME + $expire);
      }
    
      // Cache form state.
      if ($data = array_diff_key($form_state, array_flip(form_state_keys_no_cache()))) {
        cache_set('form_state_' . $form_build_id, $data, 'cache_form', REQUEST_TIME + $expire);
      }
    }
    
    /**
     * Returns an array of $form_state keys that shouldn't be cached.
     */
    function form_state_keys_no_cache() {
      return array(
        // Public properties defined by form constructors and form handlers.
        'always_process',
        'must_validate',
        'rebuild',
        'redirect',
        'no_redirect',
        'temporary',
        // Internal properties defined by form processing.
        'buttons',
        'triggering_element',
        'clicked_button',
        'complete form',
        'groups',
        'input',
        'method',
        'submit_handlers',
        'submitted',
        'validate_handlers',
        'values',
      );
    }
    
    /**
     * Retrieves, populates, and processes a form.
     *
     * This function allows you to supply values for form elements and submit a
     * form for processing. Compare to drupal_get_form(), which also builds and
     * processes a form, but does not allow you to supply values.
     *
     * There is no return value, but you can check to see if there are errors
     * by calling form_get_errors().
     *
     * @param $form_id
     *   The unique string identifying the desired form. If a function
     *   with that name exists, it is called to build the form array.
     *   Modules that need to generate the same form (or very similar forms)
     *   using different $form_ids can implement hook_forms(), which maps
     *   different $form_id values to the proper form constructor function. Examples
     *   may be found in node_forms(), search_forms(), and user_forms().
     * @param $form_state
     *   A keyed array containing the current state of the form. Most important is
     *   the $form_state['values'] collection, a tree of data used to simulate the
     *   incoming $_POST information from a user's form submission. If a key is not
     *   filled in $form_state['values'], then the default value of the respective
     *   element is used. To submit an unchecked checkbox or other control that
     *   browsers submit by not having a $_POST entry, include the key, but set the
     *   value to NULL.
     * @param ...
     *   Any additional arguments are passed on to the functions called by
     *   drupal_form_submit(), including the unique form constructor function.
     *   For example, the node_edit form requires that a node object be passed
     *   in here when it is called. Arguments that need to be passed by reference
     *   should not be included here, but rather placed directly in the $form_state
     *   build info array so that the reference can be preserved. For example, a
     *   form builder function with the following signature:
     *   @code
     *   function mymodule_form($form, &$form_state, &$object) {
     *   }
     *   @endcode
     *   would be called via drupal_form_submit() as follows:
     *   @code
     *   $form_state['values'] = $my_form_values;
     *   $form_state['build_info']['args'] = array(&$object);
     *   drupal_form_submit('mymodule_form', $form_state);
     *   @endcode
     * For example:
     * @code
     * // register a new user
     * $form_state = array();
     * $form_state['values']['name'] = 'robo-user';
     * $form_state['values']['mail'] = 'robouser@example.com';
     * $form_state['values']['pass']['pass1'] = 'password';
     * $form_state['values']['pass']['pass2'] = 'password';
     * $form_state['values']['op'] = t('Create new account');
     * drupal_form_submit('user_register_form', $form_state);
     * @endcode
     */
    function drupal_form_submit($form_id, &$form_state) {
      if (!isset($form_state['build_info']['args'])) {
        $args = func_get_args();
        array_shift($args);
        array_shift($args);
        $form_state['build_info']['args'] = $args;
      }
      // Merge in default values.
      $form_state += form_state_defaults();
    
      $form = drupal_retrieve_form($form_id, $form_state);
      $form_state['input'] = $form_state['values'];
      $form_state['programmed'] = TRUE;
      // Programmed forms are always submitted.
      $form_state['submitted'] = TRUE;
    
      // Reset form validation.
      $form_state['must_validate'] = TRUE;
      form_clear_error();
    
      drupal_prepare_form($form_id, $form, $form_state);
      drupal_process_form($form_id, $form, $form_state);
    }
    
    /**
     * Retrieves the structured array that defines a given form.
     *
     * @param $form_id
     *   The unique string identifying the desired form. If a function
     *   with that name exists, it is called to build the form array.
     *   Modules that need to generate the same form (or very similar forms)
     *   using different $form_ids can implement hook_forms(), which maps
     *   different $form_id values to the proper form constructor function.
     * @param $form_state
     *   A keyed array containing the current state of the form, including the
     *   additional arguments to drupal_get_form() or drupal_form_submit() in the
     *   'args' component of the array.
     */
    function drupal_retrieve_form($form_id, &$form_state) {
      $forms = &drupal_static(__FUNCTION__);
    
      // We save two copies of the incoming arguments: one for modules to use
      // when mapping form ids to constructor functions, and another to pass to
      // the constructor function itself.
      $args = $form_state['build_info']['args'];
    
      // We first check to see if there's a function named after the $form_id.
      // If there is, we simply pass the arguments on to it to get the form.
      if (!function_exists($form_id)) {
        // In cases where many form_ids need to share a central constructor function,
        // such as the node editing form, modules can implement hook_forms(). It
        // maps one or more form_ids to the correct constructor functions.
        //
        // We cache the results of that hook to save time, but that only works
        // for modules that know all their form_ids in advance. (A module that
        // adds a small 'rate this comment' form to each comment in a list
        // would need a unique form_id for each one, for example.)
        //
        // So, we call the hook if $forms isn't yet populated, OR if it doesn't
        // yet have an entry for the requested form_id.
        if (!isset($forms) || !isset($forms[$form_id])) {
          $forms = module_invoke_all('forms', $form_id, $args);
        }
        $form_definition = $forms[$form_id];
        if (isset($form_definition['callback arguments'])) {
          $args = array_merge($form_definition['callback arguments'], $args);
        }
        if (isset($form_definition['callback'])) {
          $callback = $form_definition['callback'];
        }
        // In case $form_state['wrapper_callback'] is not defined already, we also
        // allow hook_forms() to define one.
        if (!isset($form_state['wrapper_callback']) && isset($form_definition['wrapper_callback'])) {
          $form_state['wrapper_callback'] = $form_definition['wrapper_callback'];
        }
      }
    
      $form = array();
      // We need to pass $form_state by reference in order for forms to modify it,
      // since call_user_func_array() requires that referenced variables are passed
      // explicitly.
      $args = array_merge(array($form, &$form_state), $args);
    
      // When the passed $form_state (not using drupal_get_form()) defines a
      // 'wrapper_callback', then it requests to invoke a separate (wrapping) form
      // builder function to pre-populate the $form array with form elements, which
      // the actual form builder function ($callback) expects. This allows for
      // pre-populating a form with common elements for certain forms, such as
      // back/next/save buttons in multi-step form wizards. See drupal_build_form().
      if (isset($form_state['wrapper_callback']) && function_exists($form_state['wrapper_callback'])) {
        $form = call_user_func_array($form_state['wrapper_callback'], $args);
        // Put the prepopulated $form into $args.
        $args[0] = $form;
      }
    
      // If $callback was returned by a hook_forms() implementation, call it.
      // Otherwise, call the function named after the form id.
      $form = call_user_func_array(isset($callback) ? $callback : $form_id, $args);
      $form['#form_id'] = $form_id;
    
      return $form;
    }
    
    /**
     * Processes a form submission.
     *
     * This function is the heart of form API. The form gets built, validated and in
     * appropriate cases, submitted.
     *
     * @param $form_id
     *   The unique string identifying the current form.
     * @param $form
     *   An associative array containing the structure of the form.
     * @param $form_state
     *   A keyed array containing the current state of the form. This
     *   includes the current persistent storage data for the form, and
     *   any data passed along by earlier steps when displaying a
     *   multi-step form. Additional information, like the sanitized $_POST
     *   data, is also accumulated here.
     */
    function drupal_process_form($form_id, &$form, &$form_state) {
      $form_state['values'] = array();
    
      // With $_GET, these forms are always submitted if requested.
      if ($form_state['method'] == 'get' && !empty($form_state['always_process'])) {
        if (!isset($form_state['input']['form_build_id'])) {
          $form_state['input']['form_build_id'] = $form['#build_id'];
        }
        if (!isset($form_state['input']['form_id'])) {
          $form_state['input']['form_id'] = $form_id;
        }
        if (!isset($form_state['input']['form_token']) && isset($form['#token'])) {
          $form_state['input']['form_token'] = drupal_get_token($form['#token']);
        }
      }
    
      // Build the form.
      $form = form_builder($form_id, $form, $form_state);
    
      // Only process the input if we have a correct form submission.
      if ($form_state['process_input']) {
        drupal_validate_form($form_id, $form, $form_state);
    
        // drupal_html_id() maintains a cache of element IDs it has seen,
        // so it can prevent duplicates. We want to be sure we reset that
        // cache when a form is processed, so scenarios that result in
        // the form being built behind the scenes and again for the
        // browser don't increment all the element IDs needlessly.
        drupal_static_reset('drupal_html_id');
    
        if ($form_state['submitted'] && !form_get_errors() && !$form_state['rebuild']) {
          // Execute form submit handlers.
          form_execute_handlers('submit', $form, $form_state);
    
          // We'll clear out the cached copies of the form and its stored data
          // here, as we've finished with them. The in-memory copies are still
          // here, though.
          if (!variable_get('cache', 0) && !empty($form_state['values']['form_build_id'])) {
            cache_clear_all('form_' . $form_state['values']['form_build_id'], 'cache_form');
            cache_clear_all('form_state_' . $form_state['values']['form_build_id'], 'cache_form');
          }
    
          // If batches were set in the submit handlers, we process them now,
          // possibly ending execution. We make sure we do not react to the batch
          // that is already being processed (if a batch operation performs a
          // drupal_form_submit).
          if ($batch =& batch_get() && !isset($batch['current_set'])) {
            // Store $form_state information in the batch definition.
            // We need the full $form_state when either:
            // - Some submit handlers were saved to be called during batch
            //   processing. See form_execute_handlers().
            // - The form is multistep.
            // In other cases, we only need the information expected by
            // drupal_redirect_form().
            if ($batch['has_form_submits'] || !empty($form_state['rebuild'])) {
              $batch['form_state'] = $form_state;
            }
            else {
              $batch['form_state'] = array_intersect_key($form_state, array_flip(array('programmed', 'rebuild', 'storage', 'no_redirect', 'redirect')));
            }
    
            $batch['progressive'] = !$form_state['programmed'];
            batch_process();
    
            // Execution continues only for programmatic forms.
            // For 'regular' forms, we get redirected to the batch processing
            // page. Form redirection will be handled in _batch_finished(),
            // after the batch is processed.
          }
    
          // Set a flag to indicate the the form has been processed and executed.
          $form_state['executed'] = TRUE;
    
          // Redirect the form based on values in $form_state.
          drupal_redirect_form($form_state);
        }
      }
    }
    
    /**
     * Prepares a structured form array by adding required elements,
     * executing any hook_form_alter functions, and optionally inserting
     * a validation token to prevent tampering.
     *
     * @param $form_id
     *   A unique string identifying the form for validation, submission,
     *   theming, and hook_form_alter functions.
     * @param $form
     *   An associative array containing the structure of the form.
     * @param $form_state
     *   A keyed array containing the current state of the form. Passed
     *   in here so that hook_form_alter() calls can use it, as well.
     */
    function drupal_prepare_form($form_id, &$form, &$form_state) {
      global $user;
    
      $form['#type'] = 'form';
      $form_state['programmed'] = isset($form_state['programmed']) ? $form_state['programmed'] : FALSE;
    
      if (isset($form['#build_id'])) {
        $form['form_build_id'] = array(
          '#type' => 'hidden',
          '#value' => $form['#build_id'],
          '#id' => $form['#build_id'],
          '#name' => 'form_build_id',
        );
      }
    
      // Add a token, based on either #token or form_id, to any form displayed to
      // authenticated users. This ensures that any submitted form was actually
      // requested previously by the user and protects against cross site request
      // forgeries.
      // This does not apply to programmatically submitted forms. Furthermore, since
      // tokens are session-bound and forms displayed to anonymous users are very
      // likely cached, we cannot assign a token for them.
      // During installation, there is no $user yet.
      if (!empty($user->uid) && !$form_state['programmed']) {
        // Form constructors may explicitly set #token to FALSE when cross site
        // request forgery is irrelevant to the form, such as search forms.
        if (isset($form['#token']) && $form['#token'] === FALSE) {
          unset($form['#token']);
        }
        // Otherwise, generate a public token based on the form id.
        else {
          $form['#token'] = $form_id;
          $form['form_token'] = array(
            '#id' => drupal_html_id('edit-' . $form_id . '-form-token'),
            '#type' => 'token',
            '#default_value' => drupal_get_token($form['#token']),
          );
        }
      }
    
      if (isset($form_id)) {
        $form['form_id'] = array(
          '#type' => 'hidden',
          '#value' => $form_id,
          '#id' => drupal_html_id("edit-$form_id"),
        );
      }
      if (!isset($form['#id'])) {
        $form['#id'] = drupal_html_id($form_id);
      }
    
      $form += element_info('form');
      $form += array('#tree' => FALSE, '#parents' => array());
    
      if (!isset($form['#validate'])) {
        if (function_exists($form_id . '_validate')) {
          $form['#validate'] = array($form_id . '_validate');
        }
      }
    
      if (!isset($form['#submit'])) {
        if (function_exists($form_id . '_submit')) {
          // We set submit here so that it can be altered.
          $form['#submit'] = array($form_id . '_submit');
        }
      }
    
      // Invoke hook_form_alter() and hook_form_FORM_ID_alter() implementations.
      drupal_alter(array('form', 'form_' . $form_id), $form, $form_state, $form_id);
    }
    
    
    /**
     * Validates user-submitted form data from the $form_state using
     * the validate functions defined in a structured form array.
     *
     * @param $form_id
     *   A unique string identifying the form for validation, submission,
     *   theming, and hook_form_alter functions.
     * @param $form
     *   An associative array containing the structure of the form, which is passed
     *   by reference. Form validation handlers are able to alter the form structure
     *   (like #process and #after_build callbacks during form building) in case of
     *   a validation error. If a validation handler alters the form structure, it
     *   is responsible for validating the values of changed form elements in
     *   $form_state['values'] to prevent form submit handlers from receiving
     *   unvalidated values.
     * @param $form_state
     *   A keyed array containing the current state of the form. The current
     *   user-submitted data is stored in $form_state['values'], though
     *   form validation functions are passed an explicit copy of the
     *   values for the sake of simplicity. Validation handlers can also
     *   $form_state to pass information on to submit handlers. For example:
     *     $form_state['data_for_submission'] = $data;
     *   This technique is useful when validation requires file parsing,
     *   web service requests, or other expensive requests that should
     *   not be repeated in the submission step.
     */
    function drupal_validate_form($form_id, &$form, &$form_state) {
      $validated_forms = &drupal_static(__FUNCTION__, array());
    
      if (isset($validated_forms[$form_id]) && empty($form_state['must_validate'])) {
        return;
      }
    
      // If the session token was set by drupal_prepare_form(), ensure that it
      // matches the current user's session.
      if (isset($form['#token'])) {
        if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) {
          // Setting this error will cause the form to fail validation.
          form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));
        }
      }
    
      _form_validate($form, $form_state, $form_id);
      $validated_forms[$form_id] = TRUE;
    }
    
    /**
     * Redirects the user to a URL after a form has been processed.
     *
     * After a form was executed, the data in $form_state controls whether the form
     * is redirected. By default, we redirect to a new destination page. The path of
     * the destination page can be set in $form_state['redirect']. If that is not
     * set, the user is redirected to the current page to display a fresh,
     * unpopulated copy of the form.
     *
     * There are several triggers that may prevent a redirection though:
     * - If $form_state['redirect'] is FALSE, a form builder function or form
     *   validation/submit handler does not want a user to be redirected, which
     *   means that drupal_goto() is not invoked. For most forms, the redirection
     *   logic will be the same regardless of whether $form_state['redirect'] is
     *   undefined or FALSE. However, in case it was not defined and the current
     *   request contains a 'destination' query string, drupal_goto() will redirect
     *   to that given destination instead. Only setting $form_state['redirect'] to
     *   FALSE will prevent any redirection.
     * - If $form_state['no_redirect'] is TRUE, then the callback that originally
     *   built the form explicitly disallows any redirection, regardless of the
     *   redirection value in $form_state['redirect']. For example, ajax_get_form()
     *   defines $form_state['no_redirect'] when building a form in an AJAX
     *   callback to prevent any redirection. $form_state['no_redirect'] should NOT
     *   be altered by form builder functions or form validation/submit handlers.
     * - If $form_state['programmed'] is TRUE, the form submission was usually
     *   invoked via drupal_form_submit(), so any redirection would break the script
     *   that invoked drupal_form_submit().
     * - If $form_state['rebuild'] is TRUE, the form needs to be rebuilt without
     *   redirection.
     *
     * @param $form_state
     *   A keyed array containing the current state of the form.
     *
     * @see drupal_process_form()
     * @see drupal_build_form()
     */
    function drupal_redirect_form($form_state) {
      // Skip redirection for form submissions invoked via drupal_form_submit().
      if (!empty($form_state['programmed'])) {
        return;
      }
      // Skip redirection if rebuild is activated.
      if (!empty($form_state['rebuild'])) {
        return;
      }
      // Skip redirection if it was explicitly disallowed.
      if (!empty($form_state['no_redirect'])) {
        return;
      }
      // Only invoke drupal_goto() if redirect value was not set to FALSE.
      if (!isset($form_state['redirect']) || $form_state['redirect'] !== FALSE) {
        if (isset($form_state['redirect'])) {
          if (is_array($form_state['redirect'])) {
            call_user_func_array('drupal_goto', $form_state['redirect']);
          }
          else {
            // This function can be called from the installer, which guarantees
            // that $redirect will always be a string, so catch that case here
            // and use the appropriate redirect function.
            $function = drupal_installation_attempted() ? 'install_goto' : 'drupal_goto';
            $function($form_state['redirect']);
          }
        }
        drupal_goto($_GET['q']);
      }
    }
    
    /**
     * Performs validation on form elements. First ensures required fields are
     * completed, #maxlength is not exceeded, and selected options were in the
     * list of options given to the user. Then calls user-defined validators.
     *
     * @param $elements
     *   An associative array containing the structure of the form.
     * @param $form_state
     *   A keyed array containing the current state of the form. The current
     *   user-submitted data is stored in $form_state['values'], though
     *   form validation functions are passed an explicit copy of the
     *   values for the sake of simplicity. Validation handlers can also
     *   $form_state to pass information on to submit handlers. For example:
     *     $form_state['data_for_submission'] = $data;
     *   This technique is useful when validation requires file parsing,
     *   web service requests, or other expensive requests that should
     *   not be repeated in the submission step.
     * @param $form_id
     *   A unique string identifying the form for validation, submission,
     *   theming, and hook_form_alter functions.
     */
    function _form_validate(&$elements, &$form_state, $form_id = NULL) {
      // Also used in the installer, pre-database setup.
      $t = get_t();
    
      // Recurse through all children.
      foreach (element_children($elements) as $key) {
        if (isset($elements[$key]) && $elements[$key]) {
          _form_validate($elements[$key], $form_state);
        }
      }
    
      // Validate the current input.
      if (!isset($elements['#validated']) || !$elements['#validated']) {
        // The following errors are always shown.
        if (isset($elements['#needs_validation'])) {
          // Verify that the value is not longer than #maxlength.
          if (isset($elements['#maxlength']) && drupal_strlen($elements['#value']) > $elements['#maxlength']) {
            form_error($elements, $t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%max' => $elements['#maxlength'], '%length' => drupal_strlen($elements['#value']))));
          }
    
          if (isset($elements['#options']) && isset($elements['#value'])) {
            if ($elements['#type'] == 'select') {
              $options = form_options_flatten($elements['#options']);
            }
            else {
              $options = $elements['#options'];
            }
            if (is_array($elements['#value'])) {
              $value = $elements['#type'] == 'checkboxes' ? array_keys($elements['#value']) : $elements['#value'];
              foreach ($value as $v) {
                if (!isset($options[$v])) {
                  form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.'));
                  watchdog('form', 'Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
                }
              }
            }
            elseif (!isset($options[$elements['#value']])) {
              form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.'));
              watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
            }
          }
        }
    
        // While this element is being validated, it may be desired that some calls
        // to form_set_error() be suppressed and not result in a form error, so
        // that a button that implements low-risk functionality (such as "Previous"
        // or "Add more") that doesn't require all user input to be valid can still
        // have its submit handlers triggered. The triggering element's
        // #limit_validation_errors property contains the information for which
        // errors are needed, and all other errors are to be suppressed. The
        // #limit_validation_errors property is ignored if submit handlers will run,
        // but the element doesn't have a #submit property, because it's too large a
        // security risk to have any invalid user input when executing form-level
        // submit handlers.
        if (isset($form_state['triggering_element']['#limit_validation_errors']) && ($form_state['triggering_element']['#limit_validation_errors'] !== FALSE) && !($form_state['submitted'] && !isset($form_state['triggering_element']['#submit']))) {
          form_set_error(NULL, '', $form_state['triggering_element']['#limit_validation_errors']);
        }
        // If submit handlers won't run (due to the submission having been triggered
        // by an element whose #executes_submit_callback property isn't TRUE), then
        // it's safe to suppress all validation errors, and we do so by default,
        // which is particularly useful during an AJAX submission triggered by a
        // non-button. An element can override this default by setting the
        // #limit_validation_errors property. For button element types,
        // #limit_validation_errors defaults to FALSE (via system_element_info()),
        // so that full validation is their default behavior.
        elseif (isset($form_state['triggering_element']) && !isset($form_state['triggering_element']['#limit_validation_errors']) && !$form_state['submitted']) {
          form_set_error(NULL, '', array());
        }
        // As an extra security measure, explicitly turn off error suppression if
        // one of the above conditions wasn't met. Since this is also done at the
        // end of this function, doing it here is only to handle the rare edge case
        // where a validate handler invokes form processing of another form.
        else {
          drupal_static_reset('form_set_error:limit_validation_errors');
        }
    
        // Make sure a value is passed when the field is required.
        // A simple call to empty() will not cut it here as some fields, like
        // checkboxes, can return a valid value of '0'. Instead, check the
        // length if it's a string, and the item count if it's an array.
        // An unchecked checkbox has a #value of integer 0, different than string
        // '0', which could be a valid value.
        if (isset($elements['#needs_validation']) && $elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0) || $elements['#value'] === 0)) {
          form_error($elements, $t('!name field is required.', array('!name' => $elements['#title'])));
        }
    
        // Call user-defined form level validators.
        if (isset($form_id)) {
          form_execute_handlers('validate', $elements, $form_state);
        }
        // Call any element-specific validators. These must act on the element
        // #value data.
        elseif (isset($elements['#element_validate'])) {
          foreach ($elements['#element_validate'] as $function) {
            $function($elements, $form_state, $form_state['complete form']);
          }
        }
        $elements['#validated'] = TRUE;
      }
    
      // Done validating this element, so turn off error suppression.
      // _form_validate() turns it on again when starting on the next element, if
      // it's still appropriate to do so.
      drupal_static_reset('form_set_error:limit_validation_errors');
    }
    
    /**
     * A helper function used to execute custom validation and submission
     * handlers for a given form. Button-specific handlers are checked
     * first. If none exist, the function falls back to form-level handlers.
     *
     * @param $type
     *   The type of handler to execute. 'validate' or 'submit' are the
     *   defaults used by Form API.
     * @param $form
     *   An associative array containing the structure of the form.
     * @param $form_state
     *   A keyed array containing the current state of the form. If the user
     *   submitted the form by clicking a button with custom handler functions
     *   defined, those handlers will be stored here.
     */
    function form_execute_handlers($type, &$form, &$form_state) {
      $return = FALSE;
      // If there was a button pressed, use its handlers.
      if (isset($form_state[$type . '_handlers'])) {
        $handlers = $form_state[$type . '_handlers'];
      }
      // Otherwise, check for a form-level handler.
      elseif (isset($form['#' . $type])) {
        $handlers = $form['#' . $type];
      }
      else {
        $handlers = array();
      }
    
      foreach ($handlers as $function) {
        // Check if a previous _submit handler has set a batch, but make sure we
        // do not react to a batch that is already being processed (for instance
        // if a batch operation performs a drupal_form_submit()).
        if ($type == 'submit' && ($batch =& batch_get()) && !isset($batch['id'])) {
          // Some previous submit handler has set a batch. To ensure correct
          // execution order, store the call in a special 'control' batch set.
          // See _batch_next_set().
          $batch['sets'][] = array('form_submit' => $function);
          $batch['has_form_submits'] = TRUE;
        }
        else {
          $function($form, $form_state);
        }
        $return = TRUE;
      }
      return $return;
    }
    
    /**
     * Files an error against a form element.
     *
     * When a validation error is detected, the validator calls form_set_error() to
     * indicate which element needs to be changed and provide an error message. This
     * causes the Form API to not execute the form submit handlers, and instead to
     * re-display the form to the user with the corresponding elements rendered with
     * an 'error' CSS class (shown as red by default).
     *
     * The standard form_set_error() behavior can be changed if a button provides
     * the #limit_validation_errors property. Multistep forms not wanting to
     * validate the whole form can set #limit_validation_errors on buttons to
     * limit validation errors to only certain elements. For example, pressing the
     * "Previous" button in a multistep form should not fire validation errors just
     * because the current step has invalid values. If #limit_validation_errors is
     * set on a clicked button, the button must also define a #submit property
     * (may be set to an empty array). Any #submit handlers will be executed even if
     * there is invalid input, so extreme care should be taken with respect to any
     * actions taken by them. This is typically not a problem with buttons like
     * "Previous" or "Add more" that do not invoke persistent storage of the
     * submitted form values. Do not use the #limit_validation_errors property on
     * buttons that trigger saving of form values to the database.
     *
     * The #limit_validation_errors property is a list of "sections" within
     * $form_state['values'] that must contain valid values. Each "section" is an
     * array with the ordered set of keys needed to reach that part of
     * $form_state['values'] (i.e., the #parents property of the element).
     *
     * Example 1: Allow the "Previous" button to function, regardless of whether any
     * user input is valid.
     *
     * @code
     *   $form['actions']['previous'] = array(
     *     '#type' => 'submit',
     *     '#value' => t('Previous'),
     *     '#limit_validation_errors' => array(),       // No validation.
     *     '#submit' => array('some_submit_function'),  // #submit required.
     *   );
     * @endcode
     *
     * Example 2: Require some, but not all, user input to be valid to process the
     * submission of a "Previous" button.
     *
     * @code
     *   $form['actions']['previous'] = array(
     *     '#type' => 'submit',
     *     '#value' => t('Previous'),
     *     '#limit_validation_errors' => array(
     *       array('step1'),       // Validate $form_state['values']['step1'].
     *       array('foo', 'bar'),  // Validate $form_state['values']['foo']['bar'].
     *     ),
     *     '#submit' => array('some_submit_function'), // #submit required.
     *   );
     * @endcode
     *
     * This will require $form_state['values']['step1'] and everything within it
     * (for example, $form_state['values']['step1']['choice']) to be valid, so
     * calls to form_set_error('step1', $message) or
     * form_set_error('step1][choice', $message) will prevent the submit handlers
     * from running, and result in the error message being displayed to the user.
     * However, calls to form_set_error('step2', $message) and
     * form_set_error('step2][groupX][choiceY', $message) will be suppressed,
     * resulting in the message not being displayed to the user, and the submit
     * handlers will run despite $form_state['values']['step2'] and
     * $form_state['values']['step2']['groupX']['choiceY'] containing invalid
     * values. Errors for an invalid $form_state['values']['foo'] will be
     * suppressed, but errors flagging invalid values for
     * $form_state['values']['foo']['bar'] and everything within it will be
     * flagged and submission prevented.
     *
     * Partial form validation is implemented by suppressing errors rather than by
     * skipping the input processing and validation steps entirely, because some
     * forms have button-level submit handlers that call Drupal API functions that
     * assume that certain data exists within $form_state['values'], and while not
     * doing anything with that data that requires it to be valid, PHP errors
     * would be triggered if the input processing and validation steps were fully
     * skipped.
     * @see http://drupal.org/node/370537
     * @see http://drupal.org/node/763376
     *
     * @param $name
     *   The name of the form element. If the #parents property of your form
     *   element is array('foo', 'bar', 'baz') then you may set an error on 'foo'
     *   or 'foo][bar][baz'. Setting an error on 'foo' sets an error for every
     *   element where the #parents array starts with 'foo'.
     * @param $message
     *   The error message to present to the user.
     * @param $limit_validation_errors
     *   Internal use only. The #limit_validation_errors property of the clicked
     *   button, if it exists.
     *
     * @return
     *   Return value is for internal use only. To get a list of errors, use
     *   form_get_errors() or form_get_error().
     */
    function form_set_error($name = NULL, $message = '', $limit_validation_errors = NULL) {
      $form = &drupal_static(__FUNCTION__, array());
      $sections = &drupal_static(__FUNCTION__ . ':limit_validation_errors');
      if (isset($limit_validation_errors)) {
        $sections = $limit_validation_errors;
      }
    
      if (isset($name) && !isset($form[$name])) {
        $record = TRUE;
        if (isset($sections)) {
          // #limit_validation_errors is an array of "sections" within which user
          // input must be valid. If the element is within one of these sections,
          // the error must be recorded. Otherwise, it can be suppressed.
          // #limit_validation_errors can be an empty array, in which case all
          // errors are suppressed. For example, a "Previous" button might want its
          // submit action to be triggered even if none of the submitted values are
          // valid.
          $record = FALSE;
          foreach ($sections as $section) {
            // Exploding by '][' reconstructs the element's #parents. If the
            // reconstructed #parents begin with the same keys as the specified
            // section, then the element's values are within the part of
            // $form_state['values'] that the clicked button requires to be valid,
            // so errors for this element must be recorded.
            if (array_slice(explode('][', $name), 0, count($section)) === $section) {
              $record = TRUE;
              break;
            }
          }
        }
        if ($record) {
          $form[$name] = $message;
          if ($message) {
            drupal_set_message($message, 'error');
          }
        }
      }
    
      return $form;
    }
    
    /**
     * Clear all errors against all form elements made by form_set_error().
     */
    function form_clear_error() {
      drupal_static_reset('form_set_error');
    }
    
    /**
     * Return an associative array of all errors.
     */
    function form_get_errors() {
      $form = form_set_error();
      if (!empty($form)) {
        return $form;
      }
    }
    
    /**
     * Return the error message filed against the form with the specified name.
     */
    function form_get_error($element) {
      $form = form_set_error();
      $key = $element['#parents'][0];
      if (isset($form[$key])) {
        return $form[$key];
      }
      $key = implode('][', $element['#parents']);
      if (isset($form[$key])) {
        return $form[$key];
      }
    }
    
    /**
     * Flag an element as having an error.
     */
    function form_error(&$element, $message = '') {
      form_set_error(implode('][', $element['#parents']), $message);
    }
    
    /**
     * Walk through the structured form array, adding any required properties to
     * each element and mapping the incoming input data to the proper elements.
     * Also, execute any #process handlers attached to a specific element.
     *
     * This is one of the three primary functions that recursively iterates a form
     * array. This one does it for completing the form building process. The other
     * two are _form_validate() (invoked via drupal_validate_form() and used to
     * invoke validation logic for each element) and drupal_render() (for rendering
     * each element). Each of these three pipelines provides ample opportunity for
     * modules to customize what happens. For example, during this function's life
     * cycle, the following functions get called for each element:
     * - $element['#value_callback']: A function that implements how user input is
     *   mapped to an element's #value property. This defaults to a function named
     *   'form_type_TYPE_value' where TYPE is $element['#type'].
     * - $element['#process']: An array of functions called after user input has
     *   been mapped to the element's #value property. These functions can be used
     *   to dynamically add child elements: for example, for the 'date' element
     *   type, one of the functions in this array is form_process_date(), which adds
     *   the individual 'year', 'month', 'day', etc. child elements. These functions
     *   can also be used to set additional properties or implement special logic
     *   other than adding child elements: for example, for the 'fieldset' element
     *   type, one of the functions in this array is form_process_fieldset(), which
     *   adds the attributes and JavaScript needed to make the fieldset collapsible
     *   if the #collapsible property is set. The #process functions are called in
     *   preorder traversal, meaning they are called for the parent element first,
     *   then for the child elements.
     * - $element['#after_build']: An array of functions called after form_builder()
     *   is done with its processing of the element. These are called in postorder
     *   traversal, meaning they are called for the child elements first, then for
     *   the parent element.
     * There are similar properties containing callback functions invoked by
     * _form_validate() and drupal_render(), appropriate for those operations.
     *
     * Developers are strongly encouraged to integrate the functionality needed by
     * their form or module within one of these three pipelines, using the
     * appropriate callback property, rather than implementing their own recursive
     * traversal of a form array. This facilitates proper integration between
     * multiple modules. For example, module developers are familiar with the
     * relative order in which hook_form_alter() implementations and #process
     * functions run. A custom traversal function that affects the building of a
     * form is likely to not integrate with hook_form_alter() and #process in the
     * expected way. Also, deep recursion within PHP is both slow and memory
     * intensive, so it is best to minimize how often it's done.
     *
     * As stated above, each element's #process functions are executed after its
     * #value has been set. This enables those functions to execute conditional
     * logic based on the current value. However, all of form_builder() runs before
     * drupal_validate_form() is called, so during #process function execution, the
     * element's #value has not yet been validated, so any code that requires
     * validated values must reside within a submit handler.
     *
     * As a security measure, user input is used for an element's #value only if the
     * element exists within $form, is not disabled (as per the #disabled property),
     * and can be accessed (as per the #access property, except that forms submitted
     * using drupal_form_submit() bypass #access restrictions). When user input is
     * ignored due to #disabled and #access restrictions, the element's default
     * value is used.
     *
     * Because of the preorder traversal, where #process functions of an element run
     * before user input for its child elements is processed, and because of the
     * Form API security of user input processing with respect to #access and
     * #disabled described above, this generally means that #process functions
     * should not use an element's (unvalidated) #value to affect the #disabled or
     * #access of child elements. Use-cases where a developer may be tempted to
     * implement such conditional logic usually fall into one of two categories:
     * - Where user input from the current submission must affect the structure of a
     *   form, including properties like #access and #disabled that affect how the
     *   next submission needs to be processed, a multi-step workflow is needed.
     *   This is most commonly implemented with a submit handler setting persistent
     *   data within $form_state based on *validated* values in
     *   $form_state['values'] and setting $form_state['rebuild']. The form building
     *   functions must then be implmented to use the $form_state data to rebuild
     *   the form with the structure appropriate for the new state.
     * - Where user input must affect the rendering of the form without affecting
     *   its structure, the necessary conditional rendering logic should reside
     *   within functions that run during the rendering phase (#pre_render, #theme,
     *   #theme_wrappers, and #post_render).
     *
     * @param $form_id
     *   A unique string identifying the form for validation, submission,
     *   theming, and hook_form_alter functions.
     * @param $element
     *   An associative array containing the structure of the current element.
     * @param $form_state
     *   A keyed array containing the current state of the form. In this
     *   context, it is used to accumulate information about which button
     *   was clicked when the form was submitted, as well as the sanitized
     *   $_POST data.
     */
    function form_builder($form_id, $element, &$form_state) {
      // Initialize as unprocessed.
      $element['#processed'] = FALSE;
    
      // Use element defaults.
      if (isset($element['#type']) && empty($element['#defaults_loaded']) && ($info = element_info($element['#type']))) {
        // Overlay $info onto $element, retaining preexisting keys in $element.
        $element += $info;
        $element['#defaults_loaded'] = TRUE;
      }
      // Assign basic defaults common for all form elements.
      $element += array(
        '#required' => FALSE,
        '#attributes' => array(),
        '#title_display' => 'before',
      );
    
      // Special handling if we're on the top level form element.
      if (isset($element['#type']) && $element['#type'] == 'form') {
        if (!empty($element['#https']) && variable_get('https', FALSE) &&
            !url_is_external($element['#action'])) {
          global $base_root;
    
          // Not an external URL so ensure that it is secure.
          $element['#action'] = str_replace('http://', 'https://', $base_root) . $element['#action'];
        }
    
        // Store a complete copy of the form in form_state prior to building the form.
        $form_state['complete form'] = $element;
        // Set a flag if we have a correct form submission. This is always TRUE for
        // programmed forms coming from drupal_form_submit(), or if the form_id coming
        // from the POST data is set and matches the current form_id.
        if ($form_state['programmed'] || (!empty($form_state['input']) && (isset($form_state['input']['form_id']) && ($form_state['input']['form_id'] == $form_id)))) {
          $form_state['process_input'] = TRUE;
        }
        else {
          $form_state['process_input'] = FALSE;
        }
      }
    
      if (!isset($element['#id'])) {
        $element['#id'] = drupal_html_id('edit-' . implode('-', $element['#parents']));
      }
      // Handle input elements.
      if (!empty($element['#input'])) {
        _form_builder_handle_input_element($form_id, $element, $form_state);
      }
      // Allow for elements to expand to multiple elements, e.g., radios,
      // checkboxes and files.
      if (isset($element['#process']) && !$element['#processed']) {
        foreach ($element['#process'] as $process) {
          $element = $process($element, $form_state, $form_state['complete form']);
        }
        $element['#processed'] = TRUE;
      }
    
      // We start off assuming all form elements are in the correct order.
      $element['#sorted'] = TRUE;
    
      // Recurse through all child elements.
      $count = 0;
      foreach (element_children($element) as $key) {
        // Prior to checking properties of child elements, their default properties
        // need to be loaded.
        if (isset($element[$key]['#type']) && empty($element[$key]['#defaults_loaded']) && ($info = element_info($element[$key]['#type']))) {
          $element[$key] += $info;
          $element[$key]['#defaults_loaded'] = TRUE;
        }
    
        // Don't squash an existing tree value.
        if (!isset($element[$key]['#tree'])) {
          $element[$key]['#tree'] = $element['#tree'];
        }
    
        // Deny access to child elements if parent is denied.
        if (isset($element['#access']) && !$element['#access']) {
          $element[$key]['#access'] = FALSE;
        }
    
        // Make child elements inherit their parent's #disabled and #allow_focus
        // values unless they specify their own.
        foreach (array('#disabled', '#allow_focus') as $property) {
          if (isset($element[$property]) && !isset($element[$key][$property])) {
            $element[$key][$property] = $element[$property];
          }
        }
    
        // Don't squash existing parents value.
        if (!isset($element[$key]['#parents'])) {
          // Check to see if a tree of child elements is present. If so,
          // continue down the tree if required.
          $element[$key]['#parents'] = $element[$key]['#tree'] && $element['#tree'] ? array_merge($element['#parents'], array($key)) : array($key);
        }
        // Ensure #array_parents follows the actual form structure.
        $array_parents = isset($element['#array_parents']) ? $element['#array_parents'] : array();
        $array_parents[] = $key;
        $element[$key]['#array_parents'] = $array_parents;
    
        // Assign a decimal placeholder weight to preserve original array order.
        if (!isset($element[$key]['#weight'])) {
          $element[$key]['#weight'] = $count/1000;
        }
        else {
          // If one of the child elements has a weight then we will need to sort
          // later.
          unset($element['#sorted']);
        }
        $element[$key] = form_builder($form_id, $element[$key], $form_state);
        $count++;
      }
    
      // The #after_build flag allows any piece of a form to be altered
      // after normal input parsing has been completed.
      if (isset($element['#after_build']) && !isset($element['#after_build_done'])) {
        foreach ($element['#after_build'] as $function) {
          $element = $function($element, $form_state);
        }
        $element['#after_build_done'] = TRUE;
      }
    
      // If there is a file element, we need to flip a flag so later the
      // form encoding can be set.
      if (isset($element['#type']) && $element['#type'] == 'file') {
        $form_state['has_file_element'] = TRUE;
      }
    
      // Final tasks for the form element after form_builder() has run for all other
      // elements.
      if (isset($element['#type']) && $element['#type'] == 'form') {
        // If there is a file element, we set the form encoding.
        if (isset($form_state['has_file_element'])) {
          $element['#attributes']['enctype'] = 'multipart/form-data';
        }
    
        // If a form contains a single textfield, and the ENTER key is pressed
        // within it, Internet Explorer submits the form with no POST data
        // identifying any submit button. Other browsers submit POST data as though
        // the user clicked the first button. Therefore, to be as consistent as we
        // can be across browsers, if no 'triggering_element' has been identified
        // yet, default it to the first button.
        if (!$form_state['programmed'] && !isset($form_state['triggering_element']) && !empty($form_state['buttons'])) {
          $form_state['triggering_element'] = $form_state['buttons'][0];
        }
    
        // If the triggering element specifies "button-level" validation and submit
        // handlers to run instead of the default form-level ones, then add those to
        // the form state.
        foreach (array('validate', 'submit') as $type) {
          if (isset($form_state['triggering_element']['#' . $type])) {
            $form_state[$type . '_handlers'] = $form_state['triggering_element']['#' . $type];
          }
        }
    
        // If the triggering element executes submit handlers, then set the form
        // state key that's needed for those handlers to run.
        if (!empty($form_state['triggering_element']['#executes_submit_callback'])) {
          $form_state['submitted'] = TRUE;
        }
    
        // Special processing if the triggering element is a button.
        if (isset($form_state['triggering_element']['#button_type'])) {
          // Because there are several ways in which the triggering element could
          // have been determined (including from input variables set by JavaScript
          // or fallback behavior implemented for IE), and because buttons often
          // have their #name property not derived from their #parents property, we
          // can't assume that input processing that's happened up until here has
          // resulted in $form_state['values'][BUTTON_NAME] being set. But it's
          // common for forms to have several buttons named 'op' and switch on
          // $form_state['values']['op'] during submit handler execution.
          $form_state['values'][$form_state['triggering_element']['#name']] = $form_state['triggering_element']['#value'];
    
          // @todo Legacy support. Remove in Drupal 8.
          $form_state['clicked_button'] = $form_state['triggering_element'];
        }
    
        // Update the copy of the complete form for usage in validation handlers.
        $form_state['complete form'] = $element;
      }
      return $element;
    }
    
    /**
     * Populate the #value and #name properties of input elements so they
     * can be processed and rendered.
     */
    function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
      if (!isset($element['#name'])) {
        $name = array_shift($element['#parents']);
        $element['#name'] = $name;
        if ($element['#type'] == 'file') {
          // To make it easier to handle $_FILES in file.inc, we place all
          // file fields in the 'files' array. Also, we do not support
          // nested file names.
          $element['#name'] = 'files[' . $element['#name'] . ']';
        }
        elseif (count($element['#parents'])) {
          $element['#name'] .= '[' . implode('][', $element['#parents']) . ']';
        }
        array_unshift($element['#parents'], $name);
      }
    
      // Setting #disabled to TRUE results in user input being ignored, regardless
      // of how the element is themed or whether JavaScript is used to change the
      // control's attributes. However, it's good UI to let the user know that input
      // is not wanted for the control. HTML supports two attributes for this:
      // http://www.w3.org/TR/html401/interact/forms.html#h-17.12. If a form wants
      // to start a control off with one of these attributes for UI purposes only,
      // but still allow input to be processed if it's sumitted, it can set the
      // desired attribute in #attributes directly rather than using #disabled.
      // However, developers should think carefully about the accessibility
      // implications of doing so: if the form expects input to be enterable under
      // some condition triggered by JavaScript, how would someone who has
      // JavaScript disabled trigger that condition? Instead, developers should
      // consider whether a multi-step form would be more appropriate (#disabled can
      // be changed from step to step). If one still decides to use JavaScript to
      // affect when a control is enabled, then it is best for accessibility for the
      // control to be enabled in the HTML, and disabled by JavaScript on document
      // ready.
      if (!empty($element['#disabled'])) {
        if (!empty($element['#allow_focus'])) {
          $element['#attributes']['readonly'] = 'readonly';
        }
        else {
          $element['#attributes']['disabled'] = 'disabled';
        }
      }
    
      // With JavaScript or other easy hacking, input can be submitted even for
      // elements with #access=FALSE or #disabled=TRUE. For security, these must
      // not be processed. Forms that set #disabled=TRUE on an element do not
      // expect input for the element, and even forms submitted with
      // drupal_form_submit() must not be able to get around this. Forms that set
      // #access=FALSE on an element usually allow access for some users, so forms
      // submitted with drupal_form_submit() may bypass access restriction and be
      // treated as high-privelege users instead.
      $process_input = empty($element['#disabled']) && ($form_state['programmed'] || ($form_state['process_input'] && (!isset($element['#access']) || $element['#access'])));
    
      // Set the element's #value property.
      if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
        $value_callback = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value';
        if ($process_input) {
          // Get the input for the current element. NULL values in the input need to
          // be explicitly distinguished from missing input. (see below)
          $input = $form_state['input'];
          $input_exists = TRUE;
          foreach ($element['#parents'] as $parent) {
            if (is_array($input) && array_key_exists($parent, $input)) {
              $input = $input[$parent];
            }
            else {
              $input = NULL;
              $input_exists = FALSE;
              break;
            }
          }
          // For browser-submitted forms, the submitted values do not contain values
          // for certain elements (empty multiple select, unchecked checkbox).
          // During initial form processing, we add explicit NULL values for such
          // elements in $form_state['input']. When rebuilding the form, we can
          // distinguish elements having NULL input from elements that were not part
          // of the initially submitted form and can therefore use default values
          // for the latter, if required. Programmatically submitted forms can
          // submit explicit NULL values when calling drupal_form_submit(), so we do
          // not modify $form_state['input'] for them.
          if (!$input_exists && !$form_state['rebuild'] && !$form_state['programmed']) {
            // We leverage the internal logic of form_set_value() to change the
            // input values by passing $form_state['input'] instead of the usual
            // $form_state['values']. In effect, this adds the necessary parent keys
            // to $form_state['input'] and sets the element's input value to NULL.
            _form_set_value($form_state['input'], $element, $element['#parents'], NULL);
            $input_exists = TRUE;
          }
          // If we have input for the current element, assign it to the #value
          // property, optionally filtered through $value_callback.
          if ($input_exists) {
            if (function_exists($value_callback)) {
              $element['#value'] = $value_callback($element, $input, $form_state);
            }
            if (!isset($element['#value']) && isset($input)) {
              $element['#value'] = $input;
            }
          }
          // Mark all posted values for validation.
          if (isset($element['#value']) || (!empty($element['#required']))) {
            $element['#needs_validation'] = TRUE;
          }
        }
        // Load defaults.
        if (!isset($element['#value'])) {
          // Call #type_value without a second argument to request default_value handling.
          if (function_exists($value_callback)) {
            $element['#value'] = $value_callback($element, FALSE, $form_state);
          }
          // Final catch. If we haven't set a value yet, use the explicit default value.
          // Avoid image buttons (which come with garbage value), so we only get value
          // for the button actually clicked.
          if (!isset($element['#value']) && empty($element['#has_garbage_value'])) {
            $element['#value'] = isset($element['#default_value']) ? $element['#default_value'] : '';
          }
        }
      }
    
      // Determine which element (if any) triggered the submission of the form and
      // keep track of all the clickable buttons in the form for
      // form_state_values_clean(). Enforce the same input processing restrictions
      // as above.
      if ($process_input) {
        // Detect if the element triggered the submission via AJAX.
        if (_form_element_triggered_scripted_submission($element, $form_state)) {
          $form_state['triggering_element'] = $element;
        }
    
        // If the form was submitted by the browser rather than via AJAX, then it
        // can only have been triggered by a button, and we need to determine which
        // button within the constraints of how browsers provide this information.
        if (isset($element['#button_type'])) {
          // All buttons in the form need to be tracked for
          // form_state_values_clean() and for the form_builder() code that handles
          // a form submission containing no button information in $_POST.
          $form_state['buttons'][] = $element;
          if (_form_button_was_clicked($element, $form_state)) {
            $form_state['triggering_element'] = $element;
          }
        }
      }
    
      // Set the element's value in $form_state['values'], but only, if its key
      // does not exist yet (a #value_callback may have already populated it).
      $values = $form_state['values'];
      foreach ($element['#parents'] as $key) {
        $values = (isset($values[$key]) ? $values[$key] : NULL);
      }
      if (!isset($values)) {
        form_set_value($element, $element['#value'], $form_state);
      }
    }
    
    /**
     * Helper function to handle the convoluted logic of button click detection.
     *
     * This detects button or non-button controls that trigger a form submission via
     * AJAX or some other scriptable environment. These environments can set the
     * special input key '_triggering_element_name' to identify the triggering
     * element. If the name alone doesn't identify the element uniquely, the input
     * key '_triggering_element_value' may also be set to require a match on element
     * value. An example where this is needed is if there are several buttons all
     * named 'op', and only differing in their value.
     */
    function _form_element_triggered_scripted_submission($element, &$form_state) {
      if (!empty($form_state['input']['_triggering_element_name']) && $element['#name'] == $form_state['input']['_triggering_element_name']) {
        if (empty($form_state['input']['_triggering_element_value']) || $form_state['input']['_triggering_element_value'] == $element['#value']) {
          return TRUE;
        }
      }
      return FALSE;
    }
    
    /**
     * Helper function to handle the convoluted logic of button click detection.
     *
     * This detects button controls that trigger a form submission by being clicked
     * and having the click processed by the browser rather than being captured by
     * JavaScript. Essentially, it detects if the button's name and value are part
     * of the POST data, but with extra code to deal with the convoluted way in
     * which browsers submit data for image button clicks.
     *
     * This does not detect button clicks processed by AJAX (that is done in
     * _form_element_triggered_scripted_submission()) and it does not detect form
     * submissions from Internet Explorer in response to an ENTER key pressed in a
     * textfield (form_builder() has extra code for that).
     *
     * Because this function contains only part of the logic needed to determine
     * $form_state['triggering_element'], it should not be called from anywhere
     * other than within the Form API. Form validation and submit handlers needing
     * to know which button was clicked should get that information from
     * $form_state['triggering_element'].
     */
    function _form_button_was_clicked($element, &$form_state) {
      // First detect normal 'vanilla' button clicks. Traditionally, all
      // standard buttons on a form share the same name (usually 'op'),
      // and the specific return value is used to determine which was
      // clicked. This ONLY works as long as $form['#name'] puts the
      // value at the top level of the tree of $_POST data.
      if (isset($form_state['input'][$element['#name']]) && $form_state['input'][$element['#name']] == $element['#value']) {
        return TRUE;
      }
      // When image buttons are clicked, browsers do NOT pass the form element
      // value in $_POST. Instead they pass an integer representing the
      // coordinates of the click on the button image. This means that image
      // buttons MUST have unique $form['#name'] values, but the details of
      // their $_POST data should be ignored.
      elseif (!empty($element['#has_garbage_value']) && isset($element['#value']) && $element['#value'] !== '') {
        return TRUE;
      }
      return FALSE;
    }
    
    /**
     * Removes internal Form API elements and buttons from submitted form values.
     *
     * This function can be used when a module wants to store all submitted form
     * values, for example, by serializing them into a single database column. In
     * such cases, all internal Form API values and all form button elements should
     * not be contained, and this function allows to remove them before the module
     * proceeds to storage. Next to button elements, the following internal values
     * are removed:
     * - form_id
     * - form_token
     * - form_build_id
     * - op
     *
     * @param &$form_state
     *   A keyed array containing the current state of the form, including
     *   submitted form values; altered by reference.
     */
    function form_state_values_clean(&$form_state) {
      // Remove internal Form API values.
      unset($form_state['values']['form_id'], $form_state['values']['form_token'], $form_state['values']['form_build_id'], $form_state['values']['op']);
    
      // Remove button values.
      // form_builder() collects all button elements in a form. We remove the button
      // value separately for each button element.
      foreach ($form_state['buttons'] as $button) {
        // Remove this button's value from the submitted form values by finding
        // the value corresponding to this button.
        // We iterate over the #parents of this button and move a reference to
        // each parent in $form_state['values']. For example, if #parents is:
        //   array('foo', 'bar', 'baz')
        // then the corresponding $form_state['values'] part will look like this:
        // array(
        //   'foo' => array(
        //     'bar' => array(
        //       'baz' => 'button_value',
        //     ),
        //   ),
        // )
        // We start by (re)moving 'baz' to $last_parent, so we are able unset it
        // at the end of the iteration. Initially, $values will contain a
        // reference to $form_state['values'], but in the iteration we move the
        // reference to $form_state['values']['foo'], and finally to
        // $form_state['values']['foo']['bar'], which is the level where we can
        // unset 'baz' (that is stored in $last_parent).
        $parents = $button['#parents'];
        $values = &$form_state['values'];
        $last_parent = array_pop($parents);
        foreach ($parents as $parent) {
          $values = &$values[$parent];
        }
        unset($values[$last_parent]);
      }
    }
    
    /**
     * Helper function to determine the value for an image button form element.
     *
     * @param $form
     *   The form element whose value is being populated.
     * @param $input
     *   The incoming input to populate the form element. If this is FALSE,
     *   the element's default value should be returned.
     * @param $form_state
     *   A keyed array containing the current state of the form.
     * @return
     *   The data that will appear in the $form_state['values'] collection
     *   for this element. Return nothing to use the default.
     */
    function form_type_image_button_value($form, $input, $form_state) {
      if ($input !== FALSE) {
        if (!empty($input)) {
          // If we're dealing with Mozilla or Opera, we're lucky. It will
          // return a proper value, and we can get on with things.
          return $form['#return_value'];
        }
        else {
          // Unfortunately, in IE we never get back a proper value for THIS
          // form element. Instead, we get back two split values: one for the
          // X and one for the Y coordinates on which the user clicked the
          // button. We'll find this element in the #post data, and search
          // in the same spot for its name, with '_x'.
          $input = $form_state['input'];
          foreach (explode('[', $form['#name']) as $element_name) {
            // chop off the ] that may exist.
            if (substr($element_name, -1) == ']') {
              $element_name = substr($element_name, 0, -1);
            }
    
            if (!isset($input[$element_name])) {
              if (isset($input[$element_name . '_x'])) {
                return $form['#return_value'];
              }
              return NULL;
            }
            $input = $input[$element_name];
          }
          return $form['#return_value'];
        }
      }
    }
    
    /**
     * Helper function to determine the value for a checkbox form element.
     *
     * @param $form
     *   The form element whose value is being populated.
    *  @param $input
     *   The incoming input to populate the form element. If this is FALSE,
     *   the element's default value should be returned.
     * @return
     *   The data that will appear in the $element_state['values'] collection
     *   for this element. Return nothing to use the default.
     */
    function form_type_checkbox_value($element, $input = FALSE) {
      if ($input !== FALSE) {
        // Successful (checked) checkboxes are present with a value (possibly '0').
        // http://www.w3.org/TR/html401/interact/forms.html#successful-controls
        // For an unchecked checkbox, we return integer 0, so we can explicitly
        // test for a value different than string '0'.
        return isset($input) ? $element['#return_value'] : 0;
      }
    }
    
    /**
     * Helper function to determine the value for a checkboxes form element.
     *
     * @param $element
     *   The form element whose value is being populated.
     * @param $input
     *   The incoming input to populate the form element. If this is FALSE,
     *   the element's default value should be returned.
     * @return
     *   The data that will appear in the $element_state['values'] collection
     *   for this element. Return nothing to use the default.
     */
    function form_type_checkboxes_value($element, $input = FALSE) {
      if ($input === FALSE) {
        $value = array();
        $element += array('#default_value' => array());
        foreach ($element['#default_value'] as $key) {
          $value[$key] = $key;
        }
        return $value;
      }
      elseif (is_array($input)) {
        // Programmatic form submissions use NULL to indicate that a checkbox
        // should be unchecked; see drupal_form_submit(). We therefore remove all
        // NULL elements from the array before constructing the return value, to
        // simulate the behavior of web browsers (which do not send unchecked
        // checkboxes to the server at all). This will not affect non-programmatic
        // form submissions, since a checkbox can never legitimately be NULL.
        foreach ($input as $key => $value) {
          if (is_null($value)) {
            unset($input[$key]);
          }
        }
        return drupal_map_assoc($input);
      }
      else {
        return array();
      }
    }
    
    /**
     * Helper function to determine the value for a password_confirm form
     * element.
     *
     * @param $element
     *   The form element whose value is being populated.
     * @param $input
     *   The incoming input to populate the form element. If this is FALSE,
     *   the element's default value should be returned.
     * @return
     *   The data that will appear in the $element_state['values'] collection
     *   for this element. Return nothing to use the default.
     */
    function form_type_password_confirm_value($element, $input = FALSE) {
      if ($input === FALSE) {
        $element += array('#default_value' => array());
        return $element['#default_value'] + array('pass1' => '', 'pass2' => '');
      }
    }
    
    /**
     * Helper function to determine the value for a select form element.
     *
     * @param $element
     *   The form element whose value is being populated.
     * @param $input
     *   The incoming input to populate the form element. If this is FALSE,
     *   the element's default value should be returned.
     * @return
     *   The data that will appear in the $element_state['values'] collection
     *   for this element. Return nothing to use the default.
     */
    function form_type_select_value($element, $input = FALSE) {
      if ($input !== FALSE) {
        if (isset($element['#multiple']) && $element['#multiple']) {
          // If an enabled multi-select submits NULL, it means all items are
          // unselected. A disabled multi-select always submits NULL, and the
          // default value should be used.
          if (empty($element['#disabled'])) {
            return (is_array($input)) ? drupal_map_assoc($input) : array();
          }
          else {
            return (isset($element['#default_value']) && is_array($element['#default_value'])) ? $element['#default_value'] : array();
          }
        }
        else {
          return $input;
        }
      }
    }
    
    /**
     * Helper function to determine the value for a textfield form element.
     *
     * @param $element
     *   The form element whose value is being populated.
     * @param $input
     *   The incoming input to populate the form element. If this is FALSE,
     *   the element's default value should be returned.
     * @return
     *   The data that will appear in the $element_state['values'] collection
     *   for this element. Return nothing to use the default.
     */
    function form_type_textfield_value($element, $input = FALSE) {
      if ($input !== FALSE && $input !== NULL) {
        // Equate $input to the form value to ensure it's marked for
        // validation.
        return str_replace(array("\r", "\n"), '', $input);
      }
    }
    
    /**
     * Helper function to determine the value for form's token value.
     *
     * @param $element
     *   The form element whose value is being populated.
     * @param $input
     *   The incoming input to populate the form element. If this is FALSE,
     *   the element's default value should be returned.
     * @return
     *   The data that will appear in the $element_state['values'] collection
     *   for this element. Return nothing to use the default.
     */
    function form_type_token_value($element, $input = FALSE) {
      if ($input !== FALSE) {
        return (string) $input;
      }
    }
    
    /**
     * Change submitted form values during form validation.
     *
     * Use this function to change the submitted value of a form element in a form
     * validation function, so that the changed value persists in $form_state
     * through to the submission handlers.
     *
     * Note that form validation functions are specified in the '#validate'
     * component of the form array (the value of $form['#validate'] is an array of
     * validation function names). If the form does not originate in your module,
     * you can implement hook_form_FORM_ID_alter() to add a validation function
     * to $form['#validate'].
     *
     * @param $element
     *   The form element that should have its value updated; in most cases you can
     *   just pass in the element from the $form array, although the only component
     *   that is actually used is '#parents'. If constructing yourself, set
     *   $element['#parents'] to be an array giving the path through the form
     *   array's keys to the element whose value you want to update. For instance,
     *   if you want to update the value of $form['elem1']['elem2'], which should be
     *   stored in $form_state['values']['elem1']['elem2'], you would set
     *   $element['#parents'] = array('elem1','elem2').
     * @param $value
     *   The new value for the form element.
     * @param $form_state
     *   Form state array where the value change should be recorded.
     */
    function form_set_value($element, $value, &$form_state) {
      _form_set_value($form_state['values'], $element, $element['#parents'], $value);
    }
    
    /**
     * Helper function for form_set_value() and _form_builder_handle_input_element().
     *
     * We iterate over $parents and create nested arrays for them in $form_values if
     * needed. Then we insert the value into the last parent key.
     */
    function _form_set_value(&$form_values, $element, $parents, $value) {
      $parent = array_shift($parents);
      if (empty($parents)) {
        $form_values[$parent] = $value;
      }
      else {
        if (!isset($form_values[$parent])) {
          $form_values[$parent] = array();
        }
        _form_set_value($form_values[$parent], $element, $parents, $value);
      }
    }
    
    /**
     * Allows PHP array processing of multiple select options with the same value.
     *
     * Used for form select elements which need to validate HTML option groups
     * and multiple options which may return the same value. Associative PHP arrays
     * cannot handle these structures, since they share a common key.
     *
     * @param $array
     *   The form options array to process.
     *
     * @return
     *   An array with all hierarchical elements flattened to a single array.
     */
    function form_options_flatten($array) {
      // Always reset static var when first entering the recursion.
      drupal_static_reset('_form_options_flatten');
      return _form_options_flatten($array);
    }
    
    /**
     * Helper function for form_options_flatten().
     *
     * Iterates over arrays which may share common values and produces a flat
     * array that has removed duplicate keys. Also handles cases where objects
     * are passed as array values.
     */
    function _form_options_flatten($array) {
      $return = &drupal_static(__FUNCTION__);
    
      foreach ($array as $key => $value) {
        if (is_object($value)) {
          _form_options_flatten($value->option);
        }
        elseif (is_array($value)) {
          _form_options_flatten($value);
        }
        else {
          $return[$key] = 1;
        }
      }
    
      return $return;
    }
    
    /**
     * Returns HTML for a select form element.
     *
     * It is possible to group options together; to do this, change the format of
     * $options to an associative array in which the keys are group labels, and the
     * values are associative arrays in the normal $options format.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #title, #value, #options, #description, #extra,
     *     #multiple, #required, #name, #attributes, #size.
     *
     * @ingroup themeable
     */
    function theme_select($variables) {
      $element = $variables['element'];
      $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';
      _form_set_class($element, array('form-select'));
      $multiple = $element['#multiple'];
      return '<select name="' . $element['#name'] . '' . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) . ' id="' . $element['#id'] . '" ' . $size . '>' . form_select_options($element) . '</select>';
    }
    
    /**
     * Converts a select form element's options array into an HTML.
     *
     * @param $element
     *   An associative array containing the properties of the element.
     * @param $choices
     *   Mixed: Either an associative array of items to list as choices, or an
     *   object with an 'option' member that is an associative array. This
     *   parameter is only used internally and should not be passed.
     * @return
     *   An HTML string of options for the select form element.
     */
    function form_select_options($element, $choices = NULL) {
      if (!isset($choices)) {
        $choices = $element['#options'];
      }
      // array_key_exists() accommodates the rare event where $element['#value'] is NULL.
      // isset() fails in this situation.
      $value_valid = isset($element['#value']) || array_key_exists('#value', $element);
      $value_is_array = is_array($element['#value']);
      $options = '';
      foreach ($choices as $key => $choice) {
        if (is_array($choice)) {
          $options .= '<optgroup label="' . $key . '">';
          $options .= form_select_options($element, $choice);
          $options .= '</optgroup>';
        }
        elseif (is_object($choice)) {
          $options .= form_select_options($element, $choice->option);
        }
        else {
          $key = (string) $key;
          if ($value_valid && (!$value_is_array && (string) $element['#value'] === $key || ($value_is_array && in_array($key, $element['#value'])))) {
            $selected = ' selected="selected"';
          }
          else {
            $selected = '';
          }
          $options .= '<option value="' . check_plain($key) . '"' . $selected . '>' . check_plain($choice) . '</option>';
        }
      }
      return $options;
    }
    
    /**
     * Traverses a select element's #option array looking for any values
     * that hold the given key. Returns an array of indexes that match.
     *
     * This function is useful if you need to modify the options that are
     * already in a form element; for example, to remove choices which are
     * not valid because of additional filters imposed by another module.
     * One example might be altering the choices in a taxonomy selector.
     * To correctly handle the case of a multiple hierarchy taxonomy,
     * #options arrays can now hold an array of objects, instead of a
     * direct mapping of keys to labels, so that multiple choices in the
     * selector can have the same key (and label). This makes it difficult
     * to manipulate directly, which is why this helper function exists.
     *
     * This function does not support optgroups (when the elements of the
     * #options array are themselves arrays), and will return FALSE if
     * arrays are found. The caller must either flatten/restore or
     * manually do their manipulations in this case, since returning the
     * index is not sufficient, and supporting this would make the
     * "helper" too complicated and cumbersome to be of any help.
     *
     * As usual with functions that can return array() or FALSE, do not
     * forget to use === and !== if needed.
     *
     * @param $element
     *   The select element to search.
     * @param $key
     *   The key to look for.
     * @return
     *   An array of indexes that match the given $key. Array will be
     *   empty if no elements were found. FALSE if optgroups were found.
     */
    function form_get_options($element, $key) {
      $keys = array();
      foreach ($element['#options'] as $index => $choice) {
        if (is_array($choice)) {
          return FALSE;
        }
        elseif (is_object($choice)) {
          if (isset($choice->option[$key])) {
            $keys[] = $index;
          }
        }
        elseif ($index == $key) {
          $keys[] = $index;
        }
      }
      return $keys;
    }
    
    /**
     * Returns HTML for a fieldset form element and its children.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #attributes, #children, #collapsed, #collapsible,
     *     #description, #id, #title, #value.
     *
     * @ingroup themeable
     */
    function theme_fieldset($variables) {
      $element = $variables['element'];
    
      $output = '<fieldset' . drupal_attributes($element['#attributes']) . '>';
      if (!empty($element['#title'])) {
        // Always wrap fieldset legends in a SPAN for CSS positioning.
        $output .= '<legend><span class="fieldset-legend">' . $element['#title'] . '</span></legend>';
      }
      $output .= '<div class="fieldset-wrapper">';
      if (!empty($element['#description'])) {
        $output .= '<div class="fieldset-description">' . $element['#description'] . '</div>';
      }
      $output .= $element['#children'];
      if (isset($element['#value'])) {
        $output .= $element['#value'];
      }
      $output .= '</div>';
      $output .= "</fieldset>\n";
      return $output;
    }
    
    /**
     * Returns HTML for a radio button form element.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #required, #return_value, #value, #attributes, #title,
     *     #description
     *
     * @ingroup themeable
     */
    function theme_radio($variables) {
      $element = $variables['element'];
      $element['#attributes']['type'] = 'radio';
      $element['#attributes']['name'] = $element['#name'];
      $element['#attributes']['id'] = $element['#id'];
      $element['#attributes']['value'] = $element['#return_value'];
      if (check_plain($element['#value']) == $element['#return_value']) {
        $element['#attributes']['checked'] = 'checked';
      }
      _form_set_class($element, array('form-radio'));
    
      return '<input' . drupal_attributes($element['#attributes']) . ' />';
    }
    
    /**
     * Returns HTML for a set of radio button form elements.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #title, #value, #options, #description, #required,
     *     #attributes, #children.
     *
     * @ingroup themeable
     */
    function theme_radios($variables) {
      $element = $variables['element'];
      $attributes = array();
      if (!empty($element['#id'])) {
        $attributes['id'] = $element['#id'];
      }
      $attributes['class'] = 'form-radios';
      if (!empty($element['#attributes']['class'])) {
        $attributes['class'] .= ' ' . implode(' ', $element['#attributes']['class']);
      }
      return '<div' . drupal_attributes($attributes) . '>' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
    }
    
    /**
     * Expand a password_confirm field into two text boxes.
     */
    function form_process_password_confirm($element) {
      $element['pass1'] =  array(
        '#type' => 'password',
        '#title' => t('Password'),
        '#value' => empty($element['#value']) ? NULL : $element['#value']['pass1'],
        '#required' => $element['#required'],
        '#attributes' => array('class' => array('password-field')),
      );
      $element['pass2'] =  array(
        '#type' => 'password',
        '#title' => t('Confirm password'),
        '#value' => empty($element['#value']) ? NULL : $element['#value']['pass2'],
        '#required' => $element['#required'],
        '#attributes' => array('class' => array('password-confirm')),
      );
      $element['#element_validate'] = array('password_confirm_validate');
      $element['#tree'] = TRUE;
    
      if (isset($element['#size'])) {
        $element['pass1']['#size'] = $element['pass2']['#size'] = $element['#size'];
      }
    
      return $element;
    }
    
    /**
     * Validate password_confirm element.
     */
    function password_confirm_validate($element, &$element_state) {
      $pass1 = trim($element['pass1']['#value']);
      $pass2 = trim($element['pass2']['#value']);
      if (!empty($pass1) || !empty($pass2)) {
        if (strcmp($pass1, $pass2)) {
          form_error($element, t('The specified passwords do not match.'));
        }
      }
      elseif ($element['#required'] && !empty($element_state['input'])) {
        form_error($element, t('Password field is required.'));
      }
    
      // Password field must be converted from a two-element array into a single
      // string regardless of validation results.
      form_set_value($element['pass1'], NULL, $element_state);
      form_set_value($element['pass2'], NULL, $element_state);
      form_set_value($element, $pass1, $element_state);
    
      return $element;
    
    }
    
    /**
     * Returns HTML for a date selection form element.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #title, #value, #options, #description, #required,
     *     #attributes.
     *
     * @ingroup themeable
     */
    function theme_date($variables) {
      $element = $variables['element'];
      return '<div class="container-inline">' . drupal_render_children($element) . '</div>';
    }
    
    /**
     * Roll out a single date element.
     */
    function form_process_date($element) {
      // Default to current date
      if (empty($element['#value'])) {
        $element['#value'] = array('day' => format_date(REQUEST_TIME, 'custom', 'j'),
                                'month' => format_date(REQUEST_TIME, 'custom', 'n'),
                                'year' => format_date(REQUEST_TIME, 'custom', 'Y'));
      }
    
      $element['#tree'] = TRUE;
    
      // Determine the order of day, month, year in the site's chosen date format.
      $format = variable_get('date_format_short', 'm/d/Y - H:i');
      $sort = array();
      $sort['day'] = max(strpos($format, 'd'), strpos($format, 'j'));
      $sort['month'] = max(strpos($format, 'm'), strpos($format, 'M'));
      $sort['year'] = strpos($format, 'Y');
      asort($sort);
      $order = array_keys($sort);
    
      // Output multi-selector for date.
      foreach ($order as $type) {
        switch ($type) {
          case 'day':
            $options = drupal_map_assoc(range(1, 31));
            break;
          case 'month':
            $options = drupal_map_assoc(range(1, 12), 'map_month');
            break;
          case 'year':
            $options = drupal_map_assoc(range(1900, 2050));
            break;
        }
    
        $element[$type] = array(
          '#type' => 'select',
          '#value' => $element['#value'][$type],
          '#attributes' => $element['#attributes'],
          '#options' => $options,
        );
      }
    
      return $element;
    }
    
    /**
     * Validates the date type to stop dates like February 30, 2006.
     */
    function date_validate($form) {
      if (!checkdate($form['#value']['month'], $form['#value']['day'], $form['#value']['year'])) {
        form_error($form, t('The specified date is invalid.'));
      }
    }
    
    /**
     * Helper function for usage with drupal_map_assoc to display month names.
     */
    function map_month($month) {
      $months = &drupal_static(__FUNCTION__, array(
        1 => 'Jan',
        2 => 'Feb',
        3 => 'Mar',
        4 => 'Apr',
        5 => 'May',
        6 => 'Jun',
        7 => 'Jul',
        8 => 'Aug',
        9 => 'Sep',
        10 => 'Oct',
        11 => 'Nov',
        12 => 'Dec',
      ));
      return t($months[$month]);
    }
    
    /**
     * If no default value is set for weight select boxes, use 0.
     */
    function weight_value(&$form) {
      if (isset($form['#default_value'])) {
        $form['#value'] = $form['#default_value'];
      }
      else {
        $form['#value'] = 0;
      }
    }
    
    /**
     * Roll out a single radios element to a list of radios,
     * using the options array as index.
     */
    function form_process_radios($element) {
      if (count($element['#options']) > 0) {
        foreach ($element['#options'] as $key => $choice) {
          if (!isset($element[$key])) {
            // Generate the parents as the autogenerator does, so we will have a
            // unique id for each radio button.
            $parents_for_id = array_merge($element['#parents'], array($key));
            $element[$key] = array(
              '#type' => 'radio',
              '#title' => $choice,
              '#return_value' => check_plain($key),
              '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : NULL,
              '#attributes' => $element['#attributes'],
              '#parents' => $element['#parents'],
              '#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)),
              '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
            );
          }
        }
      }
      return $element;
    }
    
    /**
     * Returns HTML for a checkbox form element.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #title, #value, #return_value, #description, #required,
     *     #attributes.
     *
     * @ingroup themeable
     */
    function theme_checkbox($variables) {
      $element = $variables['element'];
      $t = get_t();
      $element['#attributes']['type'] = 'checkbox';
      $element['#attributes']['name'] = $element['#name'];
      $element['#attributes']['id'] = $element['#id'];
      $element['#attributes']['value'] = $element['#return_value'];
      // Unchecked checkbox has #value of integer 0.
      if ($element['#value'] !== 0 && $element['#value'] == $element['#return_value']) {
        $element['#attributes']['checked'] = 'checked';
      }
      _form_set_class($element, array('form-checkbox'));
    
      return '<input' . drupal_attributes($element['#attributes']) . ' />';
    }
    
    /**
     * Returns HTML for a set of checkbox form elements.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #children, #attributes.
     *
     * @ingroup themeable
     */
    function theme_checkboxes($variables) {
      $element = $variables['element'];
      $attributes = array();
      if (!empty($element['#id'])) {
        $attributes['id'] = $element['#id'];
      }
      $attributes['class'] = 'form-checkboxes';
      if (!empty($element['#attributes']['class'])) {
        $attributes['class'] .= ' ' . implode(' ', $element['#attributes']['class']);
      }
      return '<div' . drupal_attributes($attributes) . '>' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
    }
    
    /**
     * Add form_element theming to an element if title or description is set.
     *
     * This is used as a pre render function for checkboxes and radios.
     */
    function form_pre_render_conditional_form_element($element) {
      // Set the element's title attribute to show #title as a tooltip, if needed.
      if (isset($element['#title']) && $element['#title_display'] == 'attribute') {
        $element['#attributes']['title'] = $element['#title'];
        if (!empty($element['#required'])) {
          // Append an indication that this field is required.
          $element['#attributes']['title'] .= ' (' . $t('Required') . ')';
        }
      }
    
      if (isset($element['#title']) || isset($element['#description'])) {
        $element['#theme_wrappers'][] = 'form_element';
      }
      return $element;
    }
    
    function form_process_checkboxes($element) {
      $value = is_array($element['#value']) ? $element['#value'] : array();
      $element['#tree'] = TRUE;
      if (count($element['#options']) > 0) {
        if (!isset($element['#default_value']) || $element['#default_value'] == 0) {
          $element['#default_value'] = array();
        }
        foreach ($element['#options'] as $key => $choice) {
          if (!isset($element[$key])) {
            $element[$key] = array(
              '#type' => 'checkbox',
              '#processed' => TRUE,
              '#title' => $choice,
              '#return_value' => $key,
              '#default_value' => isset($value[$key]) ? $key : NULL,
              '#attributes' => $element['#attributes'],
              '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
            );
          }
        }
      }
      return $element;
    }
    
    /**
     * Processes a form actions container element.
     *
     * @param $element
     *   An associative array containing the properties and children of the
     *   form actions container.
     * @param $form_state
     *   The $form_state array for the form this element belongs to.
     *
     * @return
     *   The processed element.
     */
    function form_process_actions($element, &$form_state) {
      $element['#attributes']['class'][] = 'form-actions';
      return $element;
    }
    
    /**
     * Processes a container element.
     *
     * @param $element
     *   An associative array containing the properties and children of the
     *   container.
     * @param $form_state
     *   The $form_state array for the form this element belongs to.
     * @return
     *   The processed element.
     */
    function form_process_container($element, &$form_state) {
      // Generate the ID of the element if it's not explicitly given.
      if (!isset($element['#id'])) {
        $element['#id'] = drupal_html_id(implode('-', $element['#parents']) . '-wrapper');
      }
      return $element;
    }
    
    /**
     * Returns HTML for a container for grouped form items.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #id, #attributes, #children.
     *
     * @ingroup themeable
     */
    function theme_container($variables) {
      $element = $variables['element'];
      if (!isset($element['#attributes']['id'])) {
        $element['#attributes']['id'] = $element['#id'];
      }
      // Force the 'form-wrapper' class.
      $element['#attributes']['class'][] = 'form-wrapper';
      return '<div' . drupal_attributes($element['#attributes']) . '>' . $element['#children'] . '</div>';
    }
    
    /**
     * Returns HTML for a table with radio buttons or checkboxes.
     *
     * An example of per-row options:
     * @code
     * $options = array();
     * $options[0]['title'] = "A red row"
     * $options[0]['#attributes'] = array ('class' => array('red-row'));
     * $options[1]['title'] = "A blue row"
     * $options[1]['#attributes'] = array ('class' => array('blue-row'));
     *
     * $form['myselector'] = array (
     * '#type' => 'tableselect',
     * '#title' => 'My Selector'
     * '#options' => $options,
     * );
     * @endcode
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties and children of
     *     the tableselect element. Properties used: #header, #options, #empty,
     *     and #js_select. The #options property is an array of selection options;
     *     each array element of #options is an array of properties. These
     *     properties can include #attributes, which is added to the
     *     table row's HTML attributes; see theme_table().
     *
     * @ingroup themeable
     */
    function theme_tableselect($variables) {
      $element = $variables['element'];
      $rows = array();
      $header = $element['#header'];
      if (!empty($element['#options'])) {
        // Generate a table row for each selectable item in #options.
        foreach ($element['#options'] as $key => $value) {
          $row = array();
    
          $row['data'] = array();
          if (isset($value['#attributes'])) {
            $row += $value['#attributes'];
          }
          // Render the checkbox / radio element.
          $row['data'][] = drupal_render($element[$key]);
    
          // As theme_table only maps header and row columns by order, create the
          // correct order by iterating over the header fields.
          foreach ($element['#header'] as $fieldname => $title) {
            $row['data'][] = $element['#options'][$key][$fieldname];
          }
          $rows[] = $row;
        }
        // Add an empty header or a "Select all" checkbox to provide room for the
        // checkboxes/radios in the first table column.
        if ($element['#js_select']) {
          drupal_add_js('misc/tableselect.js');
          array_unshift($header, array('class' => array('select-all')));
        }
      }
      return theme('table', array('header' => $header, 'rows' => $rows, 'empty' => $element['#empty'], 'attributes' => $element['#attributes']));
    }
    
    /**
     * Create the correct amount of checkbox or radio elements to populate the table.
     *
     * @param $element
     *   An associative array containing the properties and children of the
     *   tableselect element.
     * @return
     *   The processed element.
     */
    function form_process_tableselect($element) {
    
      if ($element['#multiple']) {
        $value = is_array($element['#value']) ? $element['#value'] : array();
      }
      else {
        // Advanced selection behaviour make no sense for radios.
        $element['#js_select'] = FALSE;
      }
    
      $element['#tree'] = TRUE;
    
      if (count($element['#options']) > 0) {
        if (!isset($element['#default_value']) || $element['#default_value'] === 0) {
          $element['#default_value'] = array();
        }
    
        // Sort the options by their #weight if they have a #weight.
        uasort($element['#options'], 'element_sort');
        // Create a checkbox or radio for each item in #options in such a way that
        // the value of the tableselect element behaves as if it had been of type
        // checkboxes or radios.
        foreach ($element['#options'] as $key => $choice) {
          // Do not overwrite manually created children.
          if (!isset($element[$key])) {
            if ($element['#multiple']) {
              $element[$key] = array(
                '#type' => 'checkbox',
                '#title' => '',
                '#return_value' => $key,
                '#default_value' => isset($value[$key]) ? $key : NULL,
                '#attributes' => $element['#attributes'],
              );
            }
            else {
              // Generate the parents as the autogenerator does, so we will have a
              // unique id for each radio button.
              $parents_for_id = array_merge($element['#parents'], array($key));
              $element[$key] = array(
                '#type' => 'radio',
                '#title' => '',
                '#return_value' => $key,
                '#default_value' => ($element['#default_value'] == $key) ? $key : NULL,
                '#attributes' => $element['#attributes'],
                '#parents' => $element['#parents'],
                '#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)),
                '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
              );
            }
          }
        }
      }
      else {
        $element['#value'] = array();
      }
      return $element;
    }
    
    /**
     * Adds fieldsets to the specified group or adds group members to this
     * fieldset.
     *
     * @param &$element
     *   An associative array containing the properties and children of the
     *   fieldset. Note that $element must be taken by reference here, so processed
     *   child elements are taken over into $form_state.
     * @param $form_state
     *   The $form_state array for the form this fieldset belongs to.
     * @return
     *   The processed element.
     */
    function form_process_fieldset(&$element, &$form_state) {
      $parents = implode('][', $element['#parents']);
    
      // Each fieldset forms a new group. The #type 'vertical_tabs' basically only
      // injects a new fieldset.
      $form_state['groups'][$parents]['#group_exists'] = TRUE;
      $element['#groups'] = &$form_state['groups'];
    
      // Process vertical tabs group member fieldsets.
      if (isset($element['#group'])) {
        // Add this fieldset to the defined group (by reference).
        $group = $element['#group'];
        $form_state['groups'][$group][] = &$element;
      }
    
      // Contains form element summary functionalities.
      $element['#attached']['js']['misc/form.js'] = array('weight' => JS_LIBRARY + 1);
    
      // The .form-wrapper class is required for #states to treat fieldsets like
      // containers.
      if (!isset($element['#attributes']['class'])) {
        $element['#attributes']['class'] = array();
      }
      $element['#attributes']['class'][] = 'form-wrapper';
    
      // Collapsible fieldsets
      if (!empty($element['#collapsible'])) {
        $element['#attached']['js'][] = 'misc/collapse.js';
        $element['#attributes']['class'][] = 'collapsible';
        if (!empty($element['#collapsed'])) {
          $element['#attributes']['class'][] = 'collapsed';
        }
      }
      $element['#attributes']['id'] = $element['#id'];
    
      return $element;
    }
    
    /**
     * Adds members of this group as actual elements for rendering.
     *
     * @param $element
     *   An associative array containing the properties and children of the
     *   fieldset.
     *
     * @return
     *   The modified element with all group members.
     */
    function form_pre_render_fieldset($element) {
      // Inject group member elements belonging to this group.
      $parents = implode('][', $element['#parents']);
      $children = element_children($element['#groups'][$parents]);
      if (!empty($children)) {
        foreach ($children as $key) {
          // Break references and indicate that the element should be rendered as
          // group member.
          $child = (array) $element['#groups'][$parents][$key];
          $child['#group_fieldset'] = TRUE;
          // Inject the element as new child element.
          $element[] = $child;
    
          $sort = TRUE;
        }
        // Re-sort the element's children if we injected group member elements.
        if (isset($sort)) {
          $element['#sorted'] = FALSE;
        }
      }
    
      if (isset($element['#group'])) {
        $group = $element['#group'];
        // If this element belongs to a group, but the group-holding element does
        // not exist, we need to render it (at its original location).
        if (!isset($element['#groups'][$group]['#group_exists'])) {
          // Intentionally empty to clarify the flow; we simply return $element.
        }
        // If we injected this element into the group, then we want to render it.
        elseif (!empty($element['#group_fieldset'])) {
          // Intentionally empty to clarify the flow; we simply return $element.
        }
        // Otherwise, this element belongs to a group and the group exists, so we do
        // not render it.
        elseif (element_children($element['#groups'][$group])) {
          $element['#printed'] = TRUE;
        }
      }
    
      return $element;
    }
    
    /**
     * Creates a group formatted as vertical tabs.
     *
     * @param $element
     *   An associative array containing the properties and children of the
     *   fieldset.
     * @param $form_state
     *   The $form_state array for the form this vertical tab widget belongs to.
     * @return
     *   The processed element.
     */
    function form_process_vertical_tabs($element, &$form_state) {
      // Inject a new fieldset as child, so that form_process_fieldset() processes
      // this fieldset like any other fieldset.
      $element['group'] = array(
        '#type' => 'fieldset',
        '#theme_wrappers' => array(),
        '#parents' => $element['#parents'],
      );
    
      // The JavaScript stores the currently selected tab in this hidden
      // field so that the active tab can be restored the next time the
      // form is rendered, e.g. on preview pages or when form validation
      // fails.
      $name = implode('__', $element['#parents']);
      if (isset($form_state['values'][$name . '__active_tab'])) {
        $element['#default_tab'] = $form_state['values'][$name . '__active_tab'];
      }
      $element[$name . '__active_tab'] = array(
        '#type' => 'hidden',
        '#default_value' => $element['#default_tab'],
        '#attributes' => array('class' => array('vertical-tabs-active-tab')),
      );
    
      return $element;
    }
    
    /**
     * Returns HTML for an element's children fieldsets as vertical tabs.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties and children of the
     *     fieldset. Properties used: #children.
     *
     * @ingroup themeable
     */
    function theme_vertical_tabs($variables) {
      $element = $variables['element'];
      // Add required JavaScript and Stylesheet.
      drupal_add_library('system', 'vertical-tabs');
    
      $output = '<h2 class="element-invisible">' . t('Vertical Tabs') . '</h2>';
      $output .= '<div class="vertical-tabs-panes">' . $element['#children'] . '</div>';
      return $output;
    }
    
    /**
     * Returns HTML for a submit button form element.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #attributes, #button_type, #name, #value.
     *
     * @ingroup themeable
     */
    function theme_submit($variables) {
      $element = $variables['element'];
      return theme('button', $element);
    }
    
    /**
     * Returns HTML for a button form element.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #attributes, #button_type, #name, #value.
     *
     * @ingroup themeable
     */
    function theme_button($variables) {
      $element = $variables['element'];
      $element['#attributes']['type'] = 'submit';
      if (!empty($element['#name'])) {
        $element['#attributes']['name'] = $element['#name'];
      }
      $element['#attributes']['id'] = $element['#id'];
      $element['#attributes']['value'] = $element['#value'];
      $element['#attributes']['class'][] = 'form-' . $element['#button_type'];
      if (!empty($element['#attributes']['disabled'])) {
        $element['#attributes']['class'][] = 'form-button-disabled';
      }
    
      return '<input' . drupal_attributes($element['#attributes']) . ' />';
    }
    
    /**
     * Returns HTML for an image button form element.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #attributes, #button_type, #name, #value, #title, #src.
     *
     * @ingroup themeable
     */
    function theme_image_button($variables) {
      $element = $variables['element'];
      $element['#attributes']['type'] = 'submit';
      $element['#attributes']['name'] = $element['#name'];
      if (!empty($element['#value'])) {
        $element['#attributes']['value'] = $element['#value'];
      }
      $element['#attributes']['id'] = $element['#id'];
      $element['#attributes']['src'] = file_create_url($element['#src']);
      if (!empty($element['#title'])) {
        $element['#attributes']['alt'] = $element['#title'];
        $element['#attributes']['title'] = $element['#title'];
      }
    
      $element['#attributes']['class'][] = 'form-' . $element['#button_type'];
      if (!empty($element['#attributes']['disabled'])) {
        $element['#attributes']['class'][] = 'form-button-disabled';
      }
    
      return '<input' . drupal_attributes($element['#attributes']) . ' />';
    }
    
    /**
     * Returns HTML for a hidden form element.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #name, #value, #attributes.
     *
     * @ingroup themeable
     */
    function theme_hidden($variables) {
      $element = $variables['element'];
      $element['#attributes']['type'] = 'hidden';
      $element['#attributes']['name'] = $element['#name'];
      $element['#attributes']['id'] = $element['#id'];
      $element['#attributes']['value'] = $element['#value'];
      return '<input' . drupal_attributes($element['#attributes']) . " />\n";
    }
    
    /**
     * Returns HTML for a textfield form element.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #title, #value, #description, #size, #maxlength,
     *     #required, #attributes, #autocomplete_path.
     *
     * @ingroup themeable
     */
    function theme_textfield($variables) {
      $element = $variables['element'];
      $element['#attributes']['type'] = 'text';
      $element['#attributes']['name'] = $element['#name'];
      $element['#attributes']['id'] = $element['#id'];
      $element['#attributes']['value'] = $element['#value'];
      if (!empty($element['#size'])) {
        $element['#attributes']['size'] = $element['#size'];
      }
      if (!empty($element['#maxlength'])) {
        $element['#attributes']['maxlength'] = $element['#maxlength'];
      }
      _form_set_class($element, array('form-text'));
    
      $extra = '';
      if ($element['#autocomplete_path'] && drupal_valid_path($element['#autocomplete_path'])) {
        drupal_add_js('misc/autocomplete.js');
        $element['#attributes']['class'][] = 'form-autocomplete';
    
        $attributes = array();
        $attributes['type'] = 'hidden';
        $attributes['id'] = $element['#id'] . '-autocomplete';
        $attributes['value'] = url($element['#autocomplete_path'], array('absolute' => TRUE));
        $attributes['disabled'] = 'disabled';
        $attributes['class'][] = 'autocomplete';
        $extra = '<input' . drupal_attributes($attributes) . ' />';
      }
    
      $output = '<input' . drupal_attributes($element['#attributes']) . ' />';
    
      return $output . $extra;
    }
    
    /**
     * Returns HTML for a form.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #action, #method, #attributes, #children
     *
     * @ingroup themeable
     */
    function theme_form($variables) {
      $element = $variables['element'];
      if (!empty($element['#action'])) {
        $element['#attributes']['action'] = drupal_strip_dangerous_protocols($element['#action']);
      }
      $element['#attributes']['method'] = $element['#method'];
      if (empty($element['#attributes']['accept-charset'])) {
        $element['#attributes']['accept-charset'] = "UTF-8";
      }
      $element['#attributes']['id'] = $element['#id'];
      // Anonymous DIV to satisfy XHTML compliance.
      return '<form' . drupal_attributes($element['#attributes']) . '><div>' . $element['#children'] . '</div></form>';
    }
    
    /**
     * Returns HTML for a textarea form element.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #title, #value, #description, #rows, #cols, #required,
     *     #attributes
     *
     * @ingroup themeable
     */
    function theme_textarea($variables) {
      $element = $variables['element'];
      $element['#attributes']['name'] = $element['#name'];
      $element['#attributes']['id'] = $element['#id'];
      $element['#attributes']['value'] = $element['#value'];
      $element['#attributes']['cols'] = $element['#cols'];
      $element['#attributes']['rows'] = $element['#rows'];
      _form_set_class($element, array('form-textarea'));
    
      $wrapper_attributes = array(
        'class' => array('form-textarea-wrapper'),
      );
    
      // Add resizable behavior.
      if (!empty($element['#resizable'])) {
        drupal_add_js('misc/textarea.js');
        $wrapper_attributes['class'][] = 'resizable';
      }
    
      $output = '<div' . drupal_attributes($wrapper_attributes) . '>';
      $output .= '<textarea' . drupal_attributes($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>';
      $output .= '</div>';
      return $output;
    }
    
    /**
     * Returns HTML for a password form element.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #title, #value, #description, #size, #maxlength,
     *     #required, #attributes.
     *
     * @ingroup themeable
     */
    function theme_password($variables) {
      $element = $variables['element'];
      $element['#attributes']['type'] = 'password';
      $element['#attributes']['name'] = $element['#name'];
      $element['#attributes']['id'] = $element['#id'];
      $element['#attributes']['value'] = $element['#value'];
      if (!empty($element['#size'])) {
        $element['#attributes']['size'] = $element['#size'];
      }
      if (!empty($element['#maxlength'])) {
        $element['#attributes']['maxlength'] = $element['#maxlength'];
      }
      _form_set_class($element, array('form-text'));
    
      return '<input' . drupal_attributes($element['#attributes']) . ' />';
    }
    
    /**
     * Expand weight elements into selects.
     */
    function form_process_weight($element) {
      for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) {
        $weights[$n] = $n;
      }
      $element['#options'] = $weights;
      $element['#type'] = 'select';
      $element['#is_weight'] = TRUE;
      $element += element_info('select');
      return $element;
    }
    
    /**
     * Returns HTML for a file upload form element.
     *
     * For assistance with handling the uploaded file correctly, see the API
     * provided by file.inc.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #title, #name, #size, #description, #required,
     *     #attributes.
     *
     * @ingroup themeable
     */
    function theme_file($variables) {
      $element = $variables['element'];
      $element['#attributes']['type'] = 'file';
      $element['#attributes']['name'] = $element['#name'];
      $element['#attributes']['id'] = $element['#id'];
      if (!empty($element['#size'])) {
        $element['#attributes']['size'] = $element['#size'];
      }
      _form_set_class($element, array('form-file'));
    
      return '<input' . drupal_attributes($element['#attributes']) . ' />';
    }
    
    /**
     * Returns HTML for a form element.
     *
     * Each form element is wrapped in a DIV container having the following CSS
     * classes:
     * - form-item: Generic for all form elements.
     * - form-type-#type: The internal element #type.
     * - form-item-#name: The internal form element #name (usually derived from the
     *   $form structure and set via form_builder()).
     * - form-disabled: Only set if the form element is #disabled.
     *
     * In addition to the element itself, the DIV contains a label for the element
     * based on the optional #title_display property, and an optional #description.
     *
     * The optional #title_display property can have these values:
     * - before: The label is output before the element. This is the default.
     *   The label includes the #title and the required marker, if #required.
     * - after: The label is output after the element. For example, this is used
     *   for radio and checkbox #type elements as set in system_element_info().
     *   If the #title is empty but the field is #required, the label will
     *   contain only the required marker.
     * - invisible: Labels are critical for screen readers to enable them to
     *   properly navigate through forms but can be visually distracting. This
     *   property hides the label for everyone except screen readers.
     * - attribute: Set the title attribute on the element to create a tooltip
     *   but output no label element. This is supported only for checkboxes
     *   and radios in form_pre_render_conditional_form_element(). It is used
     *   where a visual label is not needed, such as a table of checkboxes where
     *   the row and column provide the context. The tooltip will include the
     *   title and required marker.
     *
     * If the #title property is not set, then the label and any required marker
     * will not be output, regardless of the #title_display or #required values.
     * This can be useful in cases such as the password_confirm element, which
     * creates children elements that have their own labels and required markers,
     * but the parent element should have neither. Use this carefully because a
     * field without an associated label can cause accessibility challenges.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #title, #title_display, #description, #id, #required,
     *     #children, #type, #name.
     *
     * @ingroup themeable
     */
    function theme_form_element($variables) {
      $element = $variables['element'];
      // This is also used in the installer, pre-database setup.
      $t = get_t();
    
      // Add element #id for #type 'item'.
      if (isset($element['#markup']) && !empty($element['#id'])) {
        $attributes['id'] = $element['#id'];
      }
      // Add element's #type and #name as class to aid with JS/CSS selectors.
      $attributes['class'] = array('form-item');
      if (!empty($element['#type'])) {
        $attributes['class'][] = 'form-type-' . strtr($element['#type'], '_', '-');
      }
      if (!empty($element['#name'])) {
        $attributes['class'][] = 'form-item-' . strtr($element['#name'], array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
      }
      // Add a class for disabled elements to facilitate cross-browser styling.
      if (!empty($element['#attributes']['disabled'])) {
        $attributes['class'][] = 'form-disabled';
      }
      $output = '<div' . drupal_attributes($attributes) . '>' . "\n";
    
      // If #title is not set, we don't display any label or required marker.
      if (!isset($element['#title'])) {
        $element['#title_display'] = 'none';
      }
      $prefix = isset($element['#field_prefix']) ? '<span class="field-prefix">' . $element['#field_prefix'] . '</span> ' : '';
      $suffix = isset($element['#field_suffix']) ? ' <span class="field-suffix">' . $element['#field_suffix'] . '</span>' : '';
    
      switch ($element['#title_display']) {
        case 'before':
          $output .= ' ' . theme('form_element_label', $variables);
          $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
          break;
    
        case 'invisible':
        case 'after':
          $output .= ' ' . $prefix . $element['#children'] . $suffix;
          $output .= ' ' . theme('form_element_label', $variables) . "\n";
          break;
    
        case 'none':
        case 'attribute':
          // Output no label and no required marker, only the children.
          $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
          break;
      }
    
      if (!empty($element['#description'])) {
        $output .= ' <div class="description">' . $element['#description'] . "</div>\n";
      }
    
      $output .= "</div>\n";
    
      return $output;
    }
    
    /**
     * Returns HTML for a marker for required form elements.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *
     * @ingroup themeable
     */
    function theme_form_required_marker($variables) {
      // This is also used in the installer, pre-database setup.
      $t = get_t();
      $attributes = array(
        'class' => 'form-required',
        'title' => $t('This field is required.'),
      );
      return '<span' . drupal_attributes($attributes) . '>*</span>';
    }
    
    /**
     * Returns HTML for a form element label and required marker.
     *
     * Form element labels include the #title and a #required marker. The label is
     * associated with the element itself by the element #id. Labels may appear
     * before or after elements, depending on theme_form_element() and #title_display.
     *
     * This function will not be called for elements with no labels, depending on
     * #title_display. For elements that have an empty #title and are not required,
     * this function will output no label (''). For required elements that have an
     * empty #title, this will output the required marker alone within the label.
     * The label will use the #id to associate the marker with the field that is
     * required. That is especially important for screenreader users to know
     * which field is required.
     *
     * @param $variables
     *   An associative array containing:
     *   - element: An associative array containing the properties of the element.
     *     Properties used: #required, #title, #id, #value, #description.
     *
     * @ingroup themeable
     */
    function theme_form_element_label($variables) {
      $element = $variables['element'];
      // This is also used in the installer, pre-database setup.
      $t = get_t();
    
      // If title and required marker are both empty, output no label.
      if (empty($element['#title']) && empty($element['#required'])) {
        return '';
      }
    
      // If the element is required, a required marker is appended to the label.
      $required = !empty($element['#required']) ? theme('form_required_marker', array('element' => $element)) : '';
    
      $title = filter_xss_admin($element['#title']);
    
      $attributes = array();
      // Style the label as class option to display inline with the element.
      if ($element['#title_display'] == 'after') {
        $attributes['class'] = 'option';
      }
      // Show label only to screen readers to avoid disruption in visual flows.
      elseif ($element['#title_display'] == 'invisible') {
        $attributes['class'] = 'element-invisible';
      }
    
      if (!empty($element['#id'])) {
        $attributes['for'] = $element['#id'];
      }
    
      // The leading whitespace helps visually separate fields from inline labels.
      return ' <label' . drupal_attributes($attributes) . '>' . $t('!title !required', array('!title' => $title, '!required' => $required)) . "</label>\n";
    }
    
    /**
     * Sets a form element's class attribute.
     *
     * Adds 'required' and 'error' classes as needed.
     *
     * @param &$element
     *   The form element.
     * @param $name
     *   Array of new class names to be added.
     */
    function _form_set_class(&$element, $class = array()) {
      if (!empty($class)) {
        if (!isset($element['#attributes']['class'])) {
          $element['#attributes']['class'] = array();
        }
        $element['#attributes']['class'] = array_merge($element['#attributes']['class'], $class);
      }
      if ($element['#required']) {
        $element['#attributes']['class'][] = 'required';
      }
      if (form_get_error($element)) {
        $element['#attributes']['class'][] = 'error';
      }
    }
    
    /**
     * @} End of "defgroup form_api".
     */
    
    /**
     * @defgroup batch Batch operations
     * @{
     * Functions allowing forms processing to be spread out over several page
     * requests, thus ensuring that the processing does not get interrupted
     * because of a PHP timeout, while allowing the user to receive feedback
     * on the progress of the ongoing operations.
     *
     * The API is primarily designed to integrate nicely with the Form API
     * workflow, but can also be used by non-Form API scripts (like update.php)
     * or even simple page callbacks (which should probably be used sparingly).
     *
     * Example:
     * @code
     * $batch = array(
     *   'title' => t('Exporting'),
     *   'operations' => array(
     *     array('my_function_1', array($account->uid, 'story')),
     *     array('my_function_2', array()),
     *   ),
     *   'finished' => 'my_finished_callback',
     *   'file' => 'path_to_file_containing_myfunctions',
     * );
     * batch_set($batch);
     * // only needed if not inside a form _submit handler :
     * batch_process();
     * @endcode
     *
     * Note: if the batch 'title', 'init_message', 'progress_message', or
     * 'error_message' could contain any user input, it is the responsibility of
     * the code calling batch_set() to sanitize them first with a function like
     * check_plain() or filter_xss().
     *
     * Sample batch operations:
     * @code
     * // Simple and artificial: load a node of a given type for a given user
     * function my_function_1($uid, $type, &$context) {
     *   // The $context array gathers batch context information about the execution (read),
     *   // as well as 'return values' for the current operation (write)
     *   // The following keys are provided :
     *   // 'results' (read / write): The array of results gathered so far by
     *   //   the batch processing, for the current operation to append its own.
     *   // 'message' (write): A text message displayed in the progress page.
     *   // The following keys allow for multi-step operations :
     *   // 'sandbox' (read / write): An array that can be freely used to
     *   //   store persistent data between iterations. It is recommended to
     *   //   use this instead of $_SESSION, which is unsafe if the user
     *   //   continues browsing in a separate window while the batch is processing.
     *   // 'finished' (write): A float number between 0 and 1 informing
     *   //   the processing engine of the completion level for the operation.
     *   //   1 (or no value explicitly set) means the operation is finished
     *   //   and the batch processing can continue to the next operation.
     *
     *   $node = node_load(array('uid' => $uid, 'type' => $type));
     *   $context['results'][] = $node->nid . ' : ' . $node->title;
     *   $context['message'] = $node->title;
     * }
     *
     * // More advanced example: multi-step operation - load all nodes, five by five
     * function my_function_2(&$context) {
     *   if (empty($context['sandbox'])) {
     *     $context['sandbox']['progress'] = 0;
     *     $context['sandbox']['current_node'] = 0;
     *     $context['sandbox']['max'] = db_query('SELECT COUNT(DISTINCT nid) FROM {node}')->fetchField();
     *   }
     *   $limit = 5;
     *   $result = db_select('node')
     *     ->fields('node', array('nid'))
     *     ->condition('nid', $context['sandbox']['current_node'], '>')
     *     ->orderBy('nid')
     *     ->range(0, $limit)
     *     ->execute();
     *   foreach ($result as $row) {
     *     $node = node_load($row->nid, NULL, TRUE);
     *     $context['results'][] = $node->nid . ' : ' . $node->title;
     *     $context['sandbox']['progress']++;
     *     $context['sandbox']['current_node'] = $node->nid;
     *     $context['message'] = $node->title;
     *   }
     *   if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
     *     $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
     *   }
     * }
     * @endcode
     *
     * Sample 'finished' callback:
     * @code
     * function batch_test_finished($success, $results, $operations) {
     *   if ($success) {
     *     $message = format_plural(count($results), 'One post processed.', '@count posts processed.');
     *   }
     *   else {
     *     $message = t('Finished with an error.');
     *   }
     *   drupal_set_message($message);
     *   // Providing data for the redirected page is done through $_SESSION.
     *   foreach ($results as $result) {
     *     $items[] = t('Loaded node %title.', array('%title' => $result));
     *   }
     *   $_SESSION['my_batch_results'] = $items;
     * }
     * @endcode
     */
    
    /**
     * Opens a new batch.
     *
     * @param $batch
     *   An array defining the batch. The following keys can be used -- only
     *   'operations' is required, and batch_init() provides default values for
     *   the messages.
     *   - 'operations': Array of function calls to be performed.
     *     Example:
     *     @code
     *     array(
     *       array('my_function_1', array($arg1)),
     *       array('my_function_2', array($arg2_1, $arg2_2)),
     *     )
     *     @endcode
     *   - 'title': Title for the progress page. Only safe strings should be passed.
     *     Defaults to t('Processing').
     *   - 'init_message': Message displayed while the processing is initialized.
     *     Defaults to t('Initializing.').
     *   - 'progress_message': Message displayed while processing the batch.
     *     Available placeholders are @current, @remaining, @total, @percentage,
     *     @estimate and @elapsed. Defaults to t('Completed @current of @total.').
     *   - 'error_message': Message displayed if an error occurred while processing
     *     the batch. Defaults to t('An error has occurred.').
     *   - 'finished': Name of a function to be executed after the batch has
     *     completed. This should be used to perform any result massaging that
     *     may be needed, and possibly save data in $_SESSION for display after
     *     final page redirection.
     *   - 'file': Path to the file containing the definitions of the
     *     'operations' and 'finished' functions, for instance if they don't
     *     reside in the main .module file. The path should be relative to
     *     base_path(), and thus should be built using drupal_get_path().
     *   - 'css': Array of paths to CSS files to be used on the progress page.
     *   - 'url_options': options passed to url() when constructing redirect
     *     URLs for the batch.
     *
     * Operations are added as new batch sets. Batch sets are used to ensure
     * clean code independence, ensuring that several batches submitted by
     * different parts of the code (core / contrib modules) can be processed
     * correctly while not interfering or having to cope with each other. Each
     * batch set gets to specify his own UI messages, operates on its own set
     * of operations and results, and triggers its own 'finished' callback.
     * Batch sets are processed sequentially, with the progress bar starting
     * fresh for every new set.
     */
    function batch_set($batch_definition) {
      if ($batch_definition) {
        $batch =& batch_get();
    
        // Initialize the batch if needed.
        if (empty($batch)) {
          $batch = array(
            'sets' => array(),
            'has_form_submits' => FALSE,
          );
        }
    
        // Base and default properties for the batch set.
        // Use get_t() to allow batches at install time.
        $t = get_t();
        $init = array(
          'sandbox' => array(),
          'results' => array(),
          'success' => FALSE,
          'start' => 0,
          'elapsed' => 0,
        );
        $defaults = array(
          'title' => $t('Processing'),
          'init_message' => $t('Initializing.'),
          'progress_message' => $t('Completed @current of @total.'),
          'error_message' => $t('An error has occurred.'),
          'css' => array(),
        );
        $batch_set = $init + $batch_definition + $defaults;
    
        // Tweak init_message to avoid the bottom of the page flickering down after
        // init phase.
        $batch_set['init_message'] .= '<br/>&nbsp;';
    
        // The non-concurrent workflow of batch execution allows us to save
        // numberOfItems() queries by handling our own counter.
        $batch_set['total'] = count($batch_set['operations']);
        $batch_set['count'] = $batch_set['total'];
    
        // Add the set to the batch.
        if (empty($batch['id'])) {
          // The batch is not running yet. Simply add the new set.
          $batch['sets'][] = $batch_set;
        }
        else {
          // The set is being added while the batch is running. Insert the new set
          // right after the current one to ensure execution order, and store its
          // operations in a queue.
          $index = $batch['current_set'] + 1;
          $slice1 = array_slice($batch['sets'], 0, $index);
          $slice2 = array_slice($batch['sets'], $index);
          $batch['sets'] = array_merge($slice1, array($batch_set), $slice2);
          _batch_populate_queue($batch, $index);
        }
      }
    }
    
    /**
     * Processes the batch.
     *
     * Unless the batch has been marked with 'progressive' = FALSE, the function
     * issues a drupal_goto and thus ends page execution.
     *
     * This function is generally not needed in form submit handlers;
     * Form API takes care of batches that were set during form submission.
     *
     * @param $redirect
     *   (optional) Path to redirect to when the batch has finished processing.
     * @param $url
     *   (optional - should only be used for separate scripts like update.php)
     *   URL of the batch processing page.
     * @param $redirect_callback
     *   (optional) Specify a function to be called to redirect to the progressive
     *   processing page. By default drupal_goto() will be used to redirect to a
     *   page which will do the progressive page. Specifying another function will
     *   allow the progressive processing to be processed differently.
     */
    function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = 'drupal_goto') {
      $batch =& batch_get();
    
      drupal_theme_initialize();
    
      if (isset($batch)) {
        // Add process information
        $process_info = array(
          'current_set' => 0,
          'progressive' => TRUE,
          'url' => $url,
          'url_options' => array(),
          'source_url' => $_GET['q'],
          'redirect' => $redirect,
          'theme' => $GLOBALS['theme_key'],
          'redirect_callback' => $redirect_callback,
        );
        $batch += $process_info;
    
        // The batch is now completely built. Allow other modules to make changes
        // to the batch so that it is easier to reuse batch processes in other
        // environments.
        drupal_alter('batch', $batch);
    
        // Assign an arbitrary id: don't rely on a serial column in the 'batch'
        // table, since non-progressive batches skip database storage completely.
        $batch['id'] = db_next_id();
    
        // Move operations to a job queue. Non-progressive batches will use a
        // memory-based queue.
        foreach ($batch['sets'] as $key => $batch_set) {
          _batch_populate_queue($batch, $key);
        }
    
        // Initiate processing.
        if ($batch['progressive']) {
          // Now that we have a batch id, we can generate the redirection link in
          // the generic error message.
          $t = get_t();
          $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')))));
    
          // Clear the way for the drupal_goto() redirection to the batch processing
          // page, by saving and unsetting the 'destination', if there is any.
          if (isset($_GET['destination'])) {
            $batch['destination'] = $_GET['destination'];
            unset($_GET['destination']);
          }
    
          // Store the batch.
          db_insert('batch')
            ->fields(array(
              'bid' => $batch['id'],
              'timestamp' => REQUEST_TIME,
              'token' => drupal_get_token($batch['id']),
              'batch' => serialize($batch),
            ))
            ->execute();
    
          // Set the batch number in the session to guarantee that it will stay alive.
          $_SESSION['batches'][$batch['id']] = TRUE;
    
          // Redirect for processing.
          $function = $batch['redirect_callback'];
          if (function_exists($function)) {
            $function($batch['url'], array('query' => array('op' => 'start', 'id' => $batch['id'])));
          }
        }
        else {
          // Non-progressive execution: bypass the whole progressbar workflow
          // and execute the batch in one pass.
          require_once DRUPAL_ROOT . '/includes/batch.inc';
          _batch_process();
        }
      }
    }
    
    /**
     * Retrieves the current batch.
     */
    function &batch_get() {
      // Not drupal_static(), because Batch API operates at a lower level than most
      // use-cases for resetting static variables, and we specifically do not want a
      // global drupal_static_reset() resetting the batch information. Functions
      // that are part of the Batch API and need to reset the batch information may
      // call batch_get() and manipulate the result by reference. Functions that are
      // not part of the Batch API can also do this, but shouldn't.
      static $batch = array();
      return $batch;
    }
    
    /**
     * Populates a job queue with the operations of a batch set.
     *
     * Depending on whether the batch is progressive or not, the BatchQueue or
     * BatchMemoryQueue handler classes will be used.
     *
     * @param $batch
     *   The batch array.
     * @param $set_id
     *   The id of the set to process.
     * @return
     *   The name and class of the queue are added by reference to the batch set.
     */
    function _batch_populate_queue(&$batch, $set_id) {
      $batch_set = &$batch['sets'][$set_id];
    
      if (isset($batch_set['operations'])) {
        $batch_set += array(
          'queue' => array(
            'name' => 'drupal_batch:' . $batch['id'] . ':' . $set_id,
            'class' => $batch['progressive'] ? 'BatchQueue' : 'BatchMemoryQueue',
          ),
        );
    
        $queue = _batch_queue($batch_set);
        $queue->createQueue();
        foreach ($batch_set['operations'] as $operation) {
          $queue->createItem($operation);
        }
    
        unset($batch_set['operations']);
      }
    }
    
    /**
     * Returns a queue object for a batch set.
     *
     * @param $batch_set
     *   The batch set.
     * @return
     *   The queue object.
     */
    function _batch_queue($batch_set) {
      static $queues;
    
      // The class autoloader is not available when running update.php, so make
      // sure the files are manually included.
      if (is_null($queues)) {
        $queues = array();
        require_once DRUPAL_ROOT . '/modules/system/system.queue.inc';
        require_once DRUPAL_ROOT . '/includes/batch.queue.inc';
      }
    
      if (isset($batch_set['queue'])) {
        $name = $batch_set['queue']['name'];
        $class = $batch_set['queue']['class'];
    
        if (!isset($queues[$class][$name])) {
          $queues[$class][$name] = new $class($name);
        }
        return $queues[$class][$name];
      }
    }
    
    /**
     * @} End of "defgroup batch".
     */