[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/ctools/includes/ -> form.inc (source)

   1  <?php
   2  // $Id: form.inc,v 1.11.2.3 2010/08/20 22:16:57 merlinofchaos Exp $
   3  
   4  /**
   5   * @file
   6   * CTools' replacements for Drupal's form functions.
   7   *
   8   * Primarily, ctools_build_form is meant to be a replacement for drupal_get_form().
   9   *
  10   * Instead of sending arguments through to the form builder, a form state array
  11   * is passed through. This form_state can contain any arguments needed, and it can
  12   * also be used to return data to the calling function.
  13   *
  14   * This can allow cleaner separation of the form from the storage mechanism.
  15   */
  16  
  17  /**
  18   * Build a form, similar to drupal_get_form(). However, arguments
  19   * to the form builder are not sent through. Instead, the $form_state
  20   * can be given all the necessary data to fully utilize the form.
  21   */
  22  function ctools_build_form($form_id, &$form_state) {
  23    // Ensure that we have some defaults.
  24  
  25    // These are defaults only; if already set they will not be overridden.
  26    $form_state += array('storage' => NULL, 'submitted' => FALSE, 'input' => $_POST, 'method' => 'post');
  27  
  28    $args = isset($form_state['args']) ? $form_state['args'] : array();
  29    $cacheable = FALSE;
  30  
  31    if (isset($_SESSION['batch_form_state'])) {
  32      // We've been redirected here after a batch processing : the form has
  33      // already been processed, so we grab the post-process $form_state value
  34      // and move on to form display. See _batch_finished() function.
  35      $form_state = $_SESSION['batch_form_state'];
  36      unset($_SESSION['batch_form_state']);
  37    }
  38    else {
  39      // If the incoming $form_state['input'] contains a form_build_id, we'll check the
  40      // cache for a copy of the form in question. If it's there, we don't
  41      // have to rebuild the form to proceed. In addition, if there is stored
  42      // form_state data from a previous step, we'll retrieve it so it can
  43      // be passed on to the form processing code.
  44      if (isset($form_state['input']['form_id']) && $form_state['input']['form_id'] == $form_id && !empty($form_state['input']['form_build_id'])) {
  45        $form_build_id = $form_state['input']['form_build_id'];
  46        $form = form_get_cache($form_build_id, $form_state);
  47        if (!empty($form['#no_cache']) || empty($form)) {
  48          unset($form);
  49        }
  50      }
  51  
  52      // If the previous bit of code didn't result in a populated $form
  53      // object, we're hitting the form for the first time and we need
  54      // to build it from scratch.
  55      if (!isset($form)) {
  56        $form_state['post'] = $form_state['input'];
  57        // This allows us to do some interesting form embedding stuff without
  58        // messing up the form IDs too badly.
  59        if (isset($form_state['wrapper callback']) && function_exists($form_state['wrapper callback'])) {
  60          // If there is a wrapper callback, we do not use drupal_retrieve_form.
  61          // Instead, we call $form_id builder function directly. This means the args
  62          // are *different* for forms used this way, which may not be ideal but
  63          // is necessary right now.
  64          $form = array();
  65          $form_state['wrapper callback']($form, $form_state);
  66          if (function_exists($form_id)) {
  67            $form_id($form, $form_state);
  68          }
  69        }
  70        else {
  71          // Use a copy of the function's arguments for manipulation
  72          $args_temp = $args;
  73          array_unshift($args_temp, 'placeholder');
  74          $args_temp[0] = &$form_state; // replaces the placeholder.
  75          array_unshift($args_temp, $form_id);
  76  
  77          $form = call_user_func_array('drupal_retrieve_form', $args_temp);
  78        }
  79  
  80        $form_build_id = 'form-' . md5(mt_rand());
  81        $form['#build_id'] = $form_build_id;
  82  
  83  
  84        if ($form_state['method'] == 'get' && !isset($form['#method'])) {
  85          $form['#method'] = 'get';
  86        }
  87  
  88        drupal_prepare_form($form_id, $form, $form_state);
  89        // Store a copy of the unprocessed form for caching and indicate that it
  90        // is cacheable if #cache will be set.
  91        $original_form = $form;
  92        $cacheable = TRUE;
  93        unset($form_state['post']);
  94      }
  95      $form['#post'] = $form_state['input'];
  96  
  97      // Now that we know we have a form, we'll process it (validating,
  98      // submitting, and handling the results returned by its submission
  99      // handlers. Submit handlers accumulate data in the form_state by
 100      // altering the $form_state variable, which is passed into them by
 101      // reference.
 102      ctools_process_form($form_id, $form, $form_state);
 103      // If we were told not to redirect, but not told to re-render, return
 104      // here.
 105      if (!empty($form_state['executed']) && empty($form_state['rerender'])) {
 106        return;
 107      }
 108      if ($cacheable && !empty($form['#cache']) && empty($form['#no_cache'])) {
 109        // Caching is done past drupal_process_form so #process callbacks can
 110        // set #cache. By not sending the form state, we avoid storing
 111        // $form_state['storage'].
 112        form_set_cache($form_build_id, $original_form, NULL);
 113      }
 114    }
 115  
 116    // Most simple, single-step forms will be finished by this point --
 117    // drupal_process_form() usually redirects to another page (or to
 118    // a 'fresh' copy of the form) once processing is complete. If one
 119    // of the form's handlers has set $form_state['redirect'] to FALSE,
 120    // the form will simply be re-rendered with the values still in its
 121    // fields.
 122    //
 123    // If $form_state['storage'] or $form_state['rebuild'] have been
 124    // set by any submit or validate handlers, however, we know that
 125    // we're in a complex multi-part process of some sort and the form's
 126    // workflow is NOT complete. We need to construct a fresh copy of
 127    // the form, passing in the latest $form_state in addition to any
 128    // other variables passed into drupal_get_form().
 129    //
 130    // If this function is being used to perform an '#ahah' callback
 131    // to rebuild some or all of a ctools wizard form step, be sure that
 132    // $form_state['wrapper callback'], $form_state['form_info'],
 133    // $form_state['step'], $form_state['no_redirect'], and $form_state['rebuild']
 134    // are properly set.
 135  
 136    if (!empty($form_state['rebuild']) || !empty($form_state['storage'])) {
 137      $form = ctools_rebuild_form($form_id, $form_state, $args, $form_build_id);
 138    }
 139  
 140    // If whoever is calling this wants the $form array (so that it can render it
 141    // another way, for example) then return it.
 142    if (!empty($form_state['want form'])) {
 143      return $form;
 144    }
 145  
 146    // Do not render certain items if requested not to:
 147    if (!empty($form_state['drop tokens'])) {
 148      unset($form['#id']);
 149      unset($form['#build_id']);
 150      unset($form['#token']);
 151      unset($form['form_token']);
 152    }
 153    // If we haven't redirected to a new location by now, we want to
 154    // render whatever form array is currently in hand.
 155    // Do not render certain items if requested not to:
 156    if (!empty($form_state['drop tokens'])) {
 157      unset($form['form_id']);
 158      unset($form['form_build_id']);
 159      unset($form['form_token']);
 160    }
 161    return drupal_render_form($form_id, $form);
 162  }
 163  
 164  /**
 165   * ctools' replacement of drupal_rebuild_form.
 166   *
 167   * This change merely respects a form's wishes not to be cached.
 168   */
 169  function ctools_rebuild_form($form_id, &$form_state, $args, $form_build_id = NULL) {
 170    // Remove the first argument. This is $form_id.when called from
 171    // drupal_get_form and the original $form_state when called from some AHAH
 172    // callback. Neither is needed. After that, put in the current state.
 173    array_unshift($args, 'placeholder');
 174    $args[0] = &$form_state; // Replaces placeholder.
 175    // And the form_id.
 176    array_unshift($args, $form_id);
 177  
 178    if (isset($form_state['wrapper callback']) && function_exists($form_state['wrapper callback'])) {
 179      // If there is a wrapper callback, we do not use drupal_retrieve_form.
 180      // Instead, we call $form_id builder function directly. This means the args
 181      // are *different* for forms used this way, which may not be ideal but
 182      // is necessary right now.
 183      $form = array();
 184      $form_state['wrapper callback']($form, $form_state);
 185      if (function_exists($form_id)) {
 186        $form_id($form, $form_state);
 187      }
 188    }
 189    else {
 190      $form = call_user_func_array('drupal_retrieve_form', $args);
 191    }
 192  
 193    if (!isset($form_build_id)) {
 194      // We need a new build_id for the new version of the form.
 195      $form_build_id = 'form-' . md5(mt_rand());
 196    }
 197    $form['#build_id'] = $form_build_id;
 198    // Flush form ID element cache because we may be rebuilding
 199    // a form in a way that Drupal's FAPI isn't used to which
 200    // causes unnecessary form ID changes.
 201    form_clean_id(NULL, TRUE);
 202    drupal_prepare_form($form_id, $form, $form_state);
 203  
 204    if (empty($form['#no_cache'])) {
 205      // Now, we cache the form structure so it can be retrieved later for
 206      // validation. If $form_state['storage'] is populated, we'll also cache
 207      // it so that it can be used to resume complex multi-step processes.
 208      form_set_cache($form_build_id, $form, $form_state);
 209    }
 210  
 211    // Originally this called drupal_process_form, but all that happens there
 212    // is form_builder and then submission; and the rebuilt form is not
 213    // allowed to submit. Therefore, just do this:
 214    $form['#post'] = $form_state['input'];
 215    $form = form_builder($form_id, $form, $form_state);
 216  
 217    return $form;
 218  }
 219  
 220  /**
 221   * ctools' replacement for drupal_process_form that accepts commands
 222   * not to redirect, as well as forcing processing of 'get' method forms.
 223   */
 224  function ctools_process_form($form_id, &$form, &$form_state) {
 225    // submitting, and handling the results returned by its submission
 226    // handlers. Submit handlers accumulate data in the form_state by
 227    // altering the $form_state variable, which is passed into them by
 228    // reference.
 229    $form_state['values'] = array();
 230  
 231    // With $_GET, these forms are always submitted.
 232    if ($form_state['method'] == 'get') {
 233      if (!isset($form['#post']['form_build_id'])) {
 234        $form['#post']['form_build_id'] = $form['#build_id'];
 235      }
 236      if (!isset($form['#post']['form_id'])) {
 237        $form['#post']['form_id'] = $form_id;
 238      }
 239      if (!isset($form['#post']['form_token']) && isset($form['#token'])) {
 240        $form['#post']['form_token'] = drupal_get_token($form['#token']);
 241      }
 242    }
 243  
 244    $form = form_builder($form_id, $form, $form_state);
 245    // Only process the form if it is programmed or the form_id coming
 246    // from the POST data is set and matches the current form_id.
 247  
 248    if ((!empty($form['#programmed'])) || (!empty($form['#post']) && (isset($form['#post']['form_id']) && ($form['#post']['form_id'] == $form_id)))) {
 249      ctools_validate_form($form_id, $form, $form_state);
 250  
 251      // form_clean_id() maintains a cache of element IDs it has seen,
 252      // so it can prevent duplicates. We want to be sure we reset that
 253      // cache when a form is processed, so scenerios that result in
 254      // the form being built behind the scenes and again for the
 255      // browser don't increment all the element IDs needlessly.
 256      form_clean_id(NULL, TRUE);
 257  
 258      if ((!empty($form_state['submitted'])) && !form_get_errors() && empty($form_state['rebuild'])) {
 259        $form_state['redirect'] = NULL;
 260        form_execute_handlers('submit', $form, $form_state);
 261  
 262        // We'll clear out the cached copies of the form and its stored data
 263        // here, as we've finished with them. The in-memory copies are still
 264        // here, though.
 265        if (variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED && !empty($form_state['values']['form_build_id'])) {
 266          cache_clear_all('form_' . $form_state['values']['form_build_id'], 'cache_form');
 267          cache_clear_all('storage_' . $form_state['values']['form_build_id'], 'cache_form');
 268        }
 269  
 270        // If batches were set in the submit handlers, we process them now,
 271        // possibly ending execution. We make sure we do not react to the batch
 272        // that is already being processed (if a batch operation performs a
 273        // drupal_execute).
 274        if ($batch = &batch_get() && !isset($batch['current_set'])) {
 275          // The batch uses its own copies of $form and $form_state for
 276          // late execution of submit handers and post-batch redirection.
 277          $batch['form'] = $form;
 278          $batch['form_state'] = $form_state;
 279          $batch['progressive'] = !$form['#programmed'];
 280          batch_process();
 281          // Execution continues only for programmatic forms.
 282          // For 'regular' forms, we get redirected to the batch processing
 283          // page. Form redirection will be handled in _batch_finished(),
 284          // after the batch is processed.
 285        }
 286  
 287        // If no submit handlers have populated the $form_state['storage']
 288        // bundle, and the $form_state['rebuild'] flag has not been set,
 289        // we're finished and should redirect to a new destination page
 290        // if one has been set (and a fresh, unpopulated copy of the form
 291        // if one hasn't). If the form was called by drupal_execute(),
 292        // however, we'll skip this and let the calling function examine
 293        // the resulting $form_state bundle itself.
 294        if (!$form['#programmed'] && empty($form_state['rebuild']) && empty($form_state['storage'])) {
 295          if (!empty($form_state['no_redirect'])) {
 296            $form_state['executed'] = TRUE;
 297          }
 298          else {
 299            drupal_redirect_form($form, $form_state['redirect']);
 300          }
 301        }
 302      }
 303    }
 304  }
 305  
 306  /**
 307   * The original version of drupal_validate_form does not have an override for
 308   * the static check to only validate a form id once. Unfortunately, we need
 309   * to be able to override this.
 310   */
 311  function ctools_validate_form($form_id, $form, &$form_state) {
 312    static $validated_forms = array();
 313  
 314    if (isset($validated_forms[$form_id]) && empty($form_state['must_validate'])) {
 315      return;
 316    }
 317  
 318    // If the session token was set by drupal_prepare_form(), ensure that it
 319    // matches the current user's session.
 320    if (isset($form['#token'])) {
 321      if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) {
 322        // Setting this error will cause the form to fail validation.
 323        form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));
 324      }
 325    }
 326  
 327    if (!empty($form_state['clicked_button']['#skip validation'])) {
 328      return;
 329    }
 330  
 331    _form_validate($form, $form_state, $form_id);
 332    $validated_forms[$form_id] = TRUE;
 333  }
 334  


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