Code coverage for /20081101/includes/form.inc

Line #Times calledCode
1
<?php
2
// $Id: form.inc,v 1.299 2008/10/30 02:35:54 dries Exp $
3
4
/**
5
 * @defgroup forms Form builder functions
6
 * @{
7
 * Functions that build an abstract representation of a HTML form.
8
 *
9
 * All modules should declare their form builder functions to be in this
10
 * group and each builder function should reference its validate and submit
11
 * functions using \@see. Conversely, validate and submit functions should
12
 * reference the form builder function using \@see. For examples, of this
see
13
 * system_modules_uninstall() or user_pass(), the latter of which has the
14
 * following in its doxygen documentation:
15
 *
16
 * \@ingroup forms
17
 * \@see user_pass_validate().
18
 * \@see user_pass_submit().
19
 *
20
 * @} End of "defgroup forms".
21
 */
22
23
/**
24
 * @defgroup form_api Form generation
25
 * @{
26
 * Functions to enable the processing and display of HTML forms.
27
 *
28
 * Drupal uses these functions to achieve consistency in its form
processing and
29
 * presentation, while simplifying code and reducing the amount of HTML
that
30
 * must be explicitly generated by modules.
31
 *
32
 * The drupal_get_form() function handles retrieving, processing, and
33
 * displaying a rendered HTML form for modules automatically. For example:
34
 *
35
 * @code
36
 * // Display the user registration form.
37
 * $output = drupal_get_form('user_register');
38
 * @endcode
39
 *
40
 * Forms can also be built and submitted programmatically without any user
input
41
 * using the drupal_execute() function.
42
 *
43
 * For information on the format of the structured arrays used to define
forms,
44
 * and more detailed explanations of the Form API workflow, see the
45
 * @link
http://api.drupal.org/api/file/developer/topics/forms_api_reference.html
reference @endlink
46
 * and the @link
http://api.drupal.org/api/file/developer/topics/forms_api.html quickstart
guide. @endlink
47
 */
48
49
/**
50
 * Retrieves a form from a constructor function, or from the cache if
51
 * the form was built in a previous page-load. The form is then passesed
52
 * on for processing, after and rendered for display if necessary.
53
 *
54
 * @param $form_id
55
 *   The unique string identifying the desired form. If a function
56
 *   with that name exists, it is called to build the form array.
57
 *   Modules that need to generate the same form (or very similar forms)
58
 *   using different $form_ids can implement hook_forms(), which maps
59
 *   different $form_id values to the proper form constructor function.
Examples
60
 *   may be found in node_forms(), search_forms(), and user_forms().
61
 * @param ...
62
 *   Any additional arguments are passed on to the functions called by
63
 *   drupal_get_form(), including the unique form constructor function.
64
 *   For example, the node_edit form requires that a node object be passed
65
 *   in here when it is called.
66
 * @return
67
 *   The rendered form.
68
 */
692366
function drupal_get_form($form_id) {
701648
  $form_state = array('storage' => NULL, 'submitted' => FALSE);
71
721648
  $args = func_get_args();
731648
  $cacheable = FALSE;
74
751648
  if (isset($_SESSION['batch_form_state'])) {
76
    // We've been redirected here after a batch processing : the form has
77
    // already been processed, so we grab the post-process $form_state
value
78
    // and move on to form display. See _batch_finished() function.
790
    $form_state = $_SESSION['batch_form_state'];
800
    unset($_SESSION['batch_form_state']);
810
  }
82
  else {
83
    // If the incoming $_POST contains a form_build_id, we'll check the
84
    // cache for a copy of the form in question. If it's there, we don't
85
    // have to rebuild the form to proceed. In addition, if there is stored
86
    // form_state data from a previous step, we'll retrieve it so it can
87
    // be passed on to the form processing code.
881648
    if (isset($_POST['form_id']) && $_POST['form_id'] == $form_id &&
!empty($_POST['form_build_id'])) {
89540
      $form = form_get_cache($_POST['form_build_id'], $form_state);
90540
    }
91
92
    // If the previous bit of code didn't result in a populated $form
93
    // object, we're hitting the form for the first time and we need
94
    // to build it from scratch.
951648
    if (!isset($form)) {
961568
      $form_state['post'] = $_POST;
97
      // Use a copy of the function's arguments for manipulation
981568
      $args_temp = $args;
991568
      $args_temp[0] = &$form_state;
1001568
      array_unshift($args_temp, $form_id);
101
1021568
      $form = call_user_func_array('drupal_retrieve_form', $args_temp);
1031567
      $form_build_id = 'form-' . md5(uniqid(mt_rand(), TRUE));
1041567
      $form['#build_id'] = $form_build_id;
1051567
      drupal_prepare_form($form_id, $form, $form_state);
106
      // Store a copy of the unprocessed form for caching and indicate that
it
107
      // is cacheable if #cache will be set.
1081567
      $original_form = $form;
1091567
      $cacheable = TRUE;
1101567
      unset($form_state['post']);
1111567
    }
1121647
    $form['#post'] = $_POST;
113
114
    // Now that we know we have a form, we'll process it (validating,
115
    // submitting, and handling the results returned by its submission
116
    // handlers. Submit handlers accumulate data in the form_state by
117
    // altering the $form_state variable, which is passed into them by
118
    // reference.
1191647
    drupal_process_form($form_id, $form, $form_state);
1201179
    if ($cacheable && !empty($form['#cache'])) {
121
      // Caching is done past drupal_process_form so #process callbacks can
122
      // set #cache. By not sending the form state, we avoid storing
123
      // $form_state['storage'].
12484
      form_set_cache($form_build_id, $original_form, NULL);
12584
    }
126
  }
127
128
  // Most simple, single-step forms will be finished by this point --
129
  // drupal_process_form() usually redirects to another page (or to
130
  // a 'fresh' copy of the form) once processing is complete. If one
131
  // of the form's handlers has set $form_state['redirect'] to FALSE,
132
  // the form will simply be re-rendered with the values still in its
133
  // fields.
134
  //
135
  // If $form_state['storage'] or $form_state['rebuild'] have been
136
  // set by any submit or validate handlers, however, we know that
137
  // we're in a complex multi-part process of some sort and the form's
138
  // workflow is NOT complete. We need to construct a fresh copy of
139
  // the form, passing in the latest $form_state in addition to any
140
  // other variables passed into drupal_get_form().
141
1421179
  if (!empty($form_state['rebuild']) || !empty($form_state['storage'])) {
1439
    $form = drupal_rebuild_form($form_id, $form_state, $args);
1449
  }
145
146
  // If we haven't redirected to a new location by now, we want to
147
  // render whatever form array is currently in hand.
1481179
  return drupal_render_form($form_id, $form);
1490
}
150
151
/**
152
 * Retrieves a form, caches it and processes it with an empty $_POST.
153
 *
154
 * This function clears $_POST and passes the empty $_POST to the
form_builder.
155
 * To preserve some parts from $_POST, pass them in $form_state.
156
 *
157
 * If your AHAH callback simulates the pressing of a button, then your AHAH
158
 * callback will need to do the same as what drupal_get_form would do when
the
159
 * button is pressed: get the form from the cache, run drupal_process_form
over
160
 * it and then if it needs rebuild, run drupal_rebuild_form over it. Then
send
161
 * back a part of the returned form.
162
 * $form_state['clicked_button']['#array_parents'] will help you to find
which
163
 * part.
164
 *
165
 * @param $form_id
166
 *   The unique string identifying the desired form. If a function
167
 *   with that name exists, it is called to build the form array.
168
 *   Modules that need to generate the same form (or very similar forms)
169
 *   using different $form_ids can implement hook_forms(), which maps
170
 *   different $form_id values to the proper form constructor function.
Examples
171
 *   may be found in node_forms(), search_forms(), and user_forms().
172
 * @param $form_state
173
 *   A keyed array containing the current state of the form. Most
174
 *   important is the $form_state['storage'] collection.
175
 * @param $args
176
 *   Any additional arguments are passed on to the functions called by
177
 *   drupal_get_form(), plus the original form_state in the beginning. If
you
178
 *   are getting a form from the cache, use $form['#parameters'] to shift
off
179
 *   the $form_id from its beginning then the resulting array can be used
as
180
 *   $arg here.
181
 * @param $form_build_id
182
 *   If the AHAH callback calling this function only alters part of the
form,
183
 *   then pass in the existing form_build_id so we can re-cache with the
same
184
 *   csid.
185
 * @return
186
 *   The newly built form.
187
 */
1882366
function drupal_rebuild_form($form_id, &$form_state, $args, $form_build_id
= NULL) {
189
  // Remove the first argument. This is $form_id.when called from
190
  // drupal_get_form and the original $form_state when called from some
AHAH
191
  // callback. Neither is needed. After that, put in the current state.
1929
  $args[0] = &$form_state;
193
  // And the form_id.
1949
  array_unshift($args, $form_id);
1959
  $form = call_user_func_array('drupal_retrieve_form', $args);
196
1979
  if (!isset($form_build_id)) {
198
    // We need a new build_id for the new version of the form.
1999
    $form_build_id = 'form-' . md5(mt_rand());
2009
  }
2019
  $form['#build_id'] = $form_build_id;
2029
  drupal_prepare_form($form_id, $form, $form_state);
203
204
  // Now, we cache the form structure so it can be retrieved later for
205
  // validation. If $form_state['storage'] is populated, we'll also cache
206
  // it so that it can be used to resume complex multi-step processes.
2079
  form_set_cache($form_build_id, $form, $form_state);
208
209
  // Clear out all post data, as we don't want the previous step's
210
  // data to pollute this one and trigger validate/submit handling,
211
  // then process the form for rendering.
2129
  $_POST = array();
2139
  $form['#post'] = array();
2149
  drupal_process_form($form_id, $form, $form_state);
2159
  return $form;
2160
}
217
218
/**
219
 * Fetch a form from cache.
220
 */
2212366
function form_get_cache($form_build_id, &$form_state) {
222540
  if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) {
22380
    $form = $cached->data;
22480
    global $user;
22580
    if ((isset($form['#cache_token']) &&
drupal_valid_token($form['#cache_token'])) ||
(!isset($form['#cache_token']) && !$user->uid)) {
22680
      if ($cached = cache_get('storage_' . $form_build_id, 'cache_form')) {
2272
        $form_state['storage'] = $cached->data;
2282
      }
22980
      return $form;
2300
    }
2310
  }
232460
}
233
234
/**
235
 * Store a form in the cache
236
 */
2372366
function form_set_cache($form_build_id, $form, $form_state) {
238
  // 6 hours cache life time for forms should be plenty.
23993
  $expire = 21600;
24093
  global $user;
24193
  if ($user->uid) {
24293
    $form['#cache_token'] = drupal_get_token();
24393
  }
24493
  cache_set('form_' . $form_build_id, $form, 'cache_form', REQUEST_TIME +
$expire);
24593
  if (!empty($form_state['storage'])) {
2462
    cache_set('storage_' . $form_build_id, $form_state['storage'],
'cache_form', REQUEST_TIME + $expire);
2472
  }
24893
}
249
250
/**
251
 * Retrieves a form using a form_id, populates it with
$form_state['values'],
252
 * processes it, and returns any validation errors encountered. This
253
 * function is the programmatic counterpart to drupal_get_form().
254
 *
255
 * @param $form_id
256
 *   The unique string identifying the desired form. If a function
257
 *   with that name exists, it is called to build the form array.
258
 *   Modules that need to generate the same form (or very similar forms)
259
 *   using different $form_ids can implement hook_forms(), which maps
260
 *   different $form_id values to the proper form constructor function.
Examples
261
 *   may be found in node_forms(), search_forms(), and user_forms().
262
 * @param $form_state
263
 *   A keyed array containing the current state of the form. Most
264
 *   important is the $form_state['values'] collection, a tree of data
265
 *   used to simulate the incoming $_POST information from a user's
266
 *   form submission.
267
 * @param ...
268
 *   Any additional arguments are passed on to the functions called by
269
 *   drupal_execute(), including the unique form constructor function.
270
 *   For example, the node_edit form requires that a node object be passed
271
 *   in here when it is called.
272
 * For example:
273
 *
274
 * // register a new user
275
 * $form_state = array();
276
 * $form_state['values']['name'] = 'robo-user';
277
 * $form_state['values']['mail'] = 'robouser@example.com';
278
 * $form_state['values']['pass'] = 'password';
279
 * $form_state['values']['op'] = t('Create new account');
280
 * drupal_execute('user_register', $form_state);
281
 *
282
 * // Create a new node
283
 * $form_state = array();
284
 * module_load_include('inc', 'node', 'node.pages');
285
 * $node = array('type' => 'story');
286
 * $form_state['values']['title'] = 'My node';
287
 * $form_state['values']['body'] = 'This is the body text!';
288
 * $form_state['values']['name'] = 'robo-user';
289
 * $form_state['values']['op'] = t('Save');
290
 * drupal_execute('story_node_form', $form_state, (object)$node);
291
 */
2922366
function drupal_execute($form_id, &$form_state) {
2931
  $args = func_get_args();
2941
  $form = call_user_func_array('drupal_retrieve_form', $args);
2951
  $form['#post'] = $form_state['values'];
2961
  drupal_prepare_form($form_id, $form, $form_state);
2971
  drupal_process_form($form_id, $form, $form_state);
2981
}
299
300
/**
301
 * Retrieves the structured array that defines a given form.
302
 *
303
 * @param $form_id
304
 *   The unique string identifying the desired form. If a function
305
 *   with that name exists, it is called to build the form array.
306
 *   Modules that need to generate the same form (or very similar forms)
307
 *   using different $form_ids can implement hook_forms(), which maps
308
 *   different $form_id values to the proper form constructor function.
309
 * @param $form_state
310
 *   A keyed array containing the current state of the form.
311
 * @param ...
312
 *   Any additional arguments needed by the unique form constructor
313
 *   function. Generally, these are any arguments passed into the
314
 *   drupal_get_form() or drupal_execute() functions after the first
315
 *   argument. If a module implements hook_forms(), it can examine
316
 *   these additional arguments and conditionally return different
317
 *   builder functions as well.
318
 */
3192366
function drupal_retrieve_form($form_id, &$form_state) {
3201575
  static $forms;
321
322
  // We save two copies of the incoming arguments: one for modules to use
323
  // when mapping form ids to constructor functions, and another to pass to
324
  // the constructor function itself. We shift out the first argument --
the
325
  // $form_id itself -- from the list to pass into the constructor
function,
326
  // since it's already known.
3271575
  $args = func_get_args();
3281575
  $saved_args = $args;
3291575
  array_shift($args);
3301575
  if (isset($form_state)) {
3311575
    array_shift($args);
3321575
  }
333
334
  // We first check to see if there's a function named after the $form_id.
335
  // If there is, we simply pass the arguments on to it to get the form.
3361575
  if (!drupal_function_exists($form_id)) {
337
    // In cases where many form_ids need to share a central constructor
function,
338
    // such as the node editing form, modules can implement hook_forms().
It
339
    // maps one or more form_ids to the correct constructor functions.
340
    //
341
    // We cache the results of that hook to save time, but that only works
342
    // for modules that know all their form_ids in advance. (A module that
343
    // adds a small 'rate this comment' form to each comment in a list
344
    // would need a unique form_id for each one, for example.)
345
    //
346
    // So, we call the hook if $forms isn't yet populated, OR if it doesn't
347
    // yet have an entry for the requested form_id.
348150
    if (!isset($forms) || !isset($forms[$form_id])) {
349150
      $forms = module_invoke_all('forms', $form_id, $args);
350150
    }
351150
    $form_definition = $forms[$form_id];
352150
    if (isset($form_definition['callback arguments'])) {
35313
      $args = array_merge($form_definition['callback arguments'], $args);
35413
    }
355150
    if (isset($form_definition['callback'])) {
356150
      $callback = $form_definition['callback'];
357150
      drupal_function_exists($callback);
358150
    }
359150
  }
360
3611575
  array_unshift($args, NULL);
3621575
  $args[0] = &$form_state;
363
364
  // If $callback was returned by a hook_forms() implementation, call it.
365
  // Otherwise, call the function named after the form id.
3661575
  $form = call_user_func_array(isset($callback) ? $callback : $form_id,
$args);
367
368
  // We store the original function arguments, rather than the final $arg
369
  // value, so that form_alter functions can see what was originally
370
  // passed to drupal_retrieve_form(). This allows the contents of
#parameters
371
  // to be saved and passed in at a later date to recreate the form.
3721574
  $form['#parameters'] = $saved_args;
3731574
  return $form;
3740
}
375
376
/**
377
 * This function is the heart of form API. The form gets built, validated
and in
378
 * appropriate cases, submitted.
379
 *
380
 * @param $form_id
381
 *   The unique string identifying the current form.
382
 * @param $form
383
 *   An associative array containing the structure of the form.
384
 * @param $form_state
385
 *   A keyed array containing the current state of the form. This
386
 *   includes the current persistent storage data for the form, and
387
 *   any data passed along by earlier steps when displaying a
388
 *   multi-step form. Additional information, like the sanitized $_POST
389
 *   data, is also accumulated here.
390
 */
3912366
function drupal_process_form($form_id, &$form, &$form_state) {
3921648
  $form_state['values'] = array();
393
3941648
  $form = form_builder($form_id, $form, $form_state);
395
  // Only process the form if it is programmed or the form_id coming
396
  // from the POST data is set and matches the current form_id.
3971648
  if ((!empty($form['#programmed'])) || (!empty($form['#post']) &&
(isset($form['#post']['form_id']) && ($form['#post']['form_id'] ==
$form_id)))) {
398540
    drupal_validate_form($form_id, $form, $form_state);
399
400
    // form_clean_id() maintains a cache of element IDs it has seen,
401
    // so it can prevent duplicates. We want to be sure we reset that
402
    // cache when a form is processed, so scenerios that result in
403
    // the form being built behind the scenes and again for the
404
    // browser don't increment all the element IDs needlessly.
405540
    form_clean_id(NULL, TRUE);
406
407540
    if ((!empty($form_state['submitted'])) && !form_get_errors() &&
empty($form_state['rebuild'])) {
408482
      $form_state['redirect'] = NULL;
409482
      form_execute_handlers('submit', $form, $form_state);
410
411
      // We'll clear out the cached copies of the form and its stored data
412
      // here, as we've finished with them. The in-memory copies are still
413
      // here, though.
414480
      if (variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED &&
!empty($form_state['values']['form_build_id'])) {
415449
        cache_clear_all('form_' . $form_state['values']['form_build_id'],
'cache_form');
416449
        cache_clear_all('storage_' .
$form_state['values']['form_build_id'], 'cache_form');
417449
      }
418
419
      // If batches were set in the submit handlers, we process them now,
420
      // possibly ending execution. We make sure we do not react to the
batch
421
      // that is already being processed (if a batch operation performs a
422
      // drupal_execute).
423480
      if ($batch =& batch_get() && !isset($batch['current_set'])) {
424
        // The batch uses its own copies of $form and $form_state for
425
        // late execution of submit handers and post-batch redirection.
4260
        $batch['form'] = $form;
4270
        $batch['form_state'] = $form_state;
4280
        $batch['progressive'] = !$form['#programmed'];
4290
        batch_process();
430
        // Execution continues only for programmatic forms.
431
        // For 'regular' forms, we get redirected to the batch processing
432
        // page. Form redirection will be handled in _batch_finished(),
433
        // after the batch is processed.
4340
      }
435
436
      // If no submit handlers have populated the $form_state['storage']
437
      // bundle, and the $form_state['rebuild'] flag has not been set,
438
      // we're finished and should redirect to a new destination page
439
      // if one has been set (and a fresh, unpopulated copy of the form
440
      // if one hasn't). If the form was called by drupal_execute(),
441
      // however, we'll skip this and let the calling function examine
442
      // the resulting $form_state bundle itself.
443480
      if (!$form['#programmed'] && empty($form_state['rebuild']) &&
empty($form_state['storage'])) {
444471
        drupal_redirect_form($form, $form_state['redirect']);
4453
      }
44613
    }
44771
  }
4481181
}
449
450
/**
451
 * Prepares a structured form array by adding required elements,
452
 * executing any hook_form_alter functions, and optionally inserting
453
 * a validation token to prevent tampering.
454
 *
455
 * @param $form_id
456
 *   A unique string identifying the form for validation, submission,
457
 *   theming, and hook_form_alter functions.
458
 * @param $form
459
 *   An associative array containing the structure of the form.
460
 * @param $form_state
461
 *   A keyed array containing the current state of the form. Passed
462
 *   in here so that hook_form_alter() calls can use it, as well.
463
 */
4642366
function drupal_prepare_form($form_id, &$form, &$form_state) {
4651575
  global $user;
466
4671575
  $form['#type'] = 'form';
4681575
  $form['#programmed'] = isset($form['#post']);
469
4701575
  if (isset($form['#build_id'])) {
4711574
    $form['form_build_id'] = array(
4721574
      '#type' => 'hidden',
4731574
      '#value' => $form['#build_id'],
4741574
      '#id' => $form['#build_id'],
4751574
      '#name' => 'form_build_id',
476
    );
4771574
  }
478
479
  // Add a token, based on either #token or form_id, to any form displayed
to
480
  // authenticated users. This ensures that any submitted form was actually
481
  // requested previously by the user and protects against cross site
request
482
  // forgeries.
4831575
  if (isset($form['#token'])) {
48477
    if ($form['#token'] === FALSE || $user->uid == 0 ||
$form['#programmed']) {
48541
      unset($form['#token']);
48641
    }
487
    else {
48836
      $form['form_token'] = array('#type' => 'token', '#default_value' =>
drupal_get_token($form['#token']));
489
    }
49077
  }
4911531
  elseif (isset($user->uid) && $user->uid && !$form['#programmed']) {
492804
    $form['#token'] = $form_id;
493804
    $form['form_token'] = array(
494804
      '#id' => form_clean_id('edit-' . $form_id . '-form-token'),
495804
      '#type' => 'token',
496804
      '#default_value' => drupal_get_token($form['#token']),
497
    );
498804
  }
499
5001575
  if (isset($form_id)) {
5011575
    $form['form_id'] = array(
5021575
      '#type' => 'hidden',
5031575
      '#value' => $form_id,
5041575
      '#id' => form_clean_id("edit-$form_id"),
505
    );
5061575
  }
5071575
  if (!isset($form['#id'])) {
5081249
    $form['#id'] = form_clean_id($form_id);
5091249
  }
510
5111575
  $form += _element_info('form');
5121575
  $form += array('#tree' => FALSE, '#parents' => array());
513
5141575
  if (!isset($form['#validate'])) {
515705
    if (drupal_function_exists($form_id . '_validate')) {
516357
      $form['#validate'] = array($form_id . '_validate');
517357
    }
518705
  }
519
5201575
  if (!isset($form['#submit'])) {
5211202
    if (drupal_function_exists($form_id . '_submit')) {
522
      // We set submit here so that it can be altered.
5231122
      $form['#submit'] = array($form_id . '_submit');
5241122
    }
5251202
  }
526
527
  // Normally, we would call drupal_alter($form_id, $form, $form_state).
528
  // However, drupal_alter() normally supports just one byref parameter.
Using
529
  // the __drupal_alter_by_ref key, we can store any additional parameters
530
  // that need to be altered, and they'll be split out into additional
params
531
  // for the hook_form_alter() implementations.
532
  // @todo: Remove this in Drupal 7.
5331575
  $data = &$form;
5341575
  $data['__drupal_alter_by_ref'] = array(&$form_state);
5351575
  drupal_alter('form_' . $form_id, $data);
536
537
  // __drupal_alter_by_ref is unset in the drupal_alter() function, we need
538
  // to repopulate it to ensure both calls get the data.
5391575
  $data['__drupal_alter_by_ref'] = array(&$form_state);
5401575
  drupal_alter('form', $data, $form_id);
5411575
}
542
543
544
/**
545
 * Validates user-submitted form data from the $form_state using
546
 * the validate functions defined in a structured form array.
547
 *
548
 * @param $form_id
549
 *   A unique string identifying the form for validation, submission,
550
 *   theming, and hook_form_alter functions.
551
 * @param $form
552
 *   An associative array containing the structure of the form.
553
 * @param $form_state
554
 *   A keyed array containing the current state of the form. The current
555
 *   user-submitted data is stored in $form_state['values'], though
556
 *   form validation functions are passed an explicit copy of the
557
 *   values for the sake of simplicity. Validation handlers can also
558
 *   $form_state to pass information on to submit handlers. For example:
559
 *     $form_state['data_for_submision'] = $data;
560
 *   This technique is useful when validation requires file parsing,
561
 *   web service requests, or other expensive requests that should
562
 *   not be repeated in the submission step.
563
 */
5642366
function drupal_validate_form($form_id, $form, &$form_state) {
565540
  static $validated_forms = array();
566
567540
  if (isset($validated_forms[$form_id])) {
56814
    return;
5690
  }
570
571
  // If the session token was set by drupal_prepare_form(), ensure that it
572
  // matches the current user's session.
573540
  if (isset($form['#token'])) {
574338
    if (!drupal_valid_token($form_state['values']['form_token'],
$form['#token'])) {
575
      // Setting this error will cause the form to fail validation.
5760
      form_set_error('form_token', t('Validation error, please try again.
If this error persists, please contact the site administrator.'));
5770
    }
578338
  }
579
580540
  _form_validate($form, $form_state, $form_id);
581540
  $validated_forms[$form_id] = TRUE;
582540
}
583
584
/**
585
 * Renders a structured form array into themed HTML.
586
 *
587
 * @param $form_id
588
 *   A unique string identifying the form for validation, submission,
589
 *   theming, and hook_form_alter functions.
590
 * @param $form
591
 *   An associative array containing the structure of the form.
592
 * @return
593
 *   A string containing the path of the page to display when processing
594
 *   is complete.
595
 */
5962366
function drupal_render_form($form_id, &$form) {
597
  // Don't override #theme if someone already set it.
5981179
  if (!isset($form['#theme'])) {
599992
    init_theme();
600992
    $registry = theme_get_registry();
601992
    if (isset($registry[$form_id])) {
602152
      $form['#theme'] = $form_id;
603152
    }
604992
  }
605
6061179
  $output = drupal_render($form);
6071179
  return $output;
6080
}
609
610
/**
611
 * Redirect the user to a URL after a form has been processed.
612
 *
613
 * @param $form
614
 *   An associative array containing the structure of the form.
615
 * @param $redirect
616
 *   An optional value containing the destination path to redirect
617
 *   to if none is specified by the form.
618
 */
6192366
function drupal_redirect_form($form, $redirect = NULL) {
620473
  $goto = NULL;
621473
  if (isset($redirect)) {
622400
    $goto = $redirect;
623400
  }
624473
  if ($goto !== FALSE && isset($form['#redirect'])) {
6253
    $goto = $form['#redirect'];
6263
  }
627473
  if (!isset($goto) || ($goto !== FALSE)) {
628470
    if (isset($goto)) {
629400
      if (is_array($goto)) {
63015
        call_user_func_array('drupal_goto', $goto);
6310
      }
632
      else {
633385
        drupal_goto($goto);
634
      }
6350
    }
63670
    drupal_goto($_GET['q']);
6370
  }
6383
}
639
640
/**
641
 * Performs validation on form elements. First ensures required fields are
642
 * completed, #maxlength is not exceeded, and selected options were in the
643
 * list of options given to the user. Then calls user-defined validators.
644
 *
645
 * @param $elements
646
 *   An associative array containing the structure of the form.
647
 * @param $form_state
648
 *   A keyed array containing the current state of the form. The current
649
 *   user-submitted data is stored in $form_state['values'], though
650
 *   form validation functions are passed an explicit copy of the
651
 *   values for the sake of simplicity. Validation handlers can also
652
 *   $form_state to pass information on to submit handlers. For example:
653
 *     $form_state['data_for_submision'] = $data;
654
 *   This technique is useful when validation requires file parsing,
655
 *   web service requests, or other expensive requests that should
656
 *   not be repeated in the submission step.
657
 * @param $form_id
658
 *   A unique string identifying the form for validation, submission,
659
 *   theming, and hook_form_alter functions.
660
 */
6612366
function _form_validate($elements, &$form_state, $form_id = NULL) {
662540
  static $complete_form;
663
664
  // Also used in the installer, pre-database setup.
665540
  $t = get_t();
666
667
  // Recurse through all children.
668540
  foreach (element_children($elements) as $key) {
669540
    if (isset($elements[$key]) && $elements[$key]) {
670540
      _form_validate($elements[$key], $form_state);
671540
    }
672540
  }
673
  // Validate the current input.
674540
  if (!isset($elements['#validated']) || !$elements['#validated']) {
675540
    if (isset($elements['#needs_validation'])) {
676
      // Make sure a value is passed when the field is required.
677
      // A simple call to empty() will not cut it here as some fields, like
678
      // checkboxes, can return a valid value of '0'. Instead, check the
679
      // length if it's a string, and the item count if it's an array.
680540
      if ($elements['#required'] && (!count($elements['#value']) ||
(is_string($elements['#value']) && strlen(trim($elements['#value'])) ==
0))) {
6819
        form_error($elements, $t('!name field is required.', array('!name'
=> $elements['#title'])));
6829
      }
683
684
      // Verify that the value is not longer than #maxlength.
685540
      if (isset($elements['#maxlength']) &&
drupal_strlen($elements['#value']) > $elements['#maxlength']) {
6860
        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']))));
6870
      }
688
689540
      if (isset($elements['#options']) && isset($elements['#value'])) {
690205
        if ($elements['#type'] == 'select') {
691181
          $options = form_options_flatten($elements['#options']);
692181
        }
693
        else {
69458
          $options = $elements['#options'];
695
        }
696205
        if (is_array($elements['#value'])) {
69741
          $value = $elements['#type'] == 'checkboxes' ?
array_keys(array_filter($elements['#value'])) : $elements['#value'];
69841
          foreach ($value as $v) {
69925
            if (!isset($options[$v])) {
7000
              form_error($elements, $t('An illegal choice has been
detected. Please contact the site administrator.'));
7010
              watchdog('form', 'Illegal choice %choice in !name element.',
array('%choice' => $v, '!name' => empty($elements['#title']) ?
$elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
7020
            }
70325
          }
70441
        }
705185
        elseif (!isset($options[$elements['#value']])) {
7061
          form_error($elements, $t('An illegal choice has been detected.
Please contact the site administrator.'));
7071
          watchdog('form', 'Illegal choice %choice in %name element.',
array('%choice' => $elements['#value'], '%name' =>
empty($elements['#title']) ? $elements['#parents'][0] :
$elements['#title']), WATCHDOG_ERROR);
7081
        }
709205
      }
710540
    }
711
712
    // Call user-defined form level validators and store a copy of the full
713
    // form so that element-specific validators can examine the entire
structure
714
    // if necessary.
715540
    if (isset($form_id)) {
716540
      form_execute_handlers('validate', $elements, $form_state);
717540
      $complete_form = $elements;
718540
    }
719
    // Call any element-specific validators. These must act on the element
720
    // #value data.
721540
    elseif (isset($elements['#element_validate'])) {
72212
      foreach ($elements['#element_validate'] as $function) {
72312
        if (drupal_function_exists($function))  {
72412
          $function($elements, $form_state, $complete_form);
72512
        }
72612
      }
72712
    }
728540
    $elements['#validated'] = TRUE;
729540
  }
730540
}
731
732
/**
733
 * A helper function used to execute custom validation and submission
734
 * handlers for a given form. Button-specific handlers are checked
735
 * first. If none exist, the function falls back to form-level handlers.
736
 *
737
 * @param $type
738
 *   The type of handler to execute. 'validate' or 'submit' are the
739
 *   defaults used by Form API.
740
 * @param $form
741
 *   An associative array containing the structure of the form.
742
 * @param $form_state
743
 *   A keyed array containing the current state of the form. If the user
744
 *   submitted the form by clicking a button with custom handler functions
745
 *   defined, those handlers will be stored here.
746
 */
7472366
function form_execute_handlers($type, &$form, &$form_state) {
748540
  $return = FALSE;
749540
  if (isset($form_state[$type . '_handlers'])) {
75076
    $handlers = $form_state[$type . '_handlers'];
75176
  }
752540
  elseif (isset($form['#' . $type])) {
753536
    $handlers = $form['#' . $type];
754536
  }
755
  else {
756108
    $handlers = array();
757
  }
758
759540
  foreach ($handlers as $function) {
760536
    if (drupal_function_exists($function))  {
761536
      if ($type == 'submit' && ($batch =& batch_get())) {
762
        // Some previous _submit handler has set a batch. We store the call
763
        // in a special 'control' batch set, for execution at the correct
764
        // time during the batch processing workflow.
7650
        $batch['sets'][] = array('form_submit' => $function);
7660
      }
767
      else {
768536
        $function($form, $form_state);
769
      }
770534
      $return = TRUE;
771534
    }
772534
  }
773540
  return $return;
7740
}
775
776
/**
777
 * File an error against a form element.
778
 *
779
 * @param $name
780
 *   The name of the form element. If the #parents property of your form
781
 *   element is array('foo', 'bar', 'baz') then you may set an error on
'foo'
782
 *   or 'foo][bar][baz'. Setting an error on 'foo' sets an error for every
783
 *   element where the #parents array starts with 'foo'.
784
 * @param $message
785
 *   The error message to present to the user.
786
 * @param $reset
787
 *   Reset the form errors static cache.
788
 * @return
789
 *   Never use the return value of this function, use form_get_errors and
790
 *   form_get_error instead.
791
 */
7922366
function form_set_error($name = NULL, $message = '', $reset = FALSE) {
7931569
  static $form = array();
7941569
  if ($reset) {
7950
    $form = array();
7960
  }
7971569
  if (isset($name) && !isset($form[$name])) {
79849
    $form[$name] = $message;
79949
    if ($message) {
80049
      drupal_set_message($message, 'error');
80149
    }
80249
  }
8031569
  return $form;
8040
}
805
806
/**
807
 * Return an associative array of all errors.
808
 */
8092366
function form_get_errors() {
810568
  $form = form_set_error();
811568
  if (!empty($form)) {
81247
    return $form;
8130
  }
814523
}
815
816
/**
817
 * Return the error message filed against the form with the specified name.
818
 */
8192366
function form_get_error($element) {
8201090
  $form = form_set_error();
8211090
  $key = $element['#parents'][0];
8221090
  if (isset($form[$key])) {
82329
    return $form[$key];
8240
  }
8251086
  $key = implode('][', $element['#parents']);
8261086
  if (isset($form[$key])) {
8270
    return $form[$key];
8280
  }
8291086
}
830
831
/**
832
 * Flag an element as having an error.
833
 */
8342366
function form_error(&$element, $message = '') {
8359
  form_set_error(implode('][', $element['#parents']), $message);
8369
}
837
838
/**
839
 * Walk through the structured form array, adding any required
840
 * properties to each element and mapping the incoming $_POST
841
 * data to the proper elements.
842
 *
843
 * @param $form_id
844
 *   A unique string identifying the form for validation, submission,
845
 *   theming, and hook_form_alter functions.
846
 * @param $form
847
 *   An associative array containing the structure of the form.
848
 * @param $form_state
849
 *   A keyed array containing the current state of the form. In this
850
 *   context, it is used to accumulate information about which button
851
 *   was clicked when the form was submitted, as well as the sanitized
852
 *   $_POST data.
853
 */
8542366
function form_builder($form_id, $form, &$form_state) {
8551648
  static $complete_form, $cache;
856
857
  // Initialize as unprocessed.
8581648
  $form['#processed'] = FALSE;
859
860
  // Use element defaults.
8611648
  if ((!empty($form['#type'])) && ($info = _element_info($form['#type'])))
{
862
    // Overlay $info onto $form, retaining preexisting keys in $form.
8631648
    $form += $info;
8641648
  }
865
8661648
  if (isset($form['#type']) && $form['#type'] == 'form') {
8671648
    $cache = NULL;
8681648
    $complete_form = $form;
8691648
    if (!empty($form['#programmed'])) {
8702
      $form_state['submitted'] = TRUE;
8712
    }
8721648
  }
873
8741648
  if (isset($form['#input']) && $form['#input']) {
8751648
    _form_builder_handle_input_element($form_id, $form, $form_state,
$complete_form);
8761648
  }
8771648
  $form['#defaults_loaded'] = TRUE;
878
879
  // We start off assuming all form elements are in the correct order.
8801648
  $form['#sorted'] = TRUE;
881
882
  // Recurse through all child elements.
8831648
  $count = 0;
8841648
  foreach (element_children($form) as $key) {
8851648
    $form[$key]['#post'] = $form['#post'];
8861648
    $form[$key]['#programmed'] = $form['#programmed'];
887
    // Don't squash an existing tree value.
8881648
    if (!isset($form[$key]['#tree'])) {
8891648
      $form[$key]['#tree'] = $form['#tree'];
8901648
    }
891
892
    // Deny access to child elements if parent is denied.
8931648
    if (isset($form['#access']) && !$form['#access']) {
894159
      $form[$key]['#access'] = FALSE;
895159
    }
896
897
    // Don't squash existing parents value.
8981648
    if (!isset($form[$key]['#parents'])) {
899
      // Check to see if a tree of child elements is present. If so,
900
      // continue down the tree if required.
9011648
      $form[$key]['#parents'] = $form[$key]['#tree'] && $form['#tree'] ?
array_merge($form['#parents'], array($key)) : array($key);
9021648
      $array_parents = isset($form['#array_parents']) ?
$form['#array_parents'] : array();
9031648
      $array_parents[] = $key;
9041648
      $form[$key]['#array_parents'] = $array_parents;
9051648
    }
906
907
    // Assign a decimal placeholder weight to preserve original array
order.
9081648
    if (!isset($form[$key]['#weight'])) {
9091648
      $form[$key]['#weight'] = $count/1000;
9101648
    }
911
    else {
912
      // If one of the child elements has a weight then we will need to
sort
913
      // later.
914797
      unset($form['#sorted']);
915
    }
9161648
    $form[$key] = form_builder($form_id, $form[$key], $form_state);
9171648
    $count++;
9181648
  }
919
920
  // The #after_build flag allows any piece of a form to be altered
921
  // after normal input parsing has been completed.
9221648
  if (isset($form['#after_build']) && !isset($form['#after_build_done'])) {
923161
    foreach ($form['#after_build'] as $function) {
924161
      $form = $function($form, $form_state);
925161
      $form['#after_build_done'] = TRUE;
926161
    }
927161
  }
928
929
  // Now that we've processed everything, we can go back to handle the
funky
930
  // Internet Explorer button-click scenario.
9311648
  _form_builder_ie_cleanup($form, $form_state);
932
933
  // We shoud keep the buttons array until the IE clean up function
934
  // has recognized the submit button so the form has been marked
935
  // as submitted. If we already know which button was submitted,
936
  // we don't need the array.
9371648
  if (!empty($form_state['submitted'])) {
938534
    unset($form_state['buttons']);
939534
  }
940
941
  // If some callback set #cache, we need to flip a static flag so later it
942
  // can be found.
9431648
  if (!empty($form['#cache'])) {
944162
    $cache = $form['#cache'];
945162
  }
946
  // We are on the top form, we can copy back #cache if it's set.
9471648
  if (isset($form['#type']) && $form['#type'] == 'form' && isset($cache)) {
948162
    $form['#cache'] = TRUE;
949162
  }
9501648
  return $form;
9510
}
952
953
/**
954
 * Populate the #value and #name properties of input elements so they
955
 * can be processed and rendered. Also, execute any #process handlers
956
 * attached to a specific element.
957
 */
9582366
function _form_builder_handle_input_element($form_id, &$form, &$form_state,
$complete_form) {
9591648
  if (!isset($form['#name'])) {
9601648
    $name = array_shift($form['#parents']);
9611648
    $form['#name'] = $name;
9621648
    if ($form['#type'] == 'file') {
963
      // To make it easier to handle $_FILES in file.inc, we place all
964
      // file fields in the 'files' array. Also, we do not support
965
      // nested file names.
96643
      $form['#name'] = 'files[' . $form['#name'] . ']';
96743
    }
9681648
    elseif (count($form['#parents'])) {
969462
      $form['#name'] .= '[' . implode('][', $form['#parents']) . ']';
970462
    }
9711648
    array_unshift($form['#parents'], $name);
9721648
  }
9731648
  if (!isset($form['#id'])) {
9741624
    $form['#id'] = form_clean_id('edit-' . implode('-',
$form['#parents']));
9751624
  }
976
9771648
  unset($edit);
9781648
  if (!empty($form['#disabled'])) {
979152
    $form['#attributes']['disabled'] = 'disabled';
980152
  }
981
9821648
  if (!isset($form['#value']) && !array_key_exists('#value', $form)) {
9831647
    $function = !empty($form['#value_callback']) ? $form['#value_callback']
: 'form_type_' . $form['#type'] . '_value';
9841647
    if (($form['#programmed']) || ((!isset($form['#access']) ||
$form['#access']) && isset($form['#post']) &&
(isset($form['#post']['form_id']) && $form['#post']['form_id'] ==
$form_id))) {
985540
      $edit = $form['#post'];
986540
      foreach ($form['#parents'] as $parent) {
987540
        $edit = isset($edit[$parent]) ? $edit[$parent] : NULL;
988540
      }
989540
      if (!$form['#programmed'] || isset($edit)) {
990
        // Call #type_value to set the form value;
991540
        if (function_exists($function)) {
992540
          $form['#value'] = $function($form, $edit);
993540
        }
994540
        if (!isset($form['#value']) && isset($edit)) {
995409
          $form['#value'] = $edit;
996409
        }
997540
      }
998
      // Mark all posted values for validation.
999540
      if (isset($form['#value']) || (isset($form['#required']) &&
$form['#required'])) {
1000540
        $form['#needs_validation'] = TRUE;
1001540
      }
1002540
    }
1003
    // Load defaults.
10041647
    if (!isset($form['#value'])) {
1005
      // Call #type_value without a second argument to request
default_value handling.
10061226
      if (function_exists($function)) {
10071215
        $form['#value'] = $function($form);
10081215
      }
1009
      // Final catch. If we haven't set a value yet, use the explicit
default value.
1010
      // Avoid image buttons (which come with garbage value), so we only
get value
1011
      // for the button actually clicked.
10121226
      if (!isset($form['#value']) && empty($form['#has_garbage_value'])) {
10131226
        $form['#value'] = isset($form['#default_value']) ?
$form['#default_value'] : '';
10141226
      }
10151226
    }
10161647
  }
1017
1018
  // Determine which button (if any) was clicked to submit the form.
1019
  // We compare the incoming values with the buttons defined in the form,
1020
  // and flag the one that matches. We have to do some funky tricks to
1021
  // deal with Internet Explorer's handling of single-button forms, though.
10221648
  if (!empty($form['#post']) && isset($form['#executes_submit_callback']))
{
1023
    // First, accumulate a collection of buttons, divided into two bins:
1024
    // those that execute full submit callbacks and those that only
validate.
1025541
    $button_type = $form['#executes_submit_callback'] ? 'submit' :
'button';
1026541
    $form_state['buttons'][$button_type][] = $form;
1027
1028541
    if (_form_button_was_clicked($form)) {
1029539
      $form_state['submitted'] = $form_state['submitted'] ||
$form['#executes_submit_callback'];
1030
1031
      // In most cases, we want to use form_set_value() to manipulate
1032
      // the global variables. In this special case, we want to make sure
that
1033
      // the value of this element is listed in $form_variables under 'op'.
1034539
      $form_state['values'][$form['#name']] = $form['#value'];
1035539
      $form_state['clicked_button'] = $form;
1036
1037539
      if (isset($form['#validate'])) {
10380
        $form_state['validate_handlers'] = $form['#validate'];
10390
      }
1040539
      if (isset($form['#submit'])) {
104179
        $form_state['submit_handlers'] = $form['#submit'];
104279
      }
1043539
    }
1044541
  }
1045
  // Allow for elements to expand to multiple elements, e.g., radios,
1046
  // checkboxes and files.
10471648
  if (isset($form['#process']) && !$form['#processed']) {
10481648
    foreach ($form['#process'] as $process) {
10491648
      if (drupal_function_exists($process)) {
10501648
        $form = $process($form, isset($edit) ? $edit : NULL, $form_state,
$complete_form);
10511648
      }
10521648
    }
10531648
    $form['#processed'] = TRUE;
10541648
  }
10551648
  form_set_value($form, $form['#value'], $form_state);
10561648
}
1057
1058
/**
1059
 * Helper function to handle the sometimes-convoluted logic of button
1060
 * click detection.
1061
 *
1062
 * In Internet Explorer, if ONLY one submit button is present, AND the
1063
 * enter key is used to submit the form, no form value is sent for it
1064
 * and we'll never detect a match. That special case is handled by
1065
 * _form_builder_ie_cleanup().
1066
 */
10672366
function _form_button_was_clicked($form) {
1068
  // First detect normal 'vanilla' button clicks. Traditionally, all
1069
  // standard buttons on a form share the same name (usually 'op'),
1070
  // and the specific return value is used to determine which was
1071
  // clicked. This ONLY works as long as $form['#name'] puts the
1072
  // value at the top level of the tree of $_POST data.
1073541
  if (isset($form['#post'][$form['#name']]) &&
$form['#post'][$form['#name']] == $form['#value']) {
1074539
    return TRUE;
10750
  }
1076
  // When image buttons are clicked, browsers do NOT pass the form element
1077
  // value in $_POST. Instead they pass an integer representing the
1078
  // coordinates of the click on the button image. This means that image
1079
  // buttons MUST have unique $form['#name'] values, but the details of
1080
  // their $_POST data should be ignored.
1081175
  elseif (!empty($form['#has_garbage_value']) && isset($form['#value']) &&
$form['#value'] !== '') {
10820
    return TRUE;
10830
  }
1084175
  return FALSE;
10850
}
1086
1087
/**
1088
 * In IE, if only one submit button is present, AND the enter key is
1089
 * used to submit the form, no form value is sent for it and our normal
1090
 * button detection code will never detect a match. We call this
1091
 * function after all other button-detection is complete to check
1092
 * for the proper conditions, and treat the single button on the form
1093
 * as 'clicked' if they are met.
1094
 */
10952366
function _form_builder_ie_cleanup($form, &$form_state) {
1096
  // Quick check to make sure we're always looking at the full form
1097
  // and not a sub-element.
10981648
  if (!empty($form['#type']) && $form['#type'] == 'form') {
1099
    // If we haven't recognized a submission yet, and there's a single
1100
    // submit button, we know that we've hit the right conditions. Grab
1101
    // the first one and treat it as the clicked button.
11021648
    if (empty($form_state['submitted']) &&
!empty($form_state['buttons']['submit']) &&
empty($form_state['buttons']['button'])) {
110316
      $button = $form_state['buttons']['submit'][0];
1104
1105
      // Set up all the $form_state information that would have been
1106
      // populated had the button been recognized earlier.
110716
      $form_state['submitted'] = TRUE;
110816
      $form_state['submit_handlers'] = empty($button['#submit']) ? NULL :
$button['#submit'];
110916
      $form_state['validate_handlers'] = empty($button['#validate']) ? NULL
: $button['#validate'];
111016
      $form_state['values'][$button['#name']] = $button['#value'];
111116
      $form_state['clicked_button'] = $button;
111216
    }
11131648
  }
11141648
}
1115
1116
/**
1117
 * Helper function to determine the value for an image button form element.
1118
 *
1119
 * @param $form
1120
 *   The form element whose value is being populated.
1121
 * @param $edit
1122
 *   The incoming POST data to populate the form element. If this is FALSE,
1123
 *   the element's default value should be returned.
1124
 * @return
1125
 *   The data that will appear in the $form_state['values'] collection
1126
 *   for this element. Return nothing to use the default.
1127
 */
11282366
function form_type_image_button_value($form, $edit = FALSE) {
11290
  if ($edit !== FALSE) {
11300
    if (!empty($edit)) {
1131
      // If we're dealing with Mozilla or Opera, we're lucky. It will
1132
      // return a proper value, and we can get on with things.
11330
      return $form['#return_value'];
11340
    }
1135
    else {
1136
      // Unfortunately, in IE we never get back a proper value for THIS
1137
      // form element. Instead, we get back two split values: one for the
1138
      // X and one for the Y coordinates on which the user clicked the
1139
      // button. We'll find this element in the #post data, and search
1140
      // in the same spot for its name, with '_x'.
11410
      $post = $form['#post'];
11420
      foreach (split('\[', $form['#name']) as $element_name) {
1143
        // chop off the ] that may exist.
11440
        if (substr($element_name, -1) == ']') {
11450
          $element_name = substr($element_name, 0, -1);
11460
        }
1147
11480
        if (!isset($post[$element_name])) {
11490
          if (isset($post[$element_name . '_x'])) {
11500
            return $form['#return_value'];
11510
          }
11520
          return NULL;
11530
        }
11540
        $post = $post[$element_name];
11550
      }
11560
      return $form['#return_value'];
1157
    }
11580
  }
11590
}
1160
1161
/**
1162
 * Helper function to determine the value for a checkbox form element.
1163
 *
1164
 * @param $form
1165
 *   The form element whose value is being populated.
1166
 * @param $edit
1167
 *   The incoming POST data to populate the form element. If this is FALSE,
1168
 *   the element's default value should be returned.
1169
 * @return
1170
 *   The data that will appear in the $form_state['values'] collection
1171
 *   for this element. Return nothing to use the default.
1172
 */
11732366
function form_type_checkbox_value($form, $edit = FALSE) {
1174427
  if ($edit !== FALSE) {
1175157
    if (empty($form['#disabled'])) {
1176157
      return !empty($edit) ? $form['#return_value'] : 0;
11770
    }
1178
    else {
11790
      return $form['#default_value'];
1180
    }
11810
  }
1182334
}
1183
1184
/**
1185
 * Helper function to determine the value for a checkboxes form element.
1186
 *
1187
 * @param $form
1188
 *   The form element whose value is being populated.
1189
 * @param $edit
1190
 *   The incoming POST data to populate the form element. If this is FALSE,
1191
 *   the element's default value should be returned.
1192
 * @return
1193
 *   The data that will appear in the $form_state['values'] collection
1194
 *   for this element. Return nothing to use the default.
1195
 */
11962366
function form_type_checkboxes_value($form, $edit = FALSE) {
1197114
  if ($edit === FALSE) {
119878
    $value = array();
119978
    $form += array('#default_value' => array());
120078
    foreach ($form['#default_value'] as $key) {
120133
      $value[$key] = 1;
120233
    }
120378
    return $value;
12040
  }
120536
  elseif (!isset($edit)) {
120612
    return array();
12070
  }
120824
}
1209
1210
/**
1211
 * Helper function to determine the value for a password_confirm form
1212
 * element.
1213
 *
1214
 * @param $form
1215
 *   The form element whose value is being populated.
1216
 * @param $edit
1217
 *   The incoming POST data to populate the form element. If this is FALSE,
1218
 *   the element's default value should be returned.
1219
 * @return
1220
 *   The data that will appear in the $form_state['values'] collection
1221
 *   for this element. Return nothing to use the default.
1222
 */
12232366
function form_type_password_confirm_value($form, $edit = FALSE) {
122419
  if ($edit === FALSE) {
122511
    $form += array('#default_value' => array());
122611
    return $form['#default_value'] + array('pass1' => '', 'pass2' => '');
12270
  }
12288
}
1229
1230
/**
1231
 * Helper function to determine the value for a select form element.
1232
 *
1233
 * @param $form
1234
 *   The form element whose value is being populated.
1235
 * @param $edit
1236
 *   The incoming POST data to populate the form element. If this is FALSE,
1237
 *   the element's default value should be returned.
1238
 * @return
1239
 *   The data that will appear in the $form_state['values'] collection
1240
 *   for this element. Return nothing to use the default.
1241
 */
12422366
function form_type_select_value($form, $edit = FALSE) {
1243493
  if ($edit !== FALSE) {
1244154
    if (isset($form['#multiple']) && $form['#multiple']) {
12455
      return (is_array($edit)) ? drupal_map_assoc($edit) : array();
12460
    }
1247
    else {
1248149
      return $edit;
1249
    }
12500
  }
1251367
}
1252
1253
/**
1254
 * Helper function to determine the value for a textfield form element.
1255
 *
1256
 * @param $form
1257
 *   The form element whose value is being populated.
1258
 * @param $edit
1259
 *   The incoming POST data to populate the form element. If this is FALSE,
1260
 *   the element's default value should be returned.
1261
 * @return
1262
 *   The data that will appear in the $form_state['values'] collection
1263
 *   for this element. Return nothing to use the default.
1264
 */
12652366
function form_type_textfield_value($form, $edit = FALSE) {
12661272
  if ($edit !== FALSE) {
1267
    // Equate $edit to the form value to ensure it's marked for
1268
    // validation.
1269421
    return str_replace(array("\r", "\n"), '', $edit);
12700
  }
1271942
}
1272
1273
/**
1274
 * Helper function to determine the value for form's token value.
1275
 *
1276
 * @param $form
1277
 *   The form element whose value is being populated.
1278
 * @param $edit
1279
 *   The incoming POST data to populate the form element. If this is FALSE,
1280
 *   the element's default value should be returned.
1281
 * @return
1282
 *   The data that will appear in the $form_state['values'] collection
1283
 *   for this element. Return nothing to use the default.
1284
 */
12852366
function form_type_token_value($form, $edit = FALSE) {
1286913
  if ($edit !== FALSE) {
1287338
    return (string)$edit;
12880
  }
1289598
}
1290
1291
/**
1292
 * Change submitted form values during the form processing cycle.
1293
 *
1294
 * Use this function to change the submitted value of a form item in the
1295
 * validation phase so that it persists in $form_state through to the
1296
 * submission handlers in the submission phase.
1297
 *
1298
 * Since $form_state['values'] can either be a flat array of values, or a
tree
1299
 * of nested values, some care must be taken when using this function.
1300
 * Specifically, $form_item['#parents'] is an array that describes the
branch of
1301
 * the tree whose value should be updated. For example, if we wanted to
update
1302
 * $form_state['values']['one']['two'] to 'new value', we'd pass in
1303
 * $form_item['#parents'] = array('one', 'two') and $value = 'new value'.
1304
 *
1305
 * @param $form_item
1306
 *   The form item that should have its value updated. Keys used: #parents,
1307
 *   #value. In most cases you can just pass in the right element from the
$form
1308
 *   array.
1309
 * @param $value
1310
 *   The new value for the form item.
1311
 * @param $form_state
1312
 *   The array where the value change should be recorded.
1313
 */
13142366
function form_set_value($form_item, $value, &$form_state) {
13151648
  _form_set_value($form_state['values'], $form_item,
$form_item['#parents'], $value);
13161648
}
1317
1318
/**
1319
 * Helper function for form_set_value().
1320
 *
1321
 * We iterate over $parents and create nested arrays for them
1322
 * in $form_state['values'] if needed. Then we insert the value into
1323
 * the right array.
1324
 */
13252366
function _form_set_value(&$form_values, $form_item, $parents, $value) {
13261648
  $parent = array_shift($parents);
13271648
  if (empty($parents)) {
13281648
    $form_values[$parent] = $value;
13291648
  }
1330
  else {
1331462
    if (!isset($form_values[$parent])) {
1332335
      $form_values[$parent] = array();
1333335
    }
1334462
    _form_set_value($form_values[$parent], $form_item, $parents, $value);
1335
  }
13361648
}
1337
1338
/**
1339
 * Retrieve the default properties for the defined element type.
1340
 */
13412366
function _element_info($type, $refresh = NULL) {
13421837
  static $cache;
1343
1344
  $basic_defaults = array(
13451837
    '#description' => NULL,
13461837
    '#attributes' => array(),
13471837
    '#required' => FALSE,
13481837
  );
13491837
  if (!isset($cache) || $refresh) {
13501837
    $cache = array();
13511837
    foreach (module_implements('elements') as $module) {
13521837
      $elements = module_invoke($module, 'elements');
13531837
      if (isset($elements) && is_array($elements)) {
13541837
        $cache = array_merge_recursive($cache, $elements);
13551837
      }
13561837
    }
13571837
    if (sizeof($cache)) {
13581837
      foreach ($cache as $element_type => $info) {
13591837
        $cache[$element_type] = array_merge_recursive($basic_defaults,
$info);
13601837
      }
13611837
    }
13621837
  }
1363
13641837
  return $cache[$type];
13650
}
1366
13672366
function form_options_flatten($array, $reset = TRUE) {
1368181
  static $return;
1369
1370181
  if ($reset) {
1371181
    $return = array();
1372181
  }
1373
1374181
  foreach ($array as $key => $value) {
1375181
    if (is_object($value)) {
137616
      form_options_flatten($value->option, FALSE);
137716
    }
1378181
    elseif (is_array($value)) {
137919
      form_options_flatten($value, FALSE);
138019
    }
1381
    else {
1382181
      $return[$key] = 1;
1383
    }
1384181
  }
1385
1386181
  return $return;
13870
}
1388
1389
/**
1390
 * Format a dropdown menu or scrolling selection box.
1391
 *
1392
 * @param $element
1393
 *   An associative array containing the properties of the element.
1394
 *   Properties used: title, value, options, description, extra, multiple,
required
1395
 * @return
1396
 *   A themed HTML string representing the form element.
1397
 *
1398
 * @ingroup themeable
1399
 *
1400
 * It is possible to group options together; to do this, change the format
of
1401
 * $options to an associative array in which the keys are group labels, and
the
1402
 * values are associative arrays in the normal $options format.
1403
 */
14042366
function theme_select($element) {
1405359
  $select = '';
1406359
  $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';
1407359
  _form_set_class($element, array('form-select'));
1408359
  $multiple = $element['#multiple'];
1409359
  return theme('form_element', $element, '<select name="' .
$element['#name'] . '' . ($multiple ? '[]' : '') . '"' . ($multiple ? '
multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) .
' id="' . $element['#id'] . '" ' . $size . '>' .
form_select_options($element) . '</select>');
14100
}
1411
14122366
function form_select_options($element, $choices = NULL) {
1413359
  if (!isset($choices)) {
1414359
    $choices = $element['#options'];
1415359
  }
1416
  // array_key_exists() accommodates the rare event where
$element['#value'] is NULL.
1417
  // isset() fails in this situation.
1418359
  $value_valid = isset($element['#value']) || array_key_exists('#value',
$element);
1419359
  $value_is_array = is_array($element['#value']);
1420359
  $options = '';
1421359
  foreach ($choices as $key => $choice) {
1422359
    if (is_array($choice)) {
142349
      $options .= '<optgroup label="' . $key . '">';
142449
      $options .= form_select_options($element, $choice);
142549
      $options .= '</optgroup>';
142649
    }
1427359
    elseif (is_object($choice)) {
142823
      $options .= form_select_options($element, $choice->option);
142923
    }
1430
    else {
1431359
      $key = (string)$key;
1432359
      if ($value_valid && (!$value_is_array && (string)$element['#value']
=== $key || ($value_is_array && in_array($key, $element['#value'])))) {
1433288
        $selected = ' selected="selected"';
1434288
      }
1435
      else {
1436357
        $selected = '';
1437
      }
1438359
      $options .= '<option value="' . check_plain($key) . '"' . $selected .
'>' . check_plain($choice) . '</option>';
1439
    }
1440359
  }
1441359
  return $options;
14420
}
1443
1444
/**
1445
 * Traverses a select element's #option array looking for any values
1446
 * that hold the given key. Returns an array of indexes that match.
1447
 *
1448
 * This function is useful if you need to modify the options that are
1449
 * already in a form element; for example, to remove choices which are
1450
 * not valid because of additional filters imposed by another module.
1451
 * One example might be altering the choices in a taxonomy selector.
1452
 * To correctly handle the case of a multiple hierarchy taxonomy,
1453
 * #options arrays can now hold an array of objects, instead of a
1454
 * direct mapping of keys to labels, so that multiple choices in the
1455
 * selector can have the same key (and label). This makes it difficult
1456
 * to manipulate directly, which is why this helper function exists.
1457
 *
1458
 * This function does not support optgroups (when the elements of the
1459
 * #options array are themselves arrays), and will return FALSE if
1460
 * arrays are found. The caller must either flatten/restore or
1461
 * manually do their manipulations in this case, since returning the
1462
 * index is not sufficient, and supporting this would make the
1463
 * "helper" too complicated and cumbersome to be of any help.
1464
 *
1465
 * As usual with functions that can return array() or FALSE, do not
1466
 * forget to use === and !== if needed.
1467
 *
1468
 * @param $element
1469
 *   The select element to search.
1470
 * @param $key
1471
 *   The key to look for.
1472
 * @return
1473
 *   An array of indexes that match the given $key. Array will be
1474
 *   empty if no elements were found. FALSE if optgroups were found.
1475
 */
14762366
function form_get_options($element, $key) {
14770
  $keys = array();
14780
  foreach ($element['#options'] as $index => $choice) {
14790
    if (is_array($choice)) {
14800
      return FALSE;
14810
    }
14820
    elseif (is_object($choice)) {
14830
      if (isset($choice->option[$key])) {
14840
        $keys[] = $index;
14850
      }
14860
    }
14870
    elseif ($index == $key) {
14880
      $keys[] = $index;
14890
    }
14900
  }
14910
  return $keys;
14920
}
1493
1494
/**
1495
 * Format a group of form items.
1496
 *
1497
 * @param $element
1498
 *   An associative array containing the properties of the element.
1499
 *   Properties used: attributes, title, value, description, children,
collapsible, collapsed
1500
 * @return
1501
 *   A themed HTML string representing the form item group.
1502
 *
1503
 * @ingroup themeable
1504
 */
15052366
function theme_fieldset($element) {
1506270
  if ($element['#collapsible']) {
1507112
    drupal_add_js('misc/collapse.js');
1508
1509112
    if (!isset($element['#attributes']['class'])) {
151091
      $element['#attributes']['class'] = '';
151191
    }
1512
1513112
    $element['#attributes']['class'] .= ' collapsible';
1514112
    if ($element['#collapsed']) {
151569
      $element['#attributes']['class'] .= ' collapsed';
151669
    }
1517112
  }
1518
1519270
  return '<fieldset' . drupal_attributes($element['#attributes']) . '>' .
($element['#title'] ? '<legend>' . $element['#title'] . '</legend>' : '') .
(isset($element['#description']) && $element['#description'] ? '<div
class="description">' . $element['#description'] . '</div>' : '') .
(!empty($element['#children']) ? $element['#children'] : '') .
(isset($element['#value']) ? $element['#value'] : '') . "</fieldset>\n";
15200
}
1521
1522
/**
1523
 * Format a radio button.
1524
 *
1525
 * @param $element
1526
 *   An associative array containing the properties of the element.
1527
 *   Properties used: required, return_value, value, attributes, title,
description
1528
 * @return
1529
 *   A themed HTML string representing the form item group.
1530
 *
1531
 * @ingroup themeable
1532
 */
15332366
function theme_radio($element) {
153460
  _form_set_class($element, array('form-radio'));
153560
  $output = '<input type="radio" ';
153660
  $output .= 'id="' . $element['#id'] . '" ';
153760
  $output .= 'name="' . $element['#name'] . '" ';
153860
  $output .= 'value="' . $element['#return_value'] . '" ';
153960
  $output .= (check_plain($element['#value']) == $element['#return_value'])
? ' checked="checked" ' : ' ';
154060
  $output .= drupal_attributes($element['#attributes']) . ' />';
154160
  if (!is_null($element['#title'])) {
154260
    $output = '<label class="option">' . $output . ' ' . $element['#title']
. '</label>';
154360
  }
1544
154560
  unset($element['#title']);
154660
  return theme('form_element', $element, $output);
15470
}
1548
1549
/**
1550
 * Format a set of radio buttons.
1551
 *
1552
 * @param $element
1553
 *   An associative array containing the properties of the element.
1554
 *   Properties used: title, value, options, description, required and
attributes.
1555
 * @return
1556
 *   A themed HTML string representing the radio button set.
1557
 *
1558
 * @ingroup themeable
1559
 */
15602366
function theme_radios($element) {
156149
  $class = 'form-radios';
156249
  if (isset($element['#attributes']['class'])) {
15630
    $class .= ' ' . $element['#attributes']['class'];
15640
  }
156549
  $element['#children'] = '<div class="' . $class . '">' .
(!empty($element['#children']) ? $element['#children'] : '') . '</div>';
156649
  if ($element['#title'] || $element['#description']) {
156731
    unset($element['#id']);
156831
    return theme('form_element', $element, $element['#children']);
15690
  }
1570
  else {
157118
    return $element['#children'];
1572
  }
15730
}
1574
1575
/**
1576
 * Format a password_confirm item.
1577
 *
1578
 * @param $element
1579
 *   An associative array containing the properties of the element.
1580
 *   Properties used: title, value, id, required, error.
1581
 * @return
1582
 *   A themed HTML string representing the form item.
1583
 *
1584
 * @ingroup themeable
1585
 */
15862366
function theme_password_confirm($element) {
158713
  return theme('form_element', $element, $element['#children']);
15880
}
1589
1590
/**
1591
 * Expand a password_confirm field into two text boxes.
1592
 */
15932366
function form_process_password_confirm($element) {
159419
  $element['pass1'] =  array(
159519
    '#type' => 'password',
159619
    '#title' => t('Password'),
159719
    '#value' => empty($element['#value']) ? NULL :
$element['#value']['pass1'],
159819
    '#required' => $element['#required'],
159919
    '#attributes' => array('class' => 'password-field'),
1600
  );
160119
  $element['pass2'] =  array(
160219
    '#type' => 'password',
160319
    '#title' => t('Confirm password'),
160419
    '#value' => empty($element['#value']) ? NULL :
$element['#value']['pass2'],
160519
    '#required' => $element['#required'],
160619
    '#attributes' => array('class' => 'password-confirm'),
1607
  );
160819
  $element['#element_validate'] = array('password_confirm_validate');
160919
  $element['#tree'] = TRUE;
1610
161119
  if (isset($element['#size'])) {
161218
    $element['pass1']['#size'] = $element['pass2']['#size'] =
$element['#size'];
161318
  }
1614
161519
  return $element;
16160
}
1617
1618
/**
1619
 * Validate password_confirm element.
1620
 */
16212366
function password_confirm_validate($form, &$form_state) {
16228
  $pass1 = trim($form['pass1']['#value']);
16238
  if (!empty($pass1)) {
16242
    $pass2 = trim($form['pass2']['#value']);
16252
    if ($pass1 != $pass2) {
16260
      form_error($form, t('The specified passwords do not match.'));
16270
    }
16282
  }
16296
  elseif ($form['#required'] && !empty($form['#post'])) {
16301
    form_error($form, t('Password field is required.'));
16311
  }
1632
1633
  // Password field must be converted from a two-element array into a
single
1634
  // string regardless of validation results.
16358
  form_set_value($form['pass1'], NULL, $form_state);
16368
  form_set_value($form['pass2'], NULL, $form_state);
16378
  form_set_value($form, $pass1, $form_state);
1638
16398
  return $form;
1640
16410
}
1642
1643
/**
1644
 * Format a date selection element.
1645
 *
1646
 * @param $element
1647
 *   An associative array containing the properties of the element.
1648
 *   Properties used: title, value, options, description, required and
attributes.
1649
 * @return
1650
 *   A themed HTML string representing the date selection boxes.
1651
 *
1652
 * @ingroup themeable
1653
 */
16542366
function theme_date($element) {
16553
  return theme('form_element', $element, '<div class="container-inline">' .
$element['#children'] . '</div>');
16560
}
1657
1658
/**
1659
 * Roll out a single date element.
1660
 */
16612366
function form_process_date($element) {
1662
  // Default to current date
16634
  if (empty($element['#value'])) {
16642
    $element['#value'] = array('day' => format_date(REQUEST_TIME, 'custom',
'j'),
16652
                            'month' => format_date(REQUEST_TIME, 'custom',
'n'),
16662
                            'year' => format_date(REQUEST_TIME, 'custom',
'Y'));
16672
  }
1668
16694
  $element['#tree'] = TRUE;
1670
1671
  // Determine the order of day, month, year in the site's chosen date
format.
16724
  $format = variable_get('date_format_short', 'm/d/Y - H:i');
16734
  $sort = array();
16744
  $sort['day'] = max(strpos($format, 'd'), strpos($format, 'j'));
16754
  $sort['month'] = max(strpos($format, 'm'), strpos($format, 'M'));
16764
  $sort['year'] = strpos($format, 'Y');
16774
  asort($sort);
16784
  $order = array_keys($sort);
1679
1680
  // Output multi-selector for date.
16814
  foreach ($order as $type) {
1682
    switch ($type) {
16834
      case 'day':
16844
        $options = drupal_map_assoc(range(1, 31));
16854
        break;
16864
      case 'month':
16874
        $options = drupal_map_assoc(range(1, 12), 'map_month');
16884
        break;
16894
      case 'year':
16904
        $options = drupal_map_assoc(range(1900, 2050));
16914
        break;
16920
    }
16934
    $parents = $element['#parents'];
16944
    $parents[] = $type;
16954
    $element[$type] = array(
16964
      '#type' => 'select',
16974
      '#value' => $element['#value'][$type],
16984
      '#attributes' => $element['#attributes'],
16994
      '#options' => $options,
1700
    );
17014
  }
1702
17034
  return $element;
17040
}
1705
1706
/**
1707
 * Validates the date type to stop dates like February 30, 2006.
1708
 */
17092366
function date_validate($form) {
17101
  if (!checkdate($form['#value']['month'], $form['#value']['day'],
$form['#value']['year'])) {
17110
    form_error($form, t('The specified date is invalid.'));
17120
  }
17131
}
1714
1715
/**
1716
 * Helper function for usage with drupal_map_assoc to display month names.
1717
 */
17182366
function map_month($month) {
17195
  return format_date(gmmktime(0, 0, 0, $month, 2, 1970), 'custom', 'M', 0);
17200
}
1721
1722
/**
1723
 * If no default value is set for weight select boxes, use 0.
1724
 */
17252366
function weight_value(&$form) {
17260
  if (isset($form['#default_value'])) {
17270
    $form['#value'] = $form['#default_value'];
17280
  }
1729
  else {
17300
    $form['#value'] = 0;
1731
  }
17320
}
1733
1734
/**
1735
 * Roll out a single radios element to a list of radios,
1736
 * using the options array as index.
1737
 */
17382366
function form_process_radios($element) {
1739237
  if (count($element['#options']) > 0) {
1740237
    foreach ($element['#options'] as $key => $choice) {
1741237
      if (!isset($element[$key])) {
1742
        // Generate the parents as the autogenerator does, so we will have
a
1743
        // unique id for each radio button.
1744237
        $parents_for_id = array_merge($element['#parents'], array($key));
1745237
        $element[$key] = array(
1746237
          '#type' => 'radio',
1747237
          '#title' => $choice,
1748237
          '#return_value' => check_plain($key),
1749237
          '#default_value' => isset($element['#default_value']) ?
$element['#default_value'] : NULL,
1750237
          '#attributes' => $element['#attributes'],
1751237
          '#parents' => $element['#parents'],
1752237
          '#id' => form_clean_id('edit-' . implode('-', $parents_for_id)),
1753237
          '#ahah' => isset($element['#ahah']) ? $element['#ahah'] : NULL,
1754
        );
1755237
      }
1756237
    }
1757237
  }
1758237
  return $element;
17590
}
1760
1761
/**
1762
 * Add input format selector to text elements with the #input_format
property.
1763
 *
1764
 * The #input_format property should be the ID of an input format, found in
1765
 * {filter_formats}.format, which gets passed to filter_form().
1766
 *
1767
 * If the property #input_format is set, the form element will be expanded
into
1768
 * two separate form elements, one holding the content of the element, and
the
1769
 * other holding the input format selector. The original element is shifted
into
1770
 * a child element, but is otherwise unaltered, so that the format selector
is
1771
 * at the same level as the text field which it affects.
1772
 *
1773
 * For example:
1774
 * @code
1775
 *   // A simple textarea, such as a node body.
1776
 *   $form['body'] = array(
1777
 *     '#type' => 'textarea',
1778
 *     '#title' => t('Body'),
1779
 *     '#input_format' => isset($node->format) ? $node->format :
FILTER_FORMAT_DEFAULT,
1780
 *   );
1781
 * @endcode
1782
 *
1783
 * Becomes:
1784
 * @code
1785
 *   $form['body'] = array(
1786
 *     // Type switches to 'markup', as we're only interested in submitting
the child elements.
1787
 *     '#type' => 'markup',
1788
 *     // 'value' holds the original element.
1789
 *     'value' => array(
1790
 *       '#type' => 'textarea',
1791
 *       '#title' => t('Body'),
1792
 *       '#parents' => array('body'),
1793
 *     ),
1794
 *     // 'format' holds the input format selector.
1795
 *     'format' => array(
1796
 *       '#parents' => array('body_format'),
1797
 *       ...
1798
 *     ),
1799
 *   );
1800
 * @endcode
1801
 *
1802
 * And would result in:
1803
 * @code
1804
 *   // Original, unaltered form element value.
1805
 *   $form_state['values']['body'] = 'Example content';
1806
 *   // Chosen input format.
1807
 *   $form_state['values']['body_format'] = 1;
1808
 * @endcode
1809
 *
1810
 * @see system_elements(), filter_form()
1811
 */
18122366
function form_process_input_format($element) {
18131292
  if (isset($element['#input_format'])) {
1814
    // Determine the form element parents and element name to use for the
input
1815
    // format widget. This simulates the 'element' and 'element_format'
pair of
1816
    // parents that filter_form() expects.
1817198
    $element_parents = $element['#parents'];
1818198
    $element_name = array_pop($element_parents);
1819198
    $element_parents[] = $element_name . '_format';
1820
1821
    // We need to break references, otherwise form_builder recurses
infinitely.
1822198
    $element['value'] = (array)$element;
1823198
    $element['#type'] = 'markup';
1824198
    $element['format'] = filter_form($element['#input_format'], 1,
$element_parents);
1825
1826
    // We need to clear the #input_format from the new child otherwise we
1827
    // would get into an infinite loop.
1828198
    unset($element['value']['#input_format']);
1829198
    $element['value']['#weight'] = 0;
1830198
  }
18311292
  return $element;
18320
}
1833
1834
/**
1835
 * Add AHAH information about a form element to the page to communicate
with
1836
 * javascript. If #ahah[path] is set on an element, this additional
javascript is
1837
 * added to the page header to attach the AHAH behaviors. See ahah.js for
more
1838
 * information.
1839
 *
1840
 * @param $element
1841
 *   An associative array containing the properties of the element.
1842
 *   Properties used: ahah_event, ahah_path, ahah_wrapper, ahah_parameters,
1843
 *   ahah_effect.
1844
 * @return
1845
 *   None. Additional code is added to the header of the page using
1846
 *   drupal_add_js.
1847
 */
18482366
function form_process_ahah($element) {
18491648
  static $js_added = array();
1850
  // Add a reasonable default event handler if none specified.
18511648
  if (isset($element['#ahah']['path']) &&
!isset($element['#ahah']['event'])) {
185239
    switch ($element['#type']) {
185339
      case 'submit':
185439
      case 'button':
185539
      case 'image_button':
1856
        // Use the mousedown instead of the click event because form
1857
        // submission via pressing the enter key triggers a click event on
1858
        // submit inputs, inappropriately triggering AHAH behaviors.
185925
        $element['#ahah']['event'] = 'mousedown';
1860
        // Attach an additional event handler so that AHAH behaviours
1861
        // can be triggered still via keyboard input.
186225
        $element['#ahah']['keypress'] = TRUE;
186325
        break;
186414
      case 'password':
186514
      case 'textfield':
186614
      case 'textarea':
18670
        $element['#ahah']['event'] = 'blur';
18680
        break;
186914
      case 'radio':
187014
      case 'checkbox':
187114
      case 'select':
187214
        $element['#ahah']['event'] = 'change';
187314
        break;
18740
      default:
18750
        return $element;
18760
    }
187739
  }
1878
1879
  // Adding the same javascript settings twice will cause a recursion
error,
1880
  // we avoid the problem by checking if the javascript has already been
added.
18811648
  if (isset($element['#ahah']['path']) && isset($element['#ahah']['event'])
&& !isset($js_added[$element['#id']])) {
188239
    drupal_add_js('misc/jquery.form.js');
188339
    drupal_add_js('misc/ahah.js');
1884
1885
    $ahah_binding = array(
188639
      'url'      => url($element['#ahah']['path']),
188739
      'event'    => $element['#ahah']['event'],
188839
      'keypress' => empty($element['#ahah']['keypress']) ? NULL :
$element['#ahah']['keypress'],
188939
      'wrapper'  => empty($element['#ahah']['wrapper']) ? NULL :
$element['#ahah']['wrapper'],
189039
      'selector' => empty($element['#ahah']['selector']) ? '#' .
$element['#id'] : $element['#ahah']['selector'],
189139
      'effect'   => empty($element['#ahah']['effect']) ? 'none' :
$element['#ahah']['effect'],
189239
      'method'   => empty($element['#ahah']['method']) ? 'replace' :
$element['#ahah']['method'],
189339
      'progress' => empty($element['#ahah']['progress']) ? array('type' =>
'throbber') : $element['#ahah']['progress'],
189439
      'button'   => isset($element['#executes_submit_callback']) ?
array($element['#name'] => $element['#value']) : FALSE,
189539
    );
1896
1897
    // Convert a simple #ahah[progress] type string into an array.
189839
    if (is_string($ahah_binding['progress'])) {
18990
      $ahah_binding['progress'] = array('type' =>
$ahah_binding['progress']);
19000
    }
1901
    // Change progress path to a full url.
190239
    if (isset($ahah_binding['progress']['path'])) {
19030
      $ahah_binding['progress']['url'] =
url($ahah_binding['progress']['path']);
19040
    }
1905
1906
    // Add progress.js if we're doing a bar display.
190739
    if ($ahah_binding['progress']['type'] == 'bar') {
190814
      drupal_add_js('misc/progress.js');
190914
    }
1910
191139
    drupal_add_js(array('ahah' => array($element['#id'] => $ahah_binding)),
'setting');
1912
191339
    $js_added[$element['#id']] = TRUE;
191439
    $element['#cache'] = TRUE;
191539
  }
19161648
  return $element;
19170
}
1918
1919
/**
1920
 * Format a form item.
1921
 *
1922
 * @param $element
1923
 *   An associative array containing the properties of the element.
1924
 *   Properties used:  title, value, description, required, error
1925
 * @return
1926
 *   A themed HTML string representing the form item.
1927
 *
1928
 * @ingroup themeable
1929
 */
19302366
function theme_item($element) {
193156
  return theme('form_element', $element, $element['#markup'] .
(!empty($element['#children']) ? $element['#children'] : ''));
19320
}
1933
1934
/**
1935
 * Format a checkbox.
1936
 *
1937
 * @param $element
1938
 *   An associative array containing the properties of the element.
1939
 *   Properties used:  title, value, return_value, description, required
1940
 * @return
1941
 *   A themed HTML string representing the checkbox.
1942
 *
1943
 * @ingroup themeable
1944
 */
19452366
function theme_checkbox($element) {
1946272
  _form_set_class($element, array('form-checkbox'));
1947272
  $checkbox = '<input ';
1948272
  $checkbox .= 'type="checkbox" ';
1949272
  $checkbox .= 'name="' . $element['#name'] . '" ';
1950272
  $checkbox .= 'id="' . $element['#id'] . '" ' ;
1951272
  $checkbox .= 'value="' . $element['#return_value'] . '" ';
1952272
  $checkbox .= $element['#value'] ? ' checked="checked" ' : ' ';
1953272
  $checkbox .= drupal_attributes($element['#attributes']) . ' />';
1954
1955272
  if (!is_null($element['#title'])) {
1956232
    $checkbox = '<label class="option">' . $checkbox . ' ' .
$element['#title'] . '</label>';
1957232
  }
1958
1959272
  unset($element['#title']);
1960272
  return theme('form_element', $element, $checkbox);
19610
}
1962
1963
/**
1964
 * Format a set of checkboxes.
1965
 *
1966
 * @param $element
1967
 *   An associative array containing the properties of the element.
1968
 * @return
1969
 *   A themed HTML string representing the checkbox set.
1970
 *
1971
 * @ingroup themeable
1972
 */
19732366
function theme_checkboxes($element) {
197481
  $class = 'form-checkboxes';
197581
  if (isset($element['#attributes']['class'])) {
19760
    $class .= ' ' . $element['#attributes']['class'];
19770
  }
197881
  $element['#children'] = '<div class="' . $class . '">' .
(!empty($element['#children']) ? $element['#children'] : '') . '</div>';
197981
  if ($element['#title'] || $element['#description']) {
198032
    unset($element['#id']);
198132
    return theme('form_element', $element, $element['#children']);
19820
  }
1983
  else {
198449
    return $element['#children'];
1985
  }
19860
}
1987
19882366
function form_process_checkboxes($element) {
1989114
  $value = is_array($element['#value']) ? $element['#value'] : array();
1990114
  $element['#tree'] = TRUE;
1991114
  if (count($element['#options']) > 0) {
1992114
    if (!isset($element['#default_value']) || $element['#default_value'] ==
0) {
199352
      $element['#default_value'] = array();
199452
    }
1995114
    foreach ($element['#options'] as $key => $choice) {
1996114
      if (!isset($element[$key])) {
1997114
        $element[$key] = array(
1998114
          '#type' => 'checkbox',
1999114
          '#processed' => TRUE,
2000114
          '#title' => $choice,
2001114
          '#return_value' => $key,
2002114
          '#default_value' => isset($value[$key]),
2003114
          '#attributes' => $element['#attributes'],
2004114
          '#ahah' => isset($element['#ahah']) ? $element['#ahah'] : NULL,
2005
        );
2006114
      }
2007114
    }
2008114
  }
2009114
  return $element;
20100
}
2011
2012
/**
2013
 * Theme a form submit button.
2014
 *
2015
 * @ingroup themeable
2016
 */
20172366
function theme_submit($element) {
20181136
  return theme('button', $element);
20190
}
2020
2021
/**
2022
 * Theme a form button.
2023
 *
2024
 * @ingroup themeable
2025
 */
20262366
function theme_button($element) {
2027
  // Make sure not to overwrite classes.
20281146
  if (isset($element['#attributes']['class'])) {
20290
    $element['#attributes']['class'] = 'form-' . $element['#button_type'] .
' ' . $element['#attributes']['class'];
20300
  }
2031
  else {
20321146
    $element['#attributes']['class'] = 'form-' . $element['#button_type'];
2033
  }
2034
20351146
  return '<input type="submit" ' . (empty($element['#name']) ? '' :
'name="' . $element['#name'] . '" ') . 'id="' . $element['#id'] . '"
value="' . check_plain($element['#value']) . '" ' .
drupal_attributes($element['#attributes']) . " />\n";
20360
}
2037
2038
/**
2039
 * Theme a form image button.
2040
 *
2041
 * @ingroup themeable
2042
 */
20432366
function theme_image_button($element) {
2044
  // Make sure not to overwrite classes.
20450
  if (isset($element['#attributes']['class'])) {
20460
    $element['#attributes']['class'] = 'form-' . $element['#button_type'] .
' ' . $element['#attributes']['class'];
20470
  }
2048
  else {
20490
    $element['#attributes']['class'] = 'form-' . $element['#button_type'];
2050
  }
2051
20520
  return '<input type="image" name="' . $element['#name'] . '" ' .
20530
    (!empty($element['#value']) ? ('value="' .
check_plain($element['#value']) . '" ') : '') .
20540
    'id="' . $element['#id'] . '" ' .
20550
    drupal_attributes($element['#attributes']) .
20560
    ' src="' . base_path() . $element['#src'] . '" ' .
20570
    (!empty($element['#title']) ? 'alt="' . check_plain($element['#title'])
. '" title="' . check_plain($element['#title']) . '" ' : '' ) .
20580
    "/>\n";
20590
}
2060
2061
/**
2062
 * Format a hidden form field.
2063
 *
2064
 * @param $element
2065
 *   An associative array containing the properties of the element.
2066
 * @return
2067
 *   A themed HTML string representing the hidden form field.
2068
 *
2069
 * @ingroup themeable
2070
 */
20712366
function theme_hidden($element) {
20721179
  return '<input type="hidden" name="' . $element['#name'] . '" id="' .
$element['#id'] . '" value="' . check_plain($element['#value']) . "\" " .
drupal_attributes($element['#attributes']) . " />\n";
20730
}
2074
2075
/**
2076
 * Format a form token.
2077
 *
2078
 * @ingroup themeable
2079
 */
20802366
function theme_token($element) {
2081632
  return theme('hidden', $element);
20820
}
2083
2084
/**
2085
 * Format a textfield.
2086
 *
2087
 * @param $element
2088
 *   An associative array containing the properties of the element.
2089
 *   Properties used:  title, value, description, size, maxlength,
required, attributes autocomplete_path
2090
 * @return
2091
 *   A themed HTML string representing the textfield.
2092
 *
2093
 * @ingroup themeable
2094
 */
20952366
function theme_textfield($element) {
2096878
  $size = empty($element['#size']) ? '' : ' size="' . $element['#size'] .
'"';
2097878
  $maxlength = empty($element['#maxlength']) ? '' : ' maxlength="' .
$element['#maxlength'] . '"';
2098878
  $class = array('form-text');
2099878
  $extra = '';
2100878
  $output = '';
2101
2102878
  if ($element['#autocomplete_path'] && menu_valid_path(array('link_path'
=> $element['#autocomplete_path']))) {
210318
    drupal_add_js('misc/autocomplete.js');
210418
    $class[] = 'form-autocomplete';
210518
    $extra =  '<input class="autocomplete" type="hidden" id="' .
$element['#id'] . '-autocomplete" value="' .
check_url(url($element['#autocomplete_path'], array('absolute' => TRUE))) .
'" disabled="disabled" />';
210618
  }
2107878
  _form_set_class($element, $class);
2108
2109878
  if (isset($element['#field_prefix'])) {
211012
    $output .= '<span class="field-prefix">' . $element['#field_prefix'] .
'</span> ';
211112
  }
2112
2113878
  $output .= '<input type="text"' . $maxlength . ' name="' .
$element['#name'] . '" id="' . $element['#id'] . '"' . $size . ' value="' .
check_plain($element['#value']) . '"' .
drupal_attributes($element['#attributes']) . ' />';
2114
2115878
  if (isset($element['#field_suffix'])) {
21166
    $output .= ' <span class="field-suffix">' . $element['#field_suffix'] .
'</span>';
21176
  }
2118
2119878
  return theme('form_element', $element, $output) . $extra;
21200
}
2121
2122
/**
2123
 * Format a form.
2124
 *
2125
 * @param $element
2126
 *   An associative array containing the properties of the element.
2127
 *   Properties used: action, method, attributes, children
2128
 * @return
2129
 *   A themed HTML string representing the form.
2130
 *
2131
 * @ingroup themeable
2132
 */
21332366
function theme_form($element) {
2134
  // Anonymous div to satisfy XHTML compliance.
21351179
  $action = $element['#action'] ? 'action="' .
check_url($element['#action']) . '" ' : '';
21361179
  return '<form ' . $action . ' accept-charset="UTF-8" method="' .
$element['#method'] . '" id="' . $element['#id'] . '"' .
drupal_attributes($element['#attributes']) . ">\n<div>" .
$element['#children'] . "\n</div></form>\n";
21370
}
2138
2139
/**
2140
 * Format a textarea.
2141
 *
2142
 * @param $element
2143
 *   An associative array containing the properties of the element.
2144
 *   Properties used: title, value, description, rows, cols, required,
attributes
2145
 * @return
2146
 *   A themed HTML string representing the textarea.
2147
 *
2148
 * @ingroup themeable
2149
 */
21502366
function theme_textarea($element) {
2151234
  $class = array('form-textarea');
2152
2153
  // Add teaser behavior (must come before resizable)
2154234
  if (!empty($element['#teaser'])) {
215585
    drupal_add_js('misc/teaser.js');
2156
    // Note: arrays are merged in drupal_get_js().
215785
    drupal_add_js(array('teaserCheckbox' => array($element['#id'] =>
$element['#teaser_checkbox'])), 'setting');
215885
    drupal_add_js(array('teaser' => array($element['#id'] =>
$element['#teaser'])), 'setting');
215985
    $class[] = 'teaser';
216085
  }
2161
2162
  // Add resizable behavior
2163234
  if ($element['#resizable'] !== FALSE) {
2164234
    drupal_add_js('misc/textarea.js');
2165234
    $class[] = 'resizable';
2166234
  }
2167
2168234
  _form_set_class($element, $class);
2169234
  return theme('form_element', $element, '<textarea cols="' .
$element['#cols'] . '" rows="' . $element['#rows'] . '" name="' .
$element['#name'] . '" id="' . $element['#id'] . '" ' .
drupal_attributes($element['#attributes']) . '>' .
check_plain($element['#value']) . '</textarea>');
21700
}
2171
2172
/**
2173
 * Format HTML markup for use in forms.
2174
 *
2175
 * This is used in more advanced forms, such as theme selection and filter
format.
2176
 *
2177
 * @param $element
2178
 *   An associative array containing the properties of the element.
2179
 *   Properties used: value, children.
2180
 * @return
2181
 *   A themed HTML string representing the HTML markup.
2182
 *
2183
 * @ingroup themeable
2184
 */
2185
21862366
function theme_markup($element) {
21871104
  return (isset($element['#markup']) ? $element['#markup'] : '') .
(isset($element['#children']) ? $element['#children'] : '');
21880
}
2189
2190
/**
2191
 * Format a password field.
2192
 *
2193
 * @param $element
2194
 *   An associative array containing the properties of the element.
2195
 *   Properties used:  title, value, description, size, maxlength,
required, attributes
2196
 * @return
2197
 *   A themed HTML string representing the form.
2198
 *
2199
 * @ingroup themeable
2200
 */
22012366
function theme_password($element) {
2202557
  $size = $element['#size'] ? ' size="' . $element['#size'] . '" ' : '';
2203557
  $maxlength = $element['#maxlength'] ? ' maxlength="' .
$element['#maxlength'] . '" ' : '';
2204
2205557
  _form_set_class($element, array('form-text'));
2206557
  $output = '<input type="password" name="' . $element['#name'] . '" id="'
. $element['#id'] . '" ' . $maxlength . $size .
drupal_attributes($element['#attributes']) . ' />';
2207557
  return theme('form_element', $element, $output);
22080
}
2209
2210
/**
2211
 * Expand weight elements into selects.
2212
 */
22132366
function form_process_weight($element) {
2214346
  for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) {
2215346
    $weights[$n] = $n;
2216346
  }
2217346
  $element['#options'] = $weights;
2218346
  $element['#type'] = 'select';
2219346
  $element['#is_weight'] = TRUE;
2220346
  $element += _element_info('select');
2221346
  return $element;
22220
}
2223
2224
/**
2225
 * Format a file upload field.
2226
 *
2227
 * @param $title
2228
 *   The label for the file upload field.
2229
 * @param $name
2230
 *   The internal name used to refer to the field.
2231
 * @param $size
2232
 *   A measure of the visible size of the field (passed directly to HTML).
2233
 * @param $description
2234
 *   Explanatory text to display after the form item.
2235
 * @param $required
2236
 *   Whether the user must upload a file to the field.
2237
 * @return
2238
 *   A themed HTML string representing the field.
2239
 *
2240
 * @ingroup themeable
2241
 *
2242
 * For assistance with handling the uploaded file correctly, see the API
2243
 * provided by file.inc.
2244
 */
22452366
function theme_file($element) {
224629
  _form_set_class($element, array('form-file'));
224729
  return theme('form_element', $element, '<input type="file" name="' .
$element['#name'] . '"' . ($element['#attributes'] ? ' ' .
drupal_attributes($element['#attributes']) : '') . ' id="' .
$element['#id'] . '" size="' . $element['#size'] . "\" />\n");
22480
}
2249
2250
/**
2251
 * Return a themed form element.
2252
 *
2253
 * @param element
2254
 *   An associative array containing the properties of the element.
2255
 *   Properties used: title, description, id, required
2256
 * @param $value
2257
 *   The form element's data.
2258
 * @return
2259
 *   A string representing the form element.
2260
 *
2261
 * @ingroup themeable
2262
 */
22632366
function theme_form_element($element, $value) {
2264
  // This is also used in the installer, pre-database setup.
22651090
  $t = get_t();
2266
22671090
  $output = '<div class="form-item"';
22681090
  if (!empty($element['#id'])) {
22691090
    $output .= ' id="' . $element['#id'] . '-wrapper"';
22701090
  }
22711090
  $output .= ">\n";
22721090
  $required = !empty($element['#required']) ? '<span class="form-required"
title="' . $t('This field is required.') . '">*</span>' : '';
2273
22741090
  if (!empty($element['#title'])) {
2275909
    $title = $element['#title'];
2276909
    if (!empty($element['#id'])) {
2277909
      $output .= ' <label for="' . $element['#id'] . '">' . $t('!title:
!required', array('!title' => filter_xss_admin($title), '!required' =>
$required)) . "</label>\n";
2278909
    }
2279
    else {
228086
      $output .= ' <label>' . $t('!title: !required', array('!title' =>
filter_xss_admin($title), '!required' => $required)) . "</label>\n";
2281
    }
2282909
  }
2283
22841090
  $output .= " $value\n";
2285
22861090
  if (!empty($element['#description'])) {
2287586
    $output .= ' <div class="description">' . $element['#description'] .
"</div>\n";
2288586
  }
2289
22901090
  $output .= "</div>\n";
2291
22921090
  return $output;
22930
}
2294
2295
/**
2296
 * Sets a form element's class attribute.
2297
 *
2298
 * Adds 'required' and 'error' classes as needed.
2299
 *
2300
 * @param &$element
2301
 *   The form element.
2302
 * @param $name
2303
 *   Array of new class names to be added.
2304
 */
23052366
function _form_set_class(&$element, $class = array()) {
23061090
  if ($element['#required']) {
2307790
    $class[] = 'required';
2308790
  }
23091090
  if (form_get_error($element)) {
231029
    $class[] = 'error';
231129
  }
23121090
  if (isset($element['#attributes']['class'])) {
2313139
    $class[] = $element['#attributes']['class'];
2314139
  }
23151090
  $element['#attributes']['class'] = implode(' ', $class);
23161090
}
2317
2318
/**
2319
 * Prepare an HTML ID attribute string for a form item.
2320
 *
2321
 * Remove invalid characters and guarantee uniqueness.
2322
 *
2323
 * @param $id
2324
 *   The ID to clean.
2325
 * @param $flush
2326
 *   If set to TRUE, the function will flush and reset the static array
2327
 *   which is built to test the uniqueness of element IDs. This is only
2328
 *   used if a form has completed the validation process. This parameter
2329
 *   should never be set to TRUE if this function is being called to
2330
 *   assign an ID to the #ID element.
2331
 * @return
2332
 *   The cleaned ID.
2333
 */
23342366
function form_clean_id($id = NULL, $flush = FALSE) {
23352212
  static $seen_ids = array();
2336
23372212
  if ($flush) {
2338540
    $seen_ids = array();
2339540
    return;
23400
  }
23412212
  $id = str_replace(array('][', '_', ' '), '-', $id);
2342
2343
  // Ensure IDs are unique. The first occurrence is held but left alone.
2344
  // Subsequent occurrences get a number appended to them. This
incrementing
2345
  // will almost certainly break code that relies on explicit HTML IDs in
2346
  // forms that appear more than once on the page, but the alternative is
2347
  // outputting duplicate IDs, which would break JS code and XHTML
2348
  // validity anyways. For now, it's an acceptable stopgap solution.
23492212
  if (isset($seen_ids[$id])) {
2350102
    $id = $id . '-' . $seen_ids[$id]++;
2351102
  }
2352
  else {
23532212
    $seen_ids[$id] = 1;
2354
  }
2355
23562212
  return $id;
23570
}
2358
2359
/**
2360
 * @} End of "defgroup form_api".
2361
 */
2362
2363
/**
2364
 * @defgroup batch Batch operations
2365
 * @{
2366
 * Functions allowing forms processing to be spread out over several page
2367
 * requests, thus ensuring that the processing does not get interrupted
2368
 * because of a PHP timeout, while allowing the user to receive feedback
2369
 * on the progress of the ongoing operations.
2370
 *
2371
 * The API is primarily designed to integrate nicely with the Form API
2372
 * workflow, but can also be used by non-FAPI scripts (like update.php)
2373
 * or even simple page callbacks (which should probably be used sparingly).
2374
 *
2375
 * Example:
2376
 * @code
2377
 * $batch = array(
2378
 *   'title' => t('Exporting'),
2379
 *   'operations' => array(
2380
 *     array('my_function_1', array($account->uid, 'story')),
2381
 *     array('my_function_2', array()),
2382
 *   ),
2383
 *   'finished' => 'my_finished_callback',
2384
 * );
2385
 * batch_set($batch);
2386
 * // only needed if not inside a form _submit handler :
2387
 * batch_process();
2388
 * @endcode
2389
 *
2390
 * Note - if the batch 'title', 'init_message', 'progress_message',
2391
 * or 'error_message' could contain any user input, it is the
responsibility of
2392
 * the code calling batch_set() to sanitize them first with a function like
2393
 * check_plain() or filter_xss().
2394
 *
2395
 * Sample batch operations:
2396
 * @code
2397
 * // Simple and artificial: load a node of a given type for a given user
2398
 * function my_function_1($uid, $type, &$context) {
2399
 *   // The $context array gathers batch context information about the
execution (read),
2400
 *   // as well as 'return values' for the current operation (write)
2401
 *   // The following keys are provided :
2402
 *   // 'results' (read / write): The array of results gathered so far by
2403
 *   //   the batch processing, for the current operation to append its
own.
2404
 *   // 'message' (write): A text message displayed in the progress page.
2405
 *   // The following keys allow for multi-step operations :
2406
 *   // 'sandbox' (read / write): An array that can be freely used to
2407
 *   //   store persistent data between iterations. It is recommended to
2408
 *   //   use this instead of $_SESSION, which is unsafe if the user
2409
 *   //   continues browsing in a separate window while the batch is
processing.
2410
 *   // 'finished' (write): A float number between 0 and 1 informing
2411
 *   //   the processing engine of the completion level for the operation.
2412
 *   //   1 (or no value explicitly set) means the operation is finished
2413
 *   //   and the batch processing can continue to the next operation.
2414
 *
2415
 *   $node = node_load(array('uid' => $uid, 'type' => $type));
2416
 *   $context['results'][] = $node->nid . ' : ' . $node->title;
2417
 *   $context['message'] = $node->title;
2418
 * }
2419
 *
2420
 * // More advanced example: multi-step operation - load all nodes, five by
five
2421
 * function my_function_2(&$context) {
2422
 *   if (empty($context['sandbox'])) {
2423
 *     $context['sandbox']['progress'] = 0;
2424
 *     $context['sandbox']['current_node'] = 0;
2425
 *     $context['sandbox']['max'] = db_result(db_query('SELECT
COUNT(DISTINCT nid) FROM {node}'));
2426
 *   }
2427
 *   $limit = 5;
2428
 *   $result = db_query_range("SELECT nid FROM {node} WHERE nid > %d ORDER
BY nid ASC", $context['sandbox']['current_node'], 0, $limit);
2429
 *   while ($row = db_fetch_array($result)) {
2430
 *     $node = node_load($row['nid'], NULL, TRUE);
2431
 *     $context['results'][] = $node->nid . ' : ' . $node->title;
2432
 *     $context['sandbox']['progress']++;
2433
 *     $context['sandbox']['current_node'] = $node->nid;
2434
 *     $context['message'] = $node->title;
2435
 *   }
2436
 *   if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
2437
 *     $context['finished'] = $context['sandbox']['progress'] /
$context['sandbox']['max'];
2438
 *   }
2439
 * }
2440
 * @endcode
2441
 *
2442
 * Sample 'finished' callback:
2443
 * @code
2444
 * function batch_test_finished($success, $results, $operations) {
2445
 *   if ($success) {
2446
 *     $message = format_plural(count($results), 'One post processed.',
'@count posts processed.');
2447
 *   }
2448
 *   else {
2449
 *     $message = t('Finished with an error.');
2450
 *   }
2451
 *   drupal_set_message($message);
2452
 *   // Providing data for the redirected page is done through $_SESSION.
2453
 *   foreach ($results as $result) {
2454
 *     $items[] = t('Loaded node %title.', array('%title' => $result));
2455
 *   }
2456
 *   $_SESSION['my_batch_results'] = $items;
2457
 * }
2458
 * @endcode
2459
 */
2460
2461
/**
2462
 * Open a new batch.
2463
 *
2464
 * @param $batch
2465
 *   An array defining the batch. The following keys can be used:
2466
 *     'operations': an array of function calls to be performed.
2467
 *        Example:
2468
 *        @code
2469
 *        array(
2470
 *          array('my_function_1', array($arg1)),
2471
 *          array('my_function_2', array($arg2_1, $arg2_2)),
2472
 *        )
2473
 *        @endcode
2474
 *     All the other values below are optional.
2475
 *     batch_init() provides default values for the messages.
2476
 *     'title': title for the progress page.
2477
 *       Defaults to t('Processing').
2478
 *     'init_message': message displayed while the processing is
initialized.
2479
 *       Defaults to t('Initializing.').
2480
 *     'progress_message': message displayed while processing the batch.
2481
 *       Available placeholders are @current, @remaining, @total and
@percent.
2482
 *       Defaults to t('Remaining @remaining of @total.').
2483
 *     'error_message': message displayed if an error occurred while
processing
2484
 *       the batch.
2485
 *       Defaults to t('An error has occurred.').
2486
 *     'finished': the name of a function to be executed after the batch
has
2487
 *       completed. This should be used to perform any result massaging
that
2488
 *       may be needed, and possibly save data in $_SESSION for display
after
2489
 *       final page redirection.
2490
 *     'file': the path to the file containing the definitions of the
2491
 *       'operations' and 'finished' functions, for instance if they don't
2492
 *       reside in the original '.module' file. The path should be relative
to
2493
 *       the base_path(), and thus should be built using drupal_get_path().
2494
 *     'css' : an array of paths to CSS files to be used on the progress
page.
2495
 *
2496
 * Operations are added as new batch sets. Batch sets are used to ensure
2497
 * clean code independence, ensuring that several batches submitted by
2498
 * different parts of the code (core / contrib modules) can be processed
2499
 * correctly while not interfering or having to cope with each other. Each
2500
 * batch set gets to specify his own UI messages, operates on its own set
2501
 * of operations and results, and triggers its own 'finished' callback.
2502
 * Batch sets are processed sequentially, with the progress bar starting
2503
 * fresh for every new set.
2504
 */
25052366
function batch_set($batch_definition) {
25062
  if ($batch_definition) {
25072
    $batch =& batch_get();
2508
    // Initialize the batch
25092
    if (empty($batch)) {
2510
      $batch = array(
25112
        'sets' => array(),
25122
      );
25132
    }
2514
2515
    $init = array(
25162
      'sandbox' => array(),
25172
      'results' => array(),
25182
      'success' => FALSE,
25192
    );
2520
    // Use get_t() to allow batches at install time.
25212
    $t = get_t();
2522
    $defaults = array(
25232
      'title' => $t('Processing'),
25242
      'init_message' => $t('Initializing.'),
25252
      'progress_message' => $t('Completed @current of @total.'),
25262
      'error_message' => $t('An error has occurred.'),
25272
      'css' => array(),
25282
    );
25292
    $batch_set = $init + $batch_definition + $defaults;
2530
2531
    // Tweak init_message to avoid the bottom of the page flickering down
after init phase.
25322
    $batch_set['init_message'] .= '<br/>&nbsp;';
25332
    $batch_set['total'] = count($batch_set['operations']);
2534
2535
    // If the batch is being processed (meaning we are executing a stored
submit handler),
2536
    // insert the new set after the current one.
25372
    if (isset($batch['current_set'])) {
2538
      // array_insert does not exist...
25390
      $slice1 = array_slice($batch['sets'], 0, $batch['current_set'] + 1);
25400
      $slice2 = array_slice($batch['sets'], $batch['current_set'] + 1);
25410
      $batch['sets'] = array_merge($slice1, array($batch_set), $slice2);
25420
    }
2543
    else {
25442
      $batch['sets'][] = $batch_set;
2545
    }
25462
  }
25472
}
2548
2549
/**
2550
 * Process the batch.
2551
 *
2552
 * Unless the batch has been marked with 'progressive' = FALSE, the
function
2553
 * issues a drupal_goto and thus ends page execution.
2554
 *
2555
 * This function is generally not needed in form submit handlers;
2556
 * Form API takes care of batches that were set during form submission.
2557
 *
2558
 * @param $redirect
2559
 *   (optional) Path to redirect to when the batch has finished processing.
2560
 * @param $url
2561
 *   (optional - should only be used for separate scripts like update.php)
2562
 *   URL of the batch processing page.
2563
 */
25642366
function batch_process($redirect = NULL, $url = NULL) {
25652
  $batch =& batch_get();
2566
25672
  if (isset($batch)) {
2568
    // Add process information
25692
    $url = isset($url) ? $url : 'batch';
2570
    $process_info = array(
25712
      'current_set' => 0,
25722
      'progressive' => TRUE,
25732
      'url' => isset($url) ? $url : 'batch',
25742
      'source_page' => $_GET['q'],
25752
      'redirect' => $redirect,
25762
    );
25772
    $batch += $process_info;
2578
25792
    if ($batch['progressive']) {
2580
      // Clear the way for the drupal_goto redirection to the batch
processing
2581
      // page, by saving and unsetting the 'destination' if any, on both
places
2582
      // drupal_goto looks for it.
25832
      if (isset($_REQUEST['destination'])) {
25840
        $batch['destination'] = $_REQUEST['destination'];
25850
        unset($_REQUEST['destination']);
25860
      }
2587
2588
      // Initiate db storage in order to get a batch id. We have to provide
2589
      // at least an empty string for the (not null) 'token' column.
25902
      $batch['id'] = db_insert('batch')
25912
        ->fields(array(
25922
          'token' => '',
25932
          'timestamp' => REQUEST_TIME,
25942
        ))
25952
        ->execute();
2596
2597
      // Now that we have a batch id, we can generate the redirection link
in
2598
      // the generic error message.
25992
      $t = get_t();
26002
      $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')))));
2601
2602
      // Actually store the batch data and the token generated form the
batch id.
26032
      db_update('batch')
26042
        ->condition('bid', $batch['id'])
26052
        ->fields(array(
26062
          'token' => drupal_get_token($batch['id']),
26072
          'batch' => serialize($batch),
26082
        ))
26092
        ->execute();
2610
26112
      drupal_goto($batch['url'], 'op=start&id=' . $batch['id']);
26120
    }
2613
    else {
2614
      // Non-progressive execution: bypass the whole progressbar workflow
2615
      // and execute the batch in one pass.
26160
      require_once DRUPAL_ROOT . '/includes/batch.inc';
26170
      _batch_process();
2618
    }
26190
  }
26200
}
2621
2622
/**
2623
 * Retrieve the current batch.
2624
 */
26252366
function &batch_get() {
2626490
  static $batch = array();
2627490
  return $batch;
26280
}
2629
2630
/**
2631
 * @} End of "defgroup batch".
2632
 */
26332366