Code coverage for /20080809/includes/form.inc

Line #Times calledCode
1
<?php
2
// $Id: form.inc,v 1.277 2008/07/18 07:06:24 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
 */
692027
function drupal_get_form($form_id) {
701409
  $form_state = array('storage' => NULL, 'submitted' => FALSE);
71
721409
  $args = func_get_args();
731409
  $cacheable = FALSE;
74
751409
  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.
881409
    if (isset($_POST['form_id']) && $_POST['form_id'] == $form_id &&
!empty($_POST['form_build_id'])) {
89480
      $form = form_get_cache($_POST['form_build_id'], $form_state);
90480
    }
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.
951409
    if (!isset($form)) {
961333
      $form_state['post'] = $_POST;
97
      // Use a copy of the function's arguments for manipulation
981333
      $args_temp = $args;
991333
      $args_temp[0] = &$form_state;
1001333
      array_unshift($args_temp, $form_id);
101
1021333
      $form = call_user_func_array('drupal_retrieve_form', $args_temp);
1031332
      $form_build_id = 'form-' . md5(mt_rand());
1041332
      $form['#build_id'] = $form_build_id;
1051332
      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.
1081332
      $original_form = $form;
1091332
      $cacheable = TRUE;
1101332
      unset($form_state['post']);
1111332
    }
1121408
    $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.
1191408
    drupal_process_form($form_id, $form, $form_state);
120993
    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'].
12479
      form_set_cache($form_build_id, $original_form, NULL);
12579
    }
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
142993
  if (!empty($form_state['rebuild']) || !empty($form_state['storage'])) {
1438
    $form = drupal_rebuild_form($form_id, $form_state, $args);
1448
  }
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.
148993
  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
 */
1882027
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.
1928
  $args[0] = &$form_state;
193
  // And the form_id.
1948
  array_unshift($args, $form_id);
1958
  $form = call_user_func_array('drupal_retrieve_form', $args);
196
1978
  if (!isset($form_build_id)) {
198
    // We need a new build_id for the new version of the form.
1998
    $form_build_id = 'form-' . md5(mt_rand());
2008
  }
2018
  $form['#build_id'] = $form_build_id;
2028
  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.
2078
  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.
2128
  $_POST = array();
2138
  $form['#post'] = array();
2148
  drupal_process_form($form_id, $form, $form_state);
2158
  return $form;
2160
}
217
218
/**
219
 * Fetch a form from cache.
220
 */
2212027
function form_get_cache($form_build_id, &$form_state) {
222480
  if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) {
22376
    $form = $cached->data;
22476
    if ($cached = cache_get('storage_' . $form_build_id, 'cache_form')) {
2252
      $form_state['storage'] = $cached->data;
2262
    }
22776
    return $form;
2280
  }
229404
}
230
231
/**
232
 * Store a form in the cache
233
 */
2342027
function form_set_cache($form_build_id, $form, $form_state) {
235
  // 6 hours cache life time for forms should be plenty.
23687
  $expire = 21600;
237
23887
  cache_set('form_' . $form_build_id, $form, 'cache_form', time() +
$expire);
23987
  if (!empty($form_state['storage'])) {
2402
    cache_set('storage_' . $form_build_id, $form_state['storage'],
'cache_form', time() + $expire);
2412
  }
24287
}
243
244
/**
245
 * Retrieves a form using a form_id, populates it with
$form_state['values'],
246
 * processes it, and returns any validation errors encountered. This
247
 * function is the programmatic counterpart to drupal_get_form().
248
 *
249
 * @param $form_id
250
 *   The unique string identifying the desired form. If a function
251
 *   with that name exists, it is called to build the form array.
252
 *   Modules that need to generate the same form (or very similar forms)
253
 *   using different $form_ids can implement hook_forms(), which maps
254
 *   different $form_id values to the proper form constructor function.
Examples
255
 *   may be found in node_forms(), search_forms(), and user_forms().
256
 * @param $form_state
257
 *   A keyed array containing the current state of the form. Most
258
 *   important is the $form_state['values'] collection, a tree of data
259
 *   used to simulate the incoming $_POST information from a user's
260
 *   form submission.
261
 * @param ...
262
 *   Any additional arguments are passed on to the functions called by
263
 *   drupal_execute(), including the unique form constructor function.
264
 *   For example, the node_edit form requires that a node object be passed
265
 *   in here when it is called.
266
 * For example:
267
 *
268
 * // register a new user
269
 * $form_state = array();
270
 * $form_state['values']['name'] = 'robo-user';
271
 * $form_state['values']['mail'] = 'robouser@example.com';
272
 * $form_state['values']['pass'] = 'password';
273
 * $form_state['values']['op'] = t('Create new account');
274
 * drupal_execute('user_register', $form_state);
275
 *
276
 * // Create a new node
277
 * $form_state = array();
278
 * module_load_include('inc', 'node', 'node.pages');
279
 * $node = array('type' => 'story');
280
 * $form_state['values']['title'] = 'My node';
281
 * $form_state['values']['body'] = 'This is the body text!';
282
 * $form_state['values']['name'] = 'robo-user';
283
 * $form_state['values']['op'] = t('Save');
284
 * drupal_execute('story_node_form', $form_state, (object)$node);
285
 */
2862027
function drupal_execute($form_id, &$form_state) {
2871
  $args = func_get_args();
2881
  $form = call_user_func_array('drupal_retrieve_form', $args);
2891
  $form['#post'] = $form_state['values'];
2901
  drupal_prepare_form($form_id, $form, $form_state);
2911
  drupal_process_form($form_id, $form, $form_state);
2921
}
293
294
/**
295
 * Retrieves the structured array that defines a given form.
296
 *
297
 * @param $form_id
298
 *   The unique string identifying the desired form. If a function
299
 *   with that name exists, it is called to build the form array.
300
 *   Modules that need to generate the same form (or very similar forms)
301
 *   using different $form_ids can implement hook_forms(), which maps
302
 *   different $form_id values to the proper form constructor function.
303
 * @param $form_state
304
 *   A keyed array containing the current state of the form.
305
 * @param ...
306
 *   Any additional arguments needed by the unique form constructor
307
 *   function. Generally, these are any arguments passed into the
308
 *   drupal_get_form() or drupal_execute() functions after the first
309
 *   argument. If a module implements hook_forms(), it can examine
310
 *   these additional arguments and conditionally return different
311
 *   builder functions as well.
312
 */
3132027
function drupal_retrieve_form($form_id, &$form_state) {
3141339
  static $forms;
315
316
  // We save two copies of the incoming arguments: one for modules to use
317
  // when mapping form ids to constructor functions, and another to pass
to
318
  // the constructor function itself. We shift out the first argument --
the
319
  // $form_id itself -- from the list to pass into the constructor
function,
320
  // since it's already known.
3211339
  $args = func_get_args();
3221339
  $saved_args = $args;
3231339
  array_shift($args);
3241339
  if (isset($form_state)) {
3251339
    array_shift($args);
3261339
  }
327
328
  // We first check to see if there's a function named after the $form_id.
329
  // If there is, we simply pass the arguments on to it to get the form.
3301339
  if (!drupal_function_exists($form_id)) {
331
    // In cases where many form_ids need to share a central constructor
function,
332
    // such as the node editing form, modules can implement hook_forms().
It
333
    // maps one or more form_ids to the correct constructor functions.
334
    //
335
    // We cache the results of that hook to save time, but that only works
336
    // for modules that know all their form_ids in advance. (A module that
337
    // adds a small 'rate this comment' form to each comment in a list
338
    // would need a unique form_id for each one, for example.)
339
    //
340
    // So, we call the hook if $forms isn't yet populated, OR if it
doesn't
341
    // yet have an entry for the requested form_id.
342135
    if (!isset($forms) || !isset($forms[$form_id])) {
343135
      $forms = module_invoke_all('forms', $form_id, $args);
344135
    }
345135
    $form_definition = $forms[$form_id];
346135
    if (isset($form_definition['callback arguments'])) {
3474
      $args = array_merge($form_definition['callback arguments'], $args);
3484
    }
349135
    if (isset($form_definition['callback'])) {
350135
      $callback = $form_definition['callback'];
351135
      drupal_function_exists($callback);
352135
    }
353135
  }
354
3551339
  array_unshift($args, NULL);
3561339
  $args[0] = &$form_state;
357
358
  // If $callback was returned by a hook_forms() implementation, call it.
359
  // Otherwise, call the function named after the form id.
3601339
  $form = call_user_func_array(isset($callback) ? $callback : $form_id,
$args);
361
362
  // We store the original function arguments, rather than the final $arg
363
  // value, so that form_alter functions can see what was originally
364
  // passed to drupal_retrieve_form(). This allows the contents of
#parameters
365
  // to be saved and passed in at a later date to recreate the form.
3661338
  $form['#parameters'] = $saved_args;
3671338
  return $form;
3680
}
369
370
/**
371
 * This function is the heart of form API. The form gets built, validated
and in
372
 * appropriate cases, submitted.
373
 *
374
 * @param $form_id
375
 *   The unique string identifying the current form.
376
 * @param $form
377
 *   An associative array containing the structure of the form.
378
 * @param $form_state
379
 *   A keyed array containing the current state of the form. This
380
 *   includes the current persistent storage data for the form, and
381
 *   any data passed along by earlier steps when displaying a
382
 *   multi-step form. Additional information, like the sanitized $_POST
383
 *   data, is also accumulated here.
384
 */
3852027
function drupal_process_form($form_id, &$form, &$form_state) {
3861408
  $form_state['values'] = array();
387
3881408
  $form = form_builder($form_id, $form, $form_state);
389
  // Only process the form if it is programmed or the form_id coming
390
  // from the POST data is set and matches the current form_id.
3911408
  if ((!empty($form['#programmed'])) || (!empty($form['#post']) &&
(isset($form['#post']['form_id']) && ($form['#post']['form_id'] ==
$form_id)))) {
392479
    drupal_validate_form($form_id, $form, $form_state);
393
394
    // form_clean_id() maintains a cache of element IDs it has seen,
395
    // so it can prevent duplicates. We want to be sure we reset that
396
    // cache when a form is processed, so scenerios that result in
397
    // the form being built behind the scenes and again for the
398
    // browser don't increment all the element IDs needlessly.
399479
    form_clean_id(NULL, TRUE);
400
401479
    if ((!empty($form_state['submitted'])) && !form_get_errors() &&
empty($form_state['rebuild'])) {
402427
      $form_state['redirect'] = NULL;
403427
      form_execute_handlers('submit', $form, $form_state);
404
405
      // We'll clear out the cached copies of the form and its stored data
406
      // here, as we've finished with them. The in-memory copies are still
407
      // here, though.
408426
      if (variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED &&
!empty($form_state['values']['form_build_id'])) {
409399
        cache_clear_all('form_' . $form_state['values']['form_build_id'],
'cache_form');
410399
        cache_clear_all('storage_' .
$form_state['values']['form_build_id'], 'cache_form');
411399
      }
412
413
      // If batches were set in the submit handlers, we process them now,
414
      // possibly ending execution. We make sure we do not react to the
batch
415
      // that is already being processed (if a batch operation performs a
416
      // drupal_execute).
417426
      if ($batch =& batch_get() && !isset($batch['current_set'])) {
418
        // The batch uses its own copies of $form and $form_state for
419
        // late execution of submit handers and post-batch redirection.
4200
        $batch['form'] = $form;
4210
        $batch['form_state'] = $form_state;
4220
        $batch['progressive'] = !$form['#programmed'];
4230
        batch_process();
424
        // Execution continues only for programmatic forms.
425
        // For 'regular' forms, we get redirected to the batch processing
426
        // page. Form redirection will be handled in _batch_finished(),
427
        // after the batch is processed.
4280
      }
429
430
      // If no submit handlers have populated the $form_state['storage']
431
      // bundle, and the $form_state['rebuild'] flag has not been set,
432
      // we're finished and should redirect to a new destination page
433
      // if one has been set (and a fresh, unpopulated copy of the form
434
      // if one hasn't). If the form was called by drupal_execute(),
435
      // however, we'll skip this and let the calling function examine
436
      // the resulting $form_state bundle itself.
437426
      if (!$form['#programmed'] && empty($form_state['rebuild']) &&
empty($form_state['storage'])) {
438418
        drupal_redirect_form($form, $form_state['redirect']);
4393
      }
44012
    }
44164
  }
442994
}
443
444
/**
445
 * Prepares a structured form array by adding required elements,
446
 * executing any hook_form_alter functions, and optionally inserting
447
 * a validation token to prevent tampering.
448
 *
449
 * @param $form_id
450
 *   A unique string identifying the form for validation, submission,
451
 *   theming, and hook_form_alter functions.
452
 * @param $form
453
 *   An associative array containing the structure of the form.
454
 * @param $form_state
455
 *   A keyed array containing the current state of the form. Passed
456
 *   in here so that hook_form_alter() calls can use it, as well.
457
 */
4582027
function drupal_prepare_form($form_id, &$form, &$form_state) {
4591338
  global $user;
460
4611338
  $form['#type'] = 'form';
4621338
  $form['#programmed'] = isset($form['#post']);
463
4641338
  if (isset($form['#build_id'])) {
4651338
    $form['form_build_id'] = array(
4661338
      '#type' => 'hidden',
4671338
      '#value' => $form['#build_id'],
4681338
      '#id' => $form['#build_id'],
4691338
      '#name' => 'form_build_id',
470
    );
4711338
  }
472
473
  // Add a token, based on either #token or form_id, to any form displayed
to
474
  // authenticated users. This ensures that any submitted form was
actually
475
  // requested previously by the user and protects against cross site
request
476
  // forgeries.
4771338
  if (isset($form['#token'])) {
47863
    if ($form['#token'] === FALSE || $user->uid == 0 ||
$form['#programmed']) {
47937
      unset($form['#token']);
48037
    }
481
    else {
48226
      $form['form_token'] = array('#type' => 'token', '#default_value' =>
drupal_get_token($form['#token']));
483
    }
48463
  }
4851304
  else if (isset($user->uid) && $user->uid && !$form['#programmed']) {
486711
    $form['#token'] = $form_id;
487711
    $form['form_token'] = array(
488711
      '#id' => form_clean_id('edit-' . $form_id . '-form-token'),
489711
      '#type' => 'token',
490711
      '#default_value' => drupal_get_token($form['#token']),
491
    );
492711
  }
493
4941338
  if (isset($form_id)) {
4951338
    $form['form_id'] = array(
4961338
      '#type' => 'hidden',
4971338
      '#value' => $form_id,
4981338
      '#id' => form_clean_id("edit-$form_id"),
499
    );
5001338
  }
5011338
  if (!isset($form['#id'])) {
5021080
    $form['#id'] = form_clean_id($form_id);
5031080
  }
504
5051338
  $form += _element_info('form');
506
5071338
  if (!isset($form['#validate'])) {
508601
    if (drupal_function_exists($form_id . '_validate')) {
509305
      $form['#validate'] = array($form_id . '_validate');
510305
    }
511601
  }
512
5131338
  if (!isset($form['#submit'])) {
5141040
    if (drupal_function_exists($form_id . '_submit')) {
515
      // We set submit here so that it can be altered.
516967
      $form['#submit'] = array($form_id . '_submit');
517967
    }
5181040
  }
519
520
  // Normally, we would call drupal_alter($form_id, $form, $form_state).
521
  // However, drupal_alter() normally supports just one byref parameter.
Using
522
  // the __drupal_alter_by_ref key, we can store any additional parameters
523
  // that need to be altered, and they'll be split out into additional
params
524
  // for the hook_form_alter() implementations.
525
  // @todo: Remove this in Drupal 7.
5261338
  $data = &$form;
5271338
  $data['__drupal_alter_by_ref'] = array(&$form_state);
5281338
  drupal_alter('form_' . $form_id, $data);
529
530
  // __drupal_alter_by_ref is unset in the drupal_alter() function, we
need
531
  // to repopulate it to ensure both calls get the data.
5321338
  $data['__drupal_alter_by_ref'] = array(&$form_state);
5331338
  drupal_alter('form', $data, $form_id);
5341338
}
535
536
537
/**
538
 * Validates user-submitted form data from the $form_state using
539
 * the validate functions defined in a structured form array.
540
 *
541
 * @param $form_id
542
 *   A unique string identifying the form for validation, submission,
543
 *   theming, and hook_form_alter functions.
544
 * @param $form
545
 *   An associative array containing the structure of the form.
546
 * @param $form_state
547
 *   A keyed array containing the current state of the form. The current
548
 *   user-submitted data is stored in $form_state['values'], though
549
 *   form validation functions are passed an explicit copy of the
550
 *   values for the sake of simplicity. Validation handlers can also
551
 *   $form_state to pass information on to submit handlers. For example:
552
 *     $form_state['data_for_submision'] = $data;
553
 *   This technique is useful when validation requires file parsing,
554
 *   web service requests, or other expensive requests that should
555
 *   not be repeated in the submission step.
556
 */
5572027
function drupal_validate_form($form_id, $form, &$form_state) {
558479
  static $validated_forms = array();
559
560479
  if (isset($validated_forms[$form_id])) {
56113
    return;
5620
  }
563
564
  // If the session token was set by drupal_prepare_form(), ensure that it
565
  // matches the current user's session.
566479
  if (isset($form['#token'])) {
567306
    if (!drupal_valid_token($form_state['values']['form_token'],
$form['#token'])) {
568
      // Setting this error will cause the form to fail validation.
5690
      form_set_error('form_token', t('Validation error, please try again.
If this error persists, please contact the site administrator.'));
5700
    }
571306
  }
572
573479
  _form_validate($form, $form_state, $form_id);
574479
  $validated_forms[$form_id] = TRUE;
575479
}
576
577
/**
578
 * Renders a structured form array into themed HTML.
579
 *
580
 * @param $form_id
581
 *   A unique string identifying the form for validation, submission,
582
 *   theming, and hook_form_alter functions.
583
 * @param $form
584
 *   An associative array containing the structure of the form.
585
 * @return
586
 *   A string containing the path of the page to display when processing
587
 *   is complete.
588
 */
5892027
function drupal_render_form($form_id, &$form) {
590
  // Don't override #theme if someone already set it.
591993
  if (!isset($form['#theme'])) {
592822
    init_theme();
593822
    $registry = theme_get_registry();
594822
    if (isset($registry[$form_id])) {
595127
      $form['#theme'] = $form_id;
596127
    }
597822
  }
598
599993
  $output = drupal_render($form);
600993
  return $output;
6010
}
602
603
/**
604
 * Redirect the user to a URL after a form has been processed.
605
 *
606
 * @param $form
607
 *   An associative array containing the structure of the form.
608
 * @param $redirect
609
 *   An optional value containing the destination path to redirect
610
 *   to if none is specified by the form.
611
 */
6122027
function drupal_redirect_form($form, $redirect = NULL) {
613418
  $goto = NULL;
614418
  if (isset($redirect)) {
615352
    $goto = $redirect;
616352
  }
617418
  if ($goto !== FALSE && isset($form['#redirect'])) {
6183
    $goto = $form['#redirect'];
6193
  }
620418
  if (!isset($goto) || ($goto !== FALSE)) {
621415
    if (isset($goto)) {
622352
      if (is_array($goto)) {
62314
        call_user_func_array('drupal_goto', $goto);
6240
      }
625
      else {
626338
        drupal_goto($goto);
627
      }
6280
    }
62963
    drupal_goto($_GET['q']);
6300
  }
6313
}
632
633
/**
634
 * Performs validation on form elements. First ensures required fields are
635
 * completed, #maxlength is not exceeded, and selected options were in the
636
 * list of options given to the user. Then calls user-defined validators.
637
 *
638
 * @param $elements
639
 *   An associative array containing the structure of the form.
640
 * @param $form_state
641
 *   A keyed array containing the current state of the form. The current
642
 *   user-submitted data is stored in $form_state['values'], though
643
 *   form validation functions are passed an explicit copy of the
644
 *   values for the sake of simplicity. Validation handlers can also
645
 *   $form_state to pass information on to submit handlers. For example:
646
 *     $form_state['data_for_submision'] = $data;
647
 *   This technique is useful when validation requires file parsing,
648
 *   web service requests, or other expensive requests that should
649
 *   not be repeated in the submission step.
650
 * @param $form_id
651
 *   A unique string identifying the form for validation, submission,
652
 *   theming, and hook_form_alter functions.
653
 */
6542027
function _form_validate($elements, &$form_state, $form_id = NULL) {
655479
  static $complete_form;
656
657
  // Also used in the installer, pre-database setup.
658479
  $t = get_t();
659
660
  // Recurse through all children.
661479
  foreach (element_children($elements) as $key) {
662479
    if (isset($elements[$key]) && $elements[$key]) {
663479
      _form_validate($elements[$key], $form_state);
664479
    }
665479
  }
666
  // Validate the current input.
667479
  if (!isset($elements['#validated']) || !$elements['#validated']) {
668479
    if (isset($elements['#needs_validation'])) {
669
      // Make sure a value is passed when the field is required.
670
      // A simple call to empty() will not cut it here as some fields,
like
671
      // checkboxes, can return a valid value of '0'. Instead, check the
672
      // length if it's a string, and the item count if it's an array.
673479
      if ($elements['#required'] && (!count($elements['#value']) ||
(is_string($elements['#value']) && strlen(trim($elements['#value'])) ==
0))) {
6747
        form_error($elements, $t('!name field is required.', array('!name'
=> $elements['#title'])));
6757
      }
676
677
      // Verify that the value is not longer than #maxlength.
678479
      if (isset($elements['#maxlength']) &&
drupal_strlen($elements['#value']) > $elements['#maxlength']) {
6790
        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']))));
6800
      }
681
682479
      if (isset($elements['#options']) && isset($elements['#value'])) {
683183
        if ($elements['#type'] == 'select') {
684163
          $options = form_options_flatten($elements['#options']);
685163
        }
686
        else {
68750
          $options = $elements['#options'];
688
        }
689183
        if (is_array($elements['#value'])) {
69031
          $value = $elements['#type'] == 'checkboxes' ?
array_keys(array_filter($elements['#value'])) : $elements['#value'];
69131
          foreach ($value as $v) {
69220
            if (!isset($options[$v])) {
6930
              form_error($elements, $t('An illegal choice has been
detected. Please contact the site administrator.'));
6940
              watchdog('form', 'Illegal choice %choice in !name element.',
array('%choice' => $v, '!name' => empty($elements['#title']) ?
$elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
6950
            }
69620
          }
69731
        }
698170
        elseif (!isset($options[$elements['#value']])) {
6990
          form_error($elements, $t('An illegal choice has been detected.
Please contact the site administrator.'));
7000
          watchdog('form', 'Illegal choice %choice in %name element.',
array('%choice' => $elements['#value'], '%name' =>
empty($elements['#title']) ? $elements['#parents'][0] :
$elements['#title']), WATCHDOG_ERROR);
7010
        }
702183
      }
703479
    }
704
705
    // Call user-defined form level validators and store a copy of the
full
706
    // form so that element-specific validators can examine the entire
structure
707
    // if necessary.
708479
    if (isset($form_id)) {
709479
      form_execute_handlers('validate', $elements, $form_state);
710479
      $complete_form = $elements;
711479
    }
712
    // Call any element-specific validators. These must act on the element
713
    // #value data.
714479
    elseif (isset($elements['#element_validate'])) {
71511
      foreach ($elements['#element_validate'] as $function) {
71611
        if (drupal_function_exists($function))  {
71711
          $function($elements, $form_state, $complete_form);
71811
        }
71911
      }
72011
    }
721479
    $elements['#validated'] = TRUE;
722479
  }
723479
}
724
725
/**
726
 * A helper function used to execute custom validation and submission
727
 * handlers for a given form. Button-specific handlers are checked
728
 * first. If none exist, the function falls back to form-level handlers.
729
 *
730
 * @param $type
731
 *   The type of handler to execute. 'validate' or 'submit' are the
732
 *   defaults used by Form API.
733
 * @param $form
734
 *   An associative array containing the structure of the form.
735
 * @param $form_state
736
 *   A keyed array containing the current state of the form. If the user
737
 *   submitted the form by clicking a button with custom handler functions
738
 *   defined, those handlers will be stored here.
739
 */
7402027
function form_execute_handlers($type, &$form, &$form_state) {
741479
  $return = FALSE;
742479
  if (isset($form_state[$type . '_handlers'])) {
74372
    $handlers = $form_state[$type . '_handlers'];
74472
  }
745479
  elseif (isset($form['#' . $type])) {
746476
    $handlers = $form['#' . $type];
747476
  }
748
  else {
74994
    $handlers = array();
750
  }
751
752479
  foreach ($handlers as $function) {
753476
    if (drupal_function_exists($function))  {
754476
      if ($type == 'submit' && ($batch =& batch_get())) {
755
        // Some previous _submit handler has set a batch. We store the
call
756
        // in a special 'control' batch set, for execution at the correct
757
        // time during the batch processing workflow.
7580
        $batch['sets'][] = array('form_submit' => $function);
7590
      }
760
      else {
761476
        $function($form, $form_state);
762
      }
763475
      $return = TRUE;
764475
    }
765475
  }
766479
  return $return;
7670
}
768
769
/**
770
 * File an error against a form element.
771
 *
772
 * @param $name
773
 *   The name of the form element. If the #parents property of your form
774
 *   element is array('foo', 'bar', 'baz') then you may set an error on
'foo'
775
 *   or 'foo][bar][baz'. Setting an error on 'foo' sets an error for every
776
 *   element where the #parents array starts with 'foo'.
777
 * @param $message
778
 *   The error message to present to the user.
779
 * @return
780
 *   Never use the return value of this function, use form_get_errors and
781
 *   form_get_error instead.
782
 */
7832027
function form_set_error($name = NULL, $message = '') {
7841333
  static $form = array();
7851333
  if (isset($name) && !isset($form[$name])) {
78643
    $form[$name] = $message;
78743
    if ($message) {
78843
      drupal_set_message($message, 'error');
78943
    }
79043
  }
7911333
  return $form;
7920
}
793
794
/**
795
 * Return an associative array of all errors.
796
 */
7972027
function form_get_errors() {
798502
  $form = form_set_error();
799502
  if (!empty($form)) {
80041
    return $form;
8010
  }
802463
}
803
804
/**
805
 * Return the error message filed against the form with the specified
name.
806
 */
8072027
function form_get_error($element) {
808911
  $form = form_set_error();
809911
  $key = $element['#parents'][0];
810911
  if (isset($form[$key])) {
81125
    return $form[$key];
8120
  }
813907
  $key = implode('][', $element['#parents']);
814907
  if (isset($form[$key])) {
8150
    return $form[$key];
8160
  }
817907
}
818
819
/**
820
 * Flag an element as having an error.
821
 */
8222027
function form_error(&$element, $message = '') {
8237
  form_set_error(implode('][', $element['#parents']), $message);
8247
}
825
826
/**
827
 * Walk through the structured form array, adding any required
828
 * properties to each element and mapping the incoming $_POST
829
 * data to the proper elements.
830
 *
831
 * @param $form_id
832
 *   A unique string identifying the form for validation, submission,
833
 *   theming, and hook_form_alter functions.
834
 * @param $form
835
 *   An associative array containing the structure of the form.
836
 * @param $form_state
837
 *   A keyed array containing the current state of the form. In this
838
 *   context, it is used to accumulate information about which button
839
 *   was clicked when the form was submitted, as well as the sanitized
840
 *   $_POST data.
841
 */
8422027
function form_builder($form_id, $form, &$form_state) {
8431408
  static $complete_form, $cache;
844
845
  // Initialize as unprocessed.
8461408
  $form['#processed'] = FALSE;
847
848
  // Use element defaults.
8491408
  if ((!empty($form['#type'])) && ($info = _element_info($form['#type'])))
{
850
    // Overlay $info onto $form, retaining preexisting keys in $form.
8511408
    $form += $info;
8521408
  }
853
8541408
  if (isset($form['#type']) && $form['#type'] == 'form') {
8551408
    $cache = NULL;
8561408
    $complete_form = $form;
8571408
    if (!empty($form['#programmed'])) {
8581
      $form_state['submitted'] = TRUE;
8591
    }
8601408
  }
861
8621408
  if (isset($form['#input']) && $form['#input']) {
8631408
    _form_builder_handle_input_element($form_id, $form, $form_state,
$complete_form);
8641408
  }
8651408
  $form['#defaults_loaded'] = TRUE;
866
867
  // We start off assuming all form elements are in the correct order.
8681408
  $form['#sorted'] = TRUE;
869
870
  // Recurse through all child elements.
8711408
  $count = 0;
8721408
  foreach (element_children($form) as $key) {
8731408
    $form[$key]['#post'] = $form['#post'];
8741408
    $form[$key]['#programmed'] = $form['#programmed'];
875
    // Don't squash an existing tree value.
8761408
    if (!isset($form[$key]['#tree'])) {
8771408
      $form[$key]['#tree'] = $form['#tree'];
8781408
    }
879
880
    // Deny access to child elements if parent is denied.
8811408
    if (isset($form['#access']) && !$form['#access']) {
882150
      $form[$key]['#access'] = FALSE;
883150
    }
884
885
    // Don't squash existing parents value.
8861408
    if (!isset($form[$key]['#parents'])) {
887
      // Check to see if a tree of child elements is present. If so,
888
      // continue down the tree if required.
8891408
      $form[$key]['#parents'] = $form[$key]['#tree'] && $form['#tree'] ?
array_merge($form['#parents'], array($key)) : array($key);
8901408
      $array_parents = isset($form['#array_parents']) ?
$form['#array_parents'] : array();
8911408
      $array_parents[] = $key;
8921408
      $form[$key]['#array_parents'] = $array_parents;
8931408
    }
894
895
    // Assign a decimal placeholder weight to preserve original array
order.
8961408
    if (!isset($form[$key]['#weight'])) {
8971408
      $form[$key]['#weight'] = $count/1000;
8981408
    }
899
    else {
900
      // If one of the child elements has a weight then we will need to
sort
901
      // later.
902701
      unset($form['#sorted']);
903
    }
9041408
    $form[$key] = form_builder($form_id, $form[$key], $form_state);
9051408
    $count++;
9061408
  }
907
908
  // The #after_build flag allows any piece of a form to be altered
909
  // after normal input parsing has been completed.
9101408
  if (isset($form['#after_build']) && !isset($form['#after_build_done']))
{
911151
    foreach ($form['#after_build'] as $function) {
912151
      $form = $function($form, $form_state);
913151
      $form['#after_build_done'] = TRUE;
914151
    }
915151
  }
916
917
  // Now that we've processed everything, we can go back to handle the
funky
918
  // Internet Explorer button-click scenario.
9191408
  _form_builder_ie_cleanup($form, $form_state);
920
921
  // We shoud keep the buttons array until the IE clean up function
922
  // has recognized the submit button so the form has been marked
923
  // as submitted. If we already know which button was submitted,
924
  // we don't need the array.
9251408
  if (!empty($form_state['submitted'])) {
926475
    unset($form_state['buttons']);
927475
  }
928
929
  // If some callback set #cache, we need to flip a static flag so later
it
930
  // can be found.
9311408
  if (isset($form['#cache'])) {
932153
    $cache = $form['#cache'];
933153
  }
934
  // We are on the top form, we can copy back #cache if it's set.
9351408
  if (isset($form['#type']) && $form['#type'] == 'form' && isset($cache))
{
936153
    $form['#cache'] = TRUE;
937153
  }
9381408
  return $form;
9390
}
940
941
/**
942
 * Populate the #value and #name properties of input elements so they
943
 * can be processed and rendered. Also, execute any #process handlers
944
 * attached to a specific element.
945
 */
9462027
function _form_builder_handle_input_element($form_id, &$form, &$form_state,
$complete_form) {
9471408
  if (!isset($form['#name'])) {
9481408
    $name = array_shift($form['#parents']);
9491408
    $form['#name'] = $name;
9501408
    if ($form['#type'] == 'file') {
951
      // To make it easier to handle $_FILES in file.inc, we place all
952
      // file fields in the 'files' array. Also, we do not support
953
      // nested file names.
95441
      $form['#name'] = 'files[' . $form['#name'] . ']';
95541
    }
9561408
    elseif (count($form['#parents'])) {
957426
      $form['#name'] .= '[' . implode('][', $form['#parents']) . ']';
958426
    }
9591408
    array_unshift($form['#parents'], $name);
9601408
  }
9611408
  if (!isset($form['#id'])) {
9621389
    $form['#id'] = form_clean_id('edit-' . implode('-',
$form['#parents']));
9631389
  }
964
9651408
  unset($edit);
9661408
  if (!empty($form['#disabled'])) {
967143
    $form['#attributes']['disabled'] = 'disabled';
968143
  }
969
9701408
  if (!isset($form['#value']) && !array_key_exists('#value', $form)) {
9711407
    $function = !empty($form['#value_callback']) ? $form['#value_callback']
: 'form_type_' . $form['#type'] . '_value';
9721407
    if (($form['#programmed']) || ((!isset($form['#access']) ||
$form['#access']) && isset($form['#post']) &&
(isset($form['#post']['form_id']) && $form['#post']['form_id'] ==
$form_id))) {
973479
      $edit = $form['#post'];
974479
      foreach ($form['#parents'] as $parent) {
975479
        $edit = isset($edit[$parent]) ? $edit[$parent] : NULL;
976479
      }
977479
      if (!$form['#programmed'] || isset($edit)) {
978
        // Call #type_value to set the form value;
979479
        if (function_exists($function)) {
980479
          $form['#value'] = $function($form, $edit);
981479
        }
982479
        if (!isset($form['#value']) && isset($edit)) {
983361
          $form['#value'] = $edit;
984361
        }
985479
      }
986
      // Mark all posted values for validation.
987479
      if (isset($form['#value']) || (isset($form['#required']) &&
$form['#required'])) {
988479
        $form['#needs_validation'] = TRUE;
989479
      }
990479
    }
991
    // Load defaults.
9921407
    if (!isset($form['#value'])) {
993
      // Call #type_value without a second argument to request
default_value handling.
9941039
      if (function_exists($function)) {
9951029
        $form['#value'] = $function($form);
9961029
      }
997
      // Final catch. If we haven't set a value yet, use the explicit
default value.
998
      // Avoid image buttons (which come with garbage value), so we only
get value
999
      // for the button actually clicked.
10001039
      if (!isset($form['#value']) && empty($form['#has_garbage_value'])) {
10011039
        $form['#value'] = isset($form['#default_value']) ?
$form['#default_value'] : '';
10021039
      }
10031039
    }
10041407
  }
1005
1006
  // Determine which button (if any) was clicked to submit the form.
1007
  // We compare the incoming values with the buttons defined in the form,
1008
  // and flag the one that matches. We have to do some funky tricks to
1009
  // deal with Internet Explorer's handling of single-button forms,
though.
10101408
  if (!empty($form['#post']) && isset($form['#executes_submit_callback']))
{
1011
    // First, accumulate a collection of buttons, divided into two bins:
1012
    // those that execute full submit callbacks and those that only
validate.
1013481
    $button_type = $form['#executes_submit_callback'] ? 'submit' :
'button';
1014481
    $form_state['buttons'][$button_type][] = $form;
1015
1016481
    if (_form_button_was_clicked($form)) {
1017479
      $form_state['submitted'] = $form_state['submitted'] ||
$form['#executes_submit_callback'];
1018
1019
      // In most cases, we want to use form_set_value() to manipulate
1020
      // the global variables. In this special case, we want to make sure
that
1021
      // the value of this element is listed in $form_variables under
'op'.
1022479
      $form_state['values'][$form['#name']] = $form['#value'];
1023479
      $form_state['clicked_button'] = $form;
1024
1025479
      if (isset($form['#validate'])) {
10260
        $form_state['validate_handlers'] = $form['#validate'];
10270
      }
1028479
      if (isset($form['#submit'])) {
102975
        $form_state['submit_handlers'] = $form['#submit'];
103075
      }
1031479
    }
1032481
  }
1033
  // Allow for elements to expand to multiple elements, e.g., radios,
1034
  // checkboxes and files.
10351408
  if (isset($form['#process']) && !$form['#processed']) {
10361408
    foreach ($form['#process'] as $process) {
10371408
      if (drupal_function_exists($process)) {
10381408
        $form = $process($form, isset($edit) ? $edit : NULL, $form_state,
$complete_form);
10391408
      }
10401408
    }
10411408
    $form['#processed'] = TRUE;
10421408
  }
10431408
  form_set_value($form, $form['#value'], $form_state);
10441408
}
1045
1046
/**
1047
 * Helper function to handle the sometimes-convoluted logic of button
1048
 * click detection.
1049
 *
1050
 * In Internet Explorer, if ONLY one submit button is present, AND the
1051
 * enter key is used to submit the form, no form value is sent for it
1052
 * and we'll never detect a match. That special case is handled by
1053
 * _form_builder_ie_cleanup().
1054
 */
10552027
function _form_button_was_clicked($form) {
1056
  // First detect normal 'vanilla' button clicks. Traditionally, all
1057
  // standard buttons on a form share the same name (usually 'op'),
1058
  // and the specific return value is used to determine which was
1059
  // clicked. This ONLY works as long as $form['#name'] puts the
1060
  // value at the top level of the tree of $_POST data.
1061481
  if (isset($form['#post'][$form['#name']]) &&
$form['#post'][$form['#name']] == $form['#value']) {
1062479
    return TRUE;
10630
  }
1064
  // When image buttons are clicked, browsers do NOT pass the form element
1065
  // value in $_POST. Instead they pass an integer representing the
1066
  // coordinates of the click on the button image. This means that image
1067
  // buttons MUST have unique $form['#name'] values, but the details of
1068
  // their $_POST data should be ignored.
1069157
  elseif (!empty($form['#has_garbage_value']) && isset($form['#value']) &&
$form['#value'] !== '') {
10700
    return TRUE;
10710
  }
1072157
  return FALSE;
10730
}
1074
1075
/**
1076
 * In IE, if only one submit button is present, AND the enter key is
1077
 * used to submit the form, no form value is sent for it and our normal
1078
 * button detection code will never detect a match. We call this
1079
 * function after all other button-detection is complete to check
1080
 * for the proper conditions, and treat the single button on the form
1081
 * as 'clicked' if they are met.
1082
 */
10832027
function _form_builder_ie_cleanup($form, &$form_state) {
1084
  // Quick check to make sure we're always looking at the full form
1085
  // and not a sub-element.
10861408
  if (!empty($form['#type']) && $form['#type'] == 'form') {
1087
    // If we haven't recognized a submission yet, and there's a single
1088
    // submit button, we know that we've hit the right conditions. Grab
1089
    // the first one and treat it as the clicked button.
10901408
    if (empty($form_state['submitted']) &&
!empty($form_state['buttons']['submit']) &&
empty($form_state['buttons']['button'])) {
109115
      $button = $form_state['buttons']['submit'][0];
1092
1093
      // Set up all the $form_state information that would have been
1094
      // populated had the button been recognized earlier.
109515
      $form_state['submitted'] = TRUE;
109615
      $form_state['submit_handlers'] = empty($button['#submit']) ? NULL :
$button['#submit'];
109715
      $form_state['validate_handlers'] = empty($button['#validate']) ? NULL
: $button['#validate'];
109815
      $form_state['values'][$button['#name']] = $button['#value'];
109915
      $form_state['clicked_button'] = $button;
110015
    }
11011408
  }
11021408
}
1103
1104
/**
1105
 * Helper function to determine the value for an image button form
element.
1106
 *
1107
 * @param $form
1108
 *   The form element whose value is being populated.
1109
 * @param $edit
1110
 *   The incoming POST data to populate the form element. If this is
FALSE,
1111
 *   the element's default value should be returned.
1112
 * @return
1113
 *   The data that will appear in the $form_state['values'] collection
1114
 *   for this element. Return nothing to use the default.
1115
 */
11162027
function form_type_image_button_value($form, $edit = FALSE) {
11170
  if ($edit !== FALSE) {
11180
    if (!empty($edit)) {
1119
      // If we're dealing with Mozilla or Opera, we're lucky. It will
1120
      // return a proper value, and we can get on with things.
11210
      return $form['#return_value'];
11220
    }
1123
    else {
1124
      // Unfortunately, in IE we never get back a proper value for THIS
1125
      // form element. Instead, we get back two split values: one for the
1126
      // X and one for the Y coordinates on which the user clicked the
1127
      // button. We'll find this element in the #post data, and search
1128
      // in the same spot for its name, with '_x'.
11290
      $post = $form['#post'];
11300
      foreach (split('\[', $form['#name']) as $element_name) {
1131
        // chop off the ] that may exist.
11320
        if (substr($element_name, -1) == ']') {
11330
          $element_name = substr($element_name, 0, -1);
11340
        }
1135
11360
        if (!isset($post[$element_name])) {
11370
          if (isset($post[$element_name . '_x'])) {
11380
            return $form['#return_value'];
11390
          }
11400
          return NULL;
11410
        }
11420
        $post = $post[$element_name];
11430
      }
11440
      return $form['#return_value'];
1145
    }
11460
  }
11470
}
1148
1149
/**
1150
 * Helper function to determine the value for a checkbox form element.
1151
 *
1152
 * @param $form
1153
 *   The form element whose value is being populated.
1154
 * @param $edit
1155
 *   The incoming POST data to populate the form element. If this is
FALSE,
1156
 *   the element's default value should be returned.
1157
 * @return
1158
 *   The data that will appear in the $form_state['values'] collection
1159
 *   for this element. Return nothing to use the default.
1160
 */
11612027
function form_type_checkbox_value($form, $edit = FALSE) {
1162382
  if ($edit !== FALSE) {
1163143
    return !empty($edit) ? $form['#return_value'] : 0;
11640
  }
1165299
}
1166
1167
/**
1168
 * Helper function to determine the value for a checkboxes form element.
1169
 *
1170
 * @param $form
1171
 *   The form element whose value is being populated.
1172
 * @param $edit
1173
 *   The incoming POST data to populate the form element. If this is
FALSE,
1174
 *   the element's default value should be returned.
1175
 * @return
1176
 *   The data that will appear in the $form_state['values'] collection
1177
 *   for this element. Return nothing to use the default.
1178
 */
11792027
function form_type_checkboxes_value($form, $edit = FALSE) {
118086
  if ($edit === FALSE) {
118158
    $value = array();
118258
    $form += array('#default_value' => array());
118358
    foreach ($form['#default_value'] as $key) {
118426
      $value[$key] = 1;
118526
    }
118658
    return $value;
11870
  }
118828
  elseif (!isset($edit)) {
118910
    return array();
11900
  }
119118
}
1192
1193
/**
1194
 * Helper function to determine the value for a password_confirm form
1195
 * element.
1196
 *
1197
 * @param $form
1198
 *   The form element whose value is being populated.
1199
 * @param $edit
1200
 *   The incoming POST data to populate the form element. If this is
FALSE,
1201
 *   the element's default value should be returned.
1202
 * @return
1203
 *   The data that will appear in the $form_state['values'] collection
1204
 *   for this element. Return nothing to use the default.
1205
 */
12062027
function form_type_password_confirm_value($form, $edit = FALSE) {
120719
  if ($edit === FALSE) {
120812
    $form += array('#default_value' => array());
120912
    return $form['#default_value'] + array('pass1' => '', 'pass2' => '');
12100
  }
12117
}
1212
1213
/**
1214
 * Helper function to determine the value for a select form element.
1215
 *
1216
 * @param $form
1217
 *   The form element whose value is being populated.
1218
 * @param $edit
1219
 *   The incoming POST data to populate the form element. If this is
FALSE,
1220
 *   the element's default value should be returned.
1221
 * @return
1222
 *   The data that will appear in the $form_state['values'] collection
1223
 *   for this element. Return nothing to use the default.
1224
 */
12252027
function form_type_select_value($form, $edit = FALSE) {
1226440
  if ($edit !== FALSE) {
1227137
    if (isset($form['#multiple']) && $form['#multiple']) {
12283
      return (is_array($edit)) ? drupal_map_assoc($edit) : array();
12290
    }
1230
    else {
1231134
      return $edit;
1232
    }
12330
  }
1234328
}
1235
1236
/**
1237
 * Helper function to determine the value for a textfield form element.
1238
 *
1239
 * @param $form
1240
 *   The form element whose value is being populated.
1241
 * @param $edit
1242
 *   The incoming POST data to populate the form element. If this is
FALSE,
1243
 *   the element's default value should be returned.
1244
 * @return
1245
 *   The data that will appear in the $form_state['values'] collection
1246
 *   for this element. Return nothing to use the default.
1247
 */
12482027
function form_type_textfield_value($form, $edit = FALSE) {
12491070
  if ($edit !== FALSE) {
1250
    // Equate $edit to the form value to ensure it's marked for
1251
    // validation.
1252368
    return str_replace(array("\r", "\n"), '', $edit);
12530
  }
1254788
}
1255
1256
/**
1257
 * Helper function to determine the value for form's token value.
1258
 *
1259
 * @param $form
1260
 *   The form element whose value is being populated.
1261
 * @param $edit
1262
 *   The incoming POST data to populate the form element. If this is
FALSE,
1263
 *   the element's default value should be returned.
1264
 * @return
1265
 *   The data that will appear in the $form_state['values'] collection
1266
 *   for this element. Return nothing to use the default.
1267
 */
12682027
function form_type_token_value($form, $edit = FALSE) {
1269807
  if ($edit !== FALSE) {
1270306
    return (string)$edit;
12710
  }
1272522
}
1273
1274
/**
1275
 * Change submitted form values during the form processing cycle.
1276
 *
1277
 * Use this function to change the submitted value of a form item in the
1278
 * validation phase so that it persists in $form_state through to the
1279
 * submission handlers in the submission phase.
1280
 *
1281
 * Since $form_state['values'] can either be a flat array of values, or a
tree
1282
 * of nested values, some care must be taken when using this function.
1283
 * Specifically, $form_item['#parents'] is an array that describes the
branch of
1284
 * the tree whose value should be updated. For example, if we wanted to
update
1285
 * $form_state['values']['one']['two'] to 'new value', we'd pass in
1286
 * $form_item['#parents'] = array('one', 'two') and $value = 'new value'.
1287
 *
1288
 * @param $form_item
1289
 *   The form item that should have its value updated. Keys used:
#parents,
1290
 *   #value. In most cases you can just pass in the right element from the
$form
1291
 *   array.
1292
 * @param $value
1293
 *   The new value for the form item.
1294
 * @param $form_state
1295
 *   The array where the value change should be recorded.
1296
 */
12972027
function form_set_value($form_item, $value, &$form_state) {
12981408
  _form_set_value($form_state['values'], $form_item,
$form_item['#parents'], $value);
12991408
}
1300
1301
/**
1302
 * Helper function for form_set_value().
1303
 *
1304
 * We iterate over $parents and create nested arrays for them
1305
 * in $form_state['values'] if needed. Then we insert the value into
1306
 * the right array.
1307
 */
13082027
function _form_set_value(&$form_values, $form_item, $parents, $value) {
13091408
  $parent = array_shift($parents);
13101408
  if (empty($parents)) {
13111408
    $form_values[$parent] = $value;
13121408
  }
1313
  else {
1314426
    if (!isset($form_values[$parent])) {
1315325
      $form_values[$parent] = array();
1316325
    }
1317426
    _form_set_value($form_values[$parent], $form_item, $parents, $value);
1318
  }
13191408
}
1320
1321
/**
1322
 * Retrieve the default properties for the defined element type.
1323
 */
13242027
function _element_info($type, $refresh = NULL) {
13251570
  static $cache;
1326
1327
  $basic_defaults = array(
13281570
    '#description' => NULL,
13291570
    '#attributes' => array(),
13301570
    '#required' => FALSE,
13311570
    '#tree' => FALSE,
13321570
    '#parents' => array()
13331570
  );
13341570
  if (!isset($cache) || $refresh) {
13351570
    $cache = array();
13361570
    foreach (module_implements('elements') as $module) {
13371570
      $elements = module_invoke($module, 'elements');
13381570
      if (isset($elements) && is_array($elements)) {
13391570
        $cache = array_merge_recursive($cache, $elements);
13401570
      }
13411570
    }
13421570
    if (sizeof($cache)) {
13431570
      foreach ($cache as $element_type => $info) {
13441570
        $cache[$element_type] = array_merge_recursive($basic_defaults,
$info);
13451570
      }
13461570
    }
13471570
  }
1348
13491570
  return $cache[$type];
13500
}
1351
13522027
function form_options_flatten($array, $reset = TRUE) {
1353163
  static $return;
1354
1355163
  if ($reset) {
1356163
    $return = array();
1357163
  }
1358
1359163
  foreach ($array as $key => $value) {
1360163
    if (is_object($value)) {
136116
      form_options_flatten($value->option, FALSE);
136216
    }
1363163
    else if (is_array($value)) {
136418
      form_options_flatten($value, FALSE);
136518
    }
1366
    else {
1367163
      $return[$key] = 1;
1368
    }
1369163
  }
1370
1371163
  return $return;
13720
}
1373
1374
/**
1375
 * Format a dropdown menu or scrolling selection box.
1376
 *
1377
 * @param $element
1378
 *   An associative array containing the properties of the element.
1379
 *   Properties used: title, value, options, description, extra, multiple,
required
1380
 * @return
1381
 *   A themed HTML string representing the form element.
1382
 *
1383
 * @ingroup themeable
1384
 *
1385
 * It is possible to group options together; to do this, change the format
of
1386
 * $options to an associative array in which the keys are group labels, and
the
1387
 * values are associative arrays in the normal $options format.
1388
 */
13892027
function theme_select($element) {
1390321
  $select = '';
1391321
  $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';
1392321
  _form_set_class($element, array('form-select'));
1393321
  $multiple = $element['#multiple'];
1394321
  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>');
13950
}
1396
13972027
function form_select_options($element, $choices = NULL) {
1398321
  if (!isset($choices)) {
1399321
    $choices = $element['#options'];
1400321
  }
1401
  // array_key_exists() accommodates the rare event where
$element['#value'] is NULL.
1402
  // isset() fails in this situation.
1403321
  $value_valid = isset($element['#value']) || array_key_exists('#value',
$element);
1404321
  $value_is_array = is_array($element['#value']);
1405321
  $options = '';
1406321
  foreach ($choices as $key => $choice) {
1407321
    if (is_array($choice)) {
140843
      $options .= '<optgroup label="' . $key . '">';
140943
      $options .= form_select_options($element, $choice);
141043
      $options .= '</optgroup>';
141143
    }
1412321
    elseif (is_object($choice)) {
141322
      $options .= form_select_options($element, $choice->option);
141422
    }
1415
    else {
1416321
      $key = (string)$key;
1417321
      if ($value_valid && (!$value_is_array && (string)$element['#value']
=== $key || ($value_is_array && in_array($key, $element['#value'])))) {
1418263
        $selected = ' selected="selected"';
1419263
      }
1420
      else {
1421319
        $selected = '';
1422
      }
1423321
      $options .= '<option value="' . check_plain($key) . '"' . $selected .
'>' . check_plain($choice) . '</option>';
1424
    }
1425321
  }
1426321
  return $options;
14270
}
1428
1429
/**
1430
 * Traverses a select element's #option array looking for any values
1431
 * that hold the given key. Returns an array of indexes that match.
1432
 *
1433
 * This function is useful if you need to modify the options that are
1434
 * already in a form element; for example, to remove choices which are
1435
 * not valid because of additional filters imposed by another module.
1436
 * One example might be altering the choices in a taxonomy selector.
1437
 * To correctly handle the case of a multiple hierarchy taxonomy,
1438
 * #options arrays can now hold an array of objects, instead of a
1439
 * direct mapping of keys to labels, so that multiple choices in the
1440
 * selector can have the same key (and label). This makes it difficult
1441
 * to manipulate directly, which is why this helper function exists.
1442
 *
1443
 * This function does not support optgroups (when the elements of the
1444
 * #options array are themselves arrays), and will return FALSE if
1445
 * arrays are found. The caller must either flatten/restore or
1446
 * manually do their manipulations in this case, since returning the
1447
 * index is not sufficient, and supporting this would make the
1448
 * "helper" too complicated and cumbersome to be of any help.
1449
 *
1450
 * As usual with functions that can return array() or FALSE, do not
1451
 * forget to use === and !== if needed.
1452
 *
1453
 * @param $element
1454
 *   The select element to search.
1455
 * @param $key
1456
 *   The key to look for.
1457
 * @return
1458
 *   An array of indexes that match the given $key. Array will be
1459
 *   empty if no elements were found. FALSE if optgroups were found.
1460
 */
14612027
function form_get_options($element, $key) {
14620
  $keys = array();
14630
  foreach ($element['#options'] as $index => $choice) {
14640
    if (is_array($choice)) {
14650
      return FALSE;
14660
    }
14670
    else if (is_object($choice)) {
14680
      if (isset($choice->option[$key])) {
14690
        $keys[] = $index;
14700
      }
14710
    }
14720
    else if ($index == $key) {
14730
      $keys[] = $index;
14740
    }
14750
  }
14760
  return $keys;
14770
}
1478
1479
/**
1480
 * Format a group of form items.
1481
 *
1482
 * @param $element
1483
 *   An associative array containing the properties of the element.
1484
 *   Properties used: attributes, title, value, description, children,
collapsible, collapsed
1485
 * @return
1486
 *   A themed HTML string representing the form item group.
1487
 *
1488
 * @ingroup themeable
1489
 */
14902027
function theme_fieldset($element) {
1491234
  if ($element['#collapsible']) {
149292
    drupal_add_js('misc/collapse.js');
1493
149492
    if (!isset($element['#attributes']['class'])) {
149579
      $element['#attributes']['class'] = '';
149679
    }
1497
149892
    $element['#attributes']['class'] .= ' collapsible';
149992
    if ($element['#collapsed']) {
150059
      $element['#attributes']['class'] .= ' collapsed';
150159
    }
150292
  }
1503
1504234
  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";
15050
}
1506
1507
/**
1508
 * Format a radio button.
1509
 *
1510
 * @param $element
1511
 *   An associative array containing the properties of the element.
1512
 *   Properties used: required, return_value, value, attributes, title,
description
1513
 * @return
1514
 *   A themed HTML string representing the form item group.
1515
 *
1516
 * @ingroup themeable
1517
 */
15182027
function theme_radio($element) {
151952
  _form_set_class($element, array('form-radio'));
152052
  $output = '<input type="radio" ';
152152
  $output .= 'name="' . $element['#name'] . '" ';
152252
  $output .= 'value="' . $element['#return_value'] . '" ';
152352
  $output .= (check_plain($element['#value']) == $element['#return_value'])
? ' checked="checked" ' : ' ';
152452
  $output .= drupal_attributes($element['#attributes']) . ' />';
152552
  if (!is_null($element['#title'])) {
152652
    $output = '<label class="option">' . $output . ' ' . $element['#title']
. '</label>';
152752
  }
1528
152952
  unset($element['#title']);
153052
  return theme('form_element', $element, $output);
15310
}
1532
1533
/**
1534
 * Format a set of radio buttons.
1535
 *
1536
 * @param $element
1537
 *   An associative array containing the properties of the element.
1538
 *   Properties used: title, value, options, description, required and
attributes.
1539
 * @return
1540
 *   A themed HTML string representing the radio button set.
1541
 *
1542
 * @ingroup themeable
1543
 */
15442027
function theme_radios($element) {
154547
  $class = 'form-radios';
154647
  if (isset($element['#attributes']['class'])) {
15470
    $class .= ' ' . $element['#attributes']['class'];
15480
  }
154947
  $element['#children'] = '<div class="' . $class . '">' .
(!empty($element['#children']) ? $element['#children'] : '') . '</div>';
155047
  if ($element['#title'] || $element['#description']) {
155129
    unset($element['#id']);
155229
    return theme('form_element', $element, $element['#children']);
15530
  }
1554
  else {
155518
    return $element['#children'];
1556
  }
15570
}
1558
1559
/**
1560
 * Format a password_confirm item.
1561
 *
1562
 * @param $element
1563
 *   An associative array containing the properties of the element.
1564
 *   Properties used: title, value, id, required, error.
1565
 * @return
1566
 *   A themed HTML string representing the form item.
1567
 *
1568
 * @ingroup themeable
1569
 */
15702027
function theme_password_confirm($element) {
157113
  return theme('form_element', $element, $element['#children']);
15720
}
1573
1574
/**
1575
 * Expand a password_confirm field into two text boxes.
1576
 */
15772027
function form_process_password_confirm($element) {
157819
  $element['pass1'] =  array(
157919
    '#type' => 'password',
158019
    '#title' => t('Password'),
158119
    '#value' => empty($element['#value']) ? NULL :
$element['#value']['pass1'],
158219
    '#required' => $element['#required'],
158319
    '#attributes' => array('class' => 'password-field'),
1584
  );
158519
  $element['pass2'] =  array(
158619
    '#type' => 'password',
158719
    '#title' => t('Confirm password'),
158819
    '#value' => empty($element['#value']) ? NULL :
$element['#value']['pass2'],
158919
    '#required' => $element['#required'],
159019
    '#attributes' => array('class' => 'password-confirm'),
1591
  );
159219
  $element['#element_validate'] = array('password_confirm_validate');
159319
  $element['#tree'] = TRUE;
1594
159519
  if (isset($element['#size'])) {
159619
    $element['pass1']['#size'] = $element['pass2']['#size'] =
$element['#size'];
159719
  }
1598
159919
  return $element;
16000
}
1601
1602
/**
1603
 * Validate password_confirm element.
1604
 */
16052027
function password_confirm_validate($form, &$form_state) {
16067
  $pass1 = trim($form['pass1']['#value']);
16077
  if (!empty($pass1)) {
16082
    $pass2 = trim($form['pass2']['#value']);
16092
    if ($pass1 != $pass2) {
16100
      form_error($form, t('The specified passwords do not match.'));
16110
    }
16122
  }
16135
  elseif ($form['#required'] && !empty($form['#post'])) {
16140
    form_error($form, t('Password field is required.'));
16150
  }
1616
1617
  // Password field must be converted from a two-element array into a
single
1618
  // string regardless of validation results.
16197
  form_set_value($form['pass1'], NULL, $form_state);
16207
  form_set_value($form['pass2'], NULL, $form_state);
16217
  form_set_value($form, $pass1, $form_state);
1622
16237
  return $form;
1624
16250
}
1626
1627
/**
1628
 * Format a date selection element.
1629
 *
1630
 * @param $element
1631
 *   An associative array containing the properties of the element.
1632
 *   Properties used: title, value, options, description, required and
attributes.
1633
 * @return
1634
 *   A themed HTML string representing the date selection boxes.
1635
 *
1636
 * @ingroup themeable
1637
 */
16382027
function theme_date($element) {
16393
  return theme('form_element', $element, '<div class="container-inline">' .
$element['#children'] . '</div>');
16400
}
1641
1642
/**
1643
 * Roll out a single date element.
1644
 */
16452027
function form_process_date($element) {
1646
  // Default to current date
16474
  if (empty($element['#value'])) {
16482
    $element['#value'] = array('day' => format_date(time(), 'custom',
'j'),
16492
                            'month' => format_date(time(), 'custom', 'n'),
16502
                            'year' => format_date(time(), 'custom', 'Y'));
16512
  }
1652
16534
  $element['#tree'] = TRUE;
1654
1655
  // Determine the order of day, month, year in the site's chosen date
format.
16564
  $format = variable_get('date_format_short', 'm/d/Y - H:i');
16574
  $sort = array();
16584
  $sort['day'] = max(strpos($format, 'd'), strpos($format, 'j'));
16594
  $sort['month'] = max(strpos($format, 'm'), strpos($format, 'M'));
16604
  $sort['year'] = strpos($format, 'Y');
16614
  asort($sort);
16624
  $order = array_keys($sort);
1663
1664
  // Output multi-selector for date.
16654
  foreach ($order as $type) {
1666
    switch ($type) {
16674
      case 'day':
16684
        $options = drupal_map_assoc(range(1, 31));
16694
        break;
16704
      case 'month':
16714
        $options = drupal_map_assoc(range(1, 12), 'map_month');
16724
        break;
16734
      case 'year':
16744
        $options = drupal_map_assoc(range(1900, 2050));
16754
        break;
16760
    }
16774
    $parents = $element['#parents'];
16784
    $parents[] = $type;
16794
    $element[$type] = array(
16804
      '#type' => 'select',
16814
      '#value' => $element['#value'][$type],
16824
      '#attributes' => $element['#attributes'],
16834
      '#options' => $options,
1684
    );
16854
  }
1686
16874
  return $element;
16880
}
1689
1690
/**
1691
 * Validates the date type to stop dates like February 30, 2006.
1692
 */
16932027
function date_validate($form) {
16941
  if (!checkdate($form['#value']['month'], $form['#value']['day'],
$form['#value']['year'])) {
16950
    form_error($form, t('The specified date is invalid.'));
16960
  }
16971
}
1698
1699
/**
1700
 * Helper function for usage with drupal_map_assoc to display month names.
1701
 */
17022027
function map_month($month) {
17035
  return format_date(gmmktime(0, 0, 0, $month, 2, 1970), 'custom', 'M',
0);
17040
}
1705
1706
/**
1707
 * If no default value is set for weight select boxes, use 0.
1708
 */
17092027
function weight_value(&$form) {
17100
  if (isset($form['#default_value'])) {
17110
    $form['#value'] = $form['#default_value'];
17120
  }
1713
  else {
17140
    $form['#value'] = 0;
1715
  }
17160
}
1717
1718
/**
1719
 * Roll out a single radios element to a list of radios,
1720
 * using the options array as index.
1721
 */
17222027
function form_process_radios($element) {
1723218
  if (count($element['#options']) > 0) {
1724218
    foreach ($element['#options'] as $key => $choice) {
1725218
      if (!isset($element[$key])) {
1726
        // Generate the parents as the autogenerator does, so we will have
a
1727
        // unique id for each radio button.
1728218
        $parents_for_id = array_merge($element['#parents'], array($key));
1729218
        $element[$key] = array(
1730218
          '#type' => 'radio',
1731218
          '#title' => $choice,
1732218
          '#return_value' => check_plain($key),
1733218
          '#default_value' => isset($element['#default_value']) ?
$element['#default_value'] : NULL,
1734218
          '#attributes' => $element['#attributes'],
1735218
          '#parents' => $element['#parents'],
1736218
          '#id' => form_clean_id('edit-' . implode('-', $parents_for_id)),
1737
        );
1738218
      }
1739218
    }
1740218
  }
1741218
  return $element;
17420
}
1743
1744
/**
1745
 * Add AHAH information about a form element to the page to communicate
with
1746
 * javascript. If #ahah[path] is set on an element, this additional
javascript is
1747
 * added to the page header to attach the AHAH behaviors. See ahah.js for
more
1748
 * information.
1749
 *
1750
 * @param $element
1751
 *   An associative array containing the properties of the element.
1752
 *   Properties used: ahah_event, ahah_path, ahah_wrapper,
ahah_parameters,
1753
 *   ahah_effect.
1754
 * @return
1755
 *   None. Additional code is added to the header of the page using
1756
 *   drupal_add_js.
1757
 */
17582027
function form_process_ahah($element) {
17591408
  static $js_added = array();
1760
  // Add a reasonable default event handler if none specified.
17611408
  if (isset($element['#ahah']['path']) &&
!isset($element['#ahah']['event'])) {
176239
    switch ($element['#type']) {
176339
      case 'submit':
176439
      case 'button':
176539
      case 'image_button':
1766
        // Use the mousedown instead of the click event because form
1767
        // submission via pressing the enter key triggers a click event on
1768
        // submit inputs, inappropriately triggering AHAH behaviors.
176925
        $element['#ahah']['event'] = 'mousedown';
1770
        // Attach an additional event handler so that AHAH behaviours
1771
        // can be triggered still via keyboard input.
177225
        $element['#ahah']['keypress'] = TRUE;
177325
        break;
177414
      case 'password':
177514
      case 'textfield':
177614
      case 'textarea':
17770
        $element['#ahah']['event'] = 'blur';
17780
        break;
177914
      case 'radio':
178014
      case 'checkbox':
178114
      case 'select':
178214
        $element['#ahah']['event'] = 'change';
178314
        break;
17840
    }
178539
  }
1786
1787
  // Adding the same javascript settings twice will cause a recursion
error,
1788
  // we avoid the problem by checking if the javascript has already been
added.
17891408
  if (isset($element['#ahah']['path']) && isset($element['#ahah']['event'])
&& !isset($js_added[$element['#id']])) {
179039
    drupal_add_js('misc/jquery.form.js');
179139
    drupal_add_js('misc/ahah.js');
1792
1793
    $ahah_binding = array(
179439
      'url'      => url($element['#ahah']['path']),
179539
      'event'    => $element['#ahah']['event'],
179639
      'keypress' => empty($element['#ahah']['keypress']) ? NULL :
$element['#ahah']['keypress'],
179739
      'wrapper'  => empty($element['#ahah']['wrapper']) ? NULL :
$element['#ahah']['wrapper'],
179839
      'selector' => empty($element['#ahah']['selector']) ? '#' .
$element['#id'] : $element['#ahah']['selector'],
179939
      'effect'   => empty($element['#ahah']['effect']) ? 'none' :
$element['#ahah']['effect'],
180039
      'method'   => empty($element['#ahah']['method']) ? 'replace' :
$element['#ahah']['method'],
180139
      'progress' => empty($element['#ahah']['progress']) ? array('type' =>
'throbber') : $element['#ahah']['progress'],
180239
      'button'   => isset($element['#executes_submit_callback']) ?
array($element['#name'] => $element['#value']) : FALSE,
180339
    );
1804
1805
    // Convert a simple #ahah[progress] type string into an array.
180639
    if (is_string($ahah_binding['progress'])) {
18070
      $ahah_binding['progress'] = array('type' =>
$ahah_binding['progress']);
18080
    }
1809
    // Change progress path to a full url.
181039
    if (isset($ahah_binding['progress']['path'])) {
18110
      $ahah_binding['progress']['url'] =
url($ahah_binding['progress']['path']);
18120
    }
1813
1814
    // Add progress.js if we're doing a bar display.
181539
    if ($ahah_binding['progress']['type'] == 'bar') {
181614
      drupal_add_js('misc/progress.js');
181714
    }
1818
181939
    drupal_add_js(array('ahah' => array($element['#id'] => $ahah_binding)),
'setting');
1820
182139
    $js_added[$element['#id']] = TRUE;
182239
    $element['#cache'] = TRUE;
182339
  }
18241408
  return $element;
18250
}
1826
1827
/**
1828
 * Format a form item.
1829
 *
1830
 * @param $element
1831
 *   An associative array containing the properties of the element.
1832
 *   Properties used:  title, value, description, required, error
1833
 * @return
1834
 *   A themed HTML string representing the form item.
1835
 *
1836
 * @ingroup themeable
1837
 */
18382027
function theme_item($element) {
183938
  return theme('form_element', $element, $element['#markup'] .
(!empty($element['#children']) ? $element['#children'] : ''));
18400
}
1841
1842
/**
1843
 * Format a checkbox.
1844
 *
1845
 * @param $element
1846
 *   An associative array containing the properties of the element.
1847
 *   Properties used:  title, value, return_value, description, required
1848
 * @return
1849
 *   A themed HTML string representing the checkbox.
1850
 *
1851
 * @ingroup themeable
1852
 */
18532027
function theme_checkbox($element) {
1854241
  _form_set_class($element, array('form-checkbox'));
1855241
  $checkbox = '<input ';
1856241
  $checkbox .= 'type="checkbox" ';
1857241
  $checkbox .= 'name="' . $element['#name'] . '" ';
1858241
  $checkbox .= 'id="' . $element['#id'] . '" ' ;
1859241
  $checkbox .= 'value="' . $element['#return_value'] . '" ';
1860241
  $checkbox .= $element['#value'] ? ' checked="checked" ' : ' ';
1861241
  $checkbox .= drupal_attributes($element['#attributes']) . ' />';
1862
1863241
  if (!is_null($element['#title'])) {
1864205
    $checkbox = '<label class="option">' . $checkbox . ' ' .
$element['#title'] . '</label>';
1865205
  }
1866
1867241
  unset($element['#title']);
1868241
  return theme('form_element', $element, $checkbox);
18690
}
1870
1871
/**
1872
 * Format a set of checkboxes.
1873
 *
1874
 * @param $element
1875
 *   An associative array containing the properties of the element.
1876
 * @return
1877
 *   A themed HTML string representing the checkbox set.
1878
 *
1879
 * @ingroup themeable
1880
 */
18812027
function theme_checkboxes($element) {
188261
  $class = 'form-checkboxes';
188361
  if (isset($element['#attributes']['class'])) {
18840
    $class .= ' ' . $element['#attributes']['class'];
18850
  }
188661
  $element['#children'] = '<div class="' . $class . '">' .
(!empty($element['#children']) ? $element['#children'] : '') . '</div>';
188761
  if ($element['#title'] || $element['#description']) {
188824
    unset($element['#id']);
188924
    return theme('form_element', $element, $element['#children']);
18900
  }
1891
  else {
189237
    return $element['#children'];
1893
  }
18940
}
1895
18962027
function form_process_checkboxes($element) {
189786
  $value = is_array($element['#value']) ? $element['#value'] : array();
189886
  $element['#tree'] = TRUE;
189986
  if (count($element['#options']) > 0) {
190086
    if (!isset($element['#default_value']) || $element['#default_value'] ==
0) {
190135
      $element['#default_value'] = array();
190235
    }
190386
    foreach ($element['#options'] as $key => $choice) {
190486
      if (!isset($element[$key])) {
190586
        $element[$key] = array('#type' => 'checkbox', '#processed' => TRUE,
'#title' => $choice, '#return_value' => $key, '#default_value' =>
isset($value[$key]), '#attributes' => $element['#attributes']);
190686
      }
190786
    }
190886
  }
190986
  return $element;
19100
}
1911
1912
/**
1913
 * Theme a form submit button.
1914
 *
1915
 * @ingroup themeable
1916
 */
19172027
function theme_submit($element) {
1918958
  return theme('button', $element);
19190
}
1920
1921
/**
1922
 * Theme a form button.
1923
 *
1924
 * @ingroup themeable
1925
 */
19262027
function theme_button($element) {
1927
  // Make sure not to overwrite classes.
1928966
  if (isset($element['#attributes']['class'])) {
19290
    $element['#attributes']['class'] = 'form-' . $element['#button_type'] .
' ' . $element['#attributes']['class'];
19300
  }
1931
  else {
1932966
    $element['#attributes']['class'] = 'form-' . $element['#button_type'];
1933
  }
1934
1935966
  return '<input type="submit" ' . (empty($element['#name']) ? '' :
'name="' . $element['#name'] . '" ') . 'id="' . $element['#id'] . '"
value="' . check_plain($element['#value']) . '" ' .
drupal_attributes($element['#attributes']) . " />\n";
19360
}
1937
1938
/**
1939
 * Theme a form image button.
1940
 *
1941
 * @ingroup themeable
1942
 */
19432027
function theme_image_button($element) {
1944
  // Make sure not to overwrite classes.
19450
  if (isset($element['#attributes']['class'])) {
19460
    $element['#attributes']['class'] = 'form-' . $element['#button_type'] .
' ' . $element['#attributes']['class'];
19470
  }
1948
  else {
19490
    $element['#attributes']['class'] = 'form-' . $element['#button_type'];
1950
  }
1951
19520
  return '<input type="image" name="' . $element['#name'] . '" ' .
19530
    (!empty($element['#value']) ? ('value="' .
check_plain($element['#value']) . '" ') : '') .
19540
    'id="' . $element['#id'] . '" ' .
19550
    drupal_attributes($element['#attributes']) .
19560
    ' src="' . base_path() . $element['#src'] . '" ' .
19570
    (!empty($element['#title']) ? 'alt="' . check_plain($element['#title'])
. '" title="' . check_plain($element['#title']) . '" ' : '' ) .
19580
    "/>\n";
19590
}
1960
1961
/**
1962
 * Format a hidden form field.
1963
 *
1964
 * @param $element
1965
 *   An associative array containing the properties of the element.
1966
 *   Properties used:  value, edit
1967
 * @return
1968
 *   A themed HTML string representing the hidden form field.
1969
 *
1970
 * @ingroup themeable
1971
 */
19722027
function theme_hidden($element) {
1973993
  return '<input type="hidden" name="' . $element['#name'] . '" id="' .
$element['#id'] . '" value="' . check_plain($element['#value']) . "\" " .
drupal_attributes($element['#attributes']) . " />\n";
19740
}
1975
1976
/**
1977
 * Format a form token.
1978
 *
1979
 * @ingroup themeable
1980
 */
19812027
function theme_token($element) {
1982552
  return theme('hidden', $element);
19830
}
1984
1985
/**
1986
 * Format a textfield.
1987
 *
1988
 * @param $element
1989
 *   An associative array containing the properties of the element.
1990
 *   Properties used:  title, value, description, size, maxlength,
required, attributes autocomplete_path
1991
 * @return
1992
 *   A themed HTML string representing the textfield.
1993
 *
1994
 * @ingroup themeable
1995
 */
19962027
function theme_textfield($element) {
1997723
  $size = empty($element['#size']) ? '' : ' size="' . $element['#size'] .
'"';
1998723
  $maxlength = empty($element['#maxlength']) ? '' : ' maxlength="' .
$element['#maxlength'] . '"';
1999723
  $class = array('form-text');
2000723
  $extra = '';
2001723
  $output = '';
2002
2003723
  if ($element['#autocomplete_path']) {
200419
    drupal_add_js('misc/autocomplete.js');
200519
    $class[] = 'form-autocomplete';
200619
    $extra =  '<input class="autocomplete" type="hidden" id="' .
$element['#id'] . '-autocomplete" value="' .
check_url(url($element['#autocomplete_path'], array('absolute' => TRUE))) .
'" disabled="disabled" />';
200719
  }
2008723
  _form_set_class($element, $class);
2009
2010723
  if (isset($element['#field_prefix'])) {
20114
    $output .= '<span class="field-prefix">' . $element['#field_prefix'] .
'</span> ';
20124
  }
2013
2014723
  $output .= '<input type="text"' . $maxlength . ' name="' .
$element['#name'] . '" id="' . $element['#id'] . '"' . $size . ' value="' .
check_plain($element['#value']) . '"' .
drupal_attributes($element['#attributes']) . ' />';
2015
2016723
  if (isset($element['#field_suffix'])) {
20176
    $output .= ' <span class="field-suffix">' . $element['#field_suffix'] .
'</span>';
20186
  }
2019
2020723
  return theme('form_element', $element, $output) . $extra;
20210
}
2022
2023
/**
2024
 * Format a form.
2025
 *
2026
 * @param $element
2027
 *   An associative array containing the properties of the element.
2028
 *   Properties used: action, method, attributes, children
2029
 * @return
2030
 *   A themed HTML string representing the form.
2031
 *
2032
 * @ingroup themeable
2033
 */
20342027
function theme_form($element) {
2035
  // Anonymous div to satisfy XHTML compliance.
2036993
  $action = $element['#action'] ? 'action="' .
check_url($element['#action']) . '" ' : '';
2037993
  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";
20380
}
2039
2040
/**
2041
 * Format a textarea.
2042
 *
2043
 * @param $element
2044
 *   An associative array containing the properties of the element.
2045
 *   Properties used: title, value, description, rows, cols, required,
attributes
2046
 * @return
2047
 *   A themed HTML string representing the textarea.
2048
 *
2049
 * @ingroup themeable
2050
 */
20512027
function theme_textarea($element) {
2052207
  $class = array('form-textarea');
2053
2054
  // Add teaser behavior (must come before resizable)
2055207
  if (!empty($element['#teaser'])) {
205679
    drupal_add_js('misc/teaser.js');
2057
    // Note: arrays are merged in drupal_get_js().
205879
    drupal_add_js(array('teaserCheckbox' => array($element['#id'] =>
$element['#teaser_checkbox'])), 'setting');
205979
    drupal_add_js(array('teaser' => array($element['#id'] =>
$element['#teaser'])), 'setting');
206079
    $class[] = 'teaser';
206179
  }
2062
2063
  // Add resizable behavior
2064207
  if ($element['#resizable'] !== FALSE) {
2065207
    drupal_add_js('misc/textarea.js');
2066207
    $class[] = 'resizable';
2067207
  }
2068
2069207
  _form_set_class($element, $class);
2070207
  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>');
20710
}
2072
2073
/**
2074
 * Format HTML markup for use in forms.
2075
 *
2076
 * This is used in more advanced forms, such as theme selection and filter
format.
2077
 *
2078
 * @param $element
2079
 *   An associative array containing the properties of the element.
2080
 *   Properties used: value, children.
2081
 * @return
2082
 *   A themed HTML string representing the HTML markup.
2083
 *
2084
 * @ingroup themeable
2085
 */
2086
20872027
function theme_markup($element) {
2088937
  return (isset($element['#markup']) ? $element['#markup'] : '') .
(isset($element['#children']) ? $element['#children'] : '');
20890
}
2090
2091
/**
2092
 * Format a password field.
2093
 *
2094
 * @param $element
2095
 *   An associative array containing the properties of the element.
2096
 *   Properties used:  title, value, description, size, maxlength,
required, attributes
2097
 * @return
2098
 *   A themed HTML string representing the form.
2099
 *
2100
 * @ingroup themeable
2101
 */
21022027
function theme_password($element) {
2103451
  $size = $element['#size'] ? ' size="' . $element['#size'] . '" ' : '';
2104451
  $maxlength = $element['#maxlength'] ? ' maxlength="' .
$element['#maxlength'] . '" ' : '';
2105
2106451
  _form_set_class($element, array('form-text'));
2107451
  $output = '<input type="password" name="' . $element['#name'] . '" id="'
. $element['#id'] . '" ' . $maxlength . $size .
drupal_attributes($element['#attributes']) . ' />';
2108451
  return theme('form_element', $element, $output);
21090
}
2110
2111
/**
2112
 * Expand weight elements into selects.
2113
 */
21142027
function form_process_weight($element) {
2115329
  for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) {
2116329
    $weights[$n] = $n;
2117329
  }
2118329
  $element['#options'] = $weights;
2119329
  $element['#type'] = 'select';
2120329
  $element['#is_weight'] = TRUE;
2121329
  $element += _element_info('select');
2122329
  return $element;
21230
}
2124
2125
/**
2126
 * Format a file upload field.
2127
 *
2128
 * @param $title
2129
 *   The label for the file upload field.
2130
 * @param $name
2131
 *   The internal name used to refer to the field.
2132
 * @param $size
2133
 *   A measure of the visible size of the field (passed directly to HTML).
2134
 * @param $description
2135
 *   Explanatory text to display after the form item.
2136
 * @param $required
2137
 *   Whether the user must upload a file to the field.
2138
 * @return
2139
 *   A themed HTML string representing the field.
2140
 *
2141
 * @ingroup themeable
2142
 *
2143
 * For assistance with handling the uploaded file correctly, see the API
2144
 * provided by file.inc.
2145
 */
21462027
function theme_file($element) {
214728
  _form_set_class($element, array('form-file'));
214828
  return theme('form_element', $element, '<input type="file" name="' .
$element['#name'] . '"' . ($element['#attributes'] ? ' ' .
drupal_attributes($element['#attributes']) : '') . ' id="' .
$element['#id'] . '" size="' . $element['#size'] . "\" />\n");
21490
}
2150
2151
/**
2152
 * Return a themed form element.
2153
 *
2154
 * @param element
2155
 *   An associative array containing the properties of the element.
2156
 *   Properties used: title, description, id, required
2157
 * @param $value
2158
 *   The form element's data.
2159
 * @return
2160
 *   A string representing the form element.
2161
 *
2162
 * @ingroup themeable
2163
 */
21642027
function theme_form_element($element, $value) {
2165
  // This is also used in the installer, pre-database setup.
2166911
  $t = get_t();
2167
2168911
  $output = '<div class="form-item"';
2169911
  if (!empty($element['#id'])) {
2170911
    $output .= ' id="' . $element['#id'] . '-wrapper"';
2171911
  }
2172911
  $output .= ">\n";
2173911
  $required = !empty($element['#required']) ? '<span class="form-required"
title="' . $t('This field is required.') . '">*</span>' : '';
2174
2175911
  if (!empty($element['#title'])) {
2176752
    $title = $element['#title'];
2177752
    if (!empty($element['#id'])) {
2178752
      $output .= ' <label for="' . $element['#id'] . '">' . $t('!title:
!required', array('!title' => filter_xss_admin($title), '!required' =>
$required)) . "</label>\n";
2179752
    }
2180
    else {
218170
      $output .= ' <label>' . $t('!title: !required', array('!title' =>
filter_xss_admin($title), '!required' => $required)) . "</label>\n";
2182
    }
2183752
  }
2184
2185911
  $output .= " $value\n";
2186
2187911
  if (!empty($element['#description'])) {
2188513
    $output .= ' <div class="description">' . $element['#description'] .
"</div>\n";
2189513
  }
2190
2191911
  $output .= "</div>\n";
2192
2193911
  return $output;
21940
}
2195
2196
/**
2197
 * Sets a form element's class attribute.
2198
 *
2199
 * Adds 'required' and 'error' classes as needed.
2200
 *
2201
 * @param &$element
2202
 *   The form element.
2203
 * @param $name
2204
 *   Array of new class names to be added.
2205
 */
22062027
function _form_set_class(&$element, $class = array()) {
2207911
  if ($element['#required']) {
2208662
    $class[] = 'required';
2209662
  }
2210911
  if (form_get_error($element)) {
221125
    $class[] = 'error';
221225
  }
2213911
  if (isset($element['#attributes']['class'])) {
2214130
    $class[] = $element['#attributes']['class'];
2215130
  }
2216911
  $element['#attributes']['class'] = implode(' ', $class);
2217911
}
2218
2219
/**
2220
 * Prepare an HTML ID attribute string for a form item.
2221
 *
2222
 * Remove invalid characters and guarantee uniqueness.
2223
 *
2224
 * @param $id
2225
 *   The ID to clean.
2226
 * @param $flush
2227
 *   If set to TRUE, the function will flush and reset the static array
2228
 *   which is built to test the uniqueness of element IDs. This is only
2229
 *   used if a form has completed the validation process. This parameter
2230
 *   should never be set to TRUE if this function is being called to
2231
 *   assign an ID to the #ID element.
2232
 * @return
2233
 *   The cleaned ID.
2234
 */
22352027
function form_clean_id($id = NULL, $flush = FALSE) {
22361905
  static $seen_ids = array();
2237
22381905
  if ($flush) {
2239479
    $seen_ids = array();
2240479
    return;
22410
  }
22421905
  $id = str_replace(array('][', '_', ' '), '-', $id);
2243
2244
  // Ensure IDs are unique. The first occurrence is held but left alone.
2245
  // Subsequent occurrences get a number appended to them. This
incrementing
2246
  // will almost certainly break code that relies on explicit HTML IDs in
2247
  // forms that appear more than once on the page, but the alternative is
2248
  // outputting duplicate IDs, which would break JS code and XHTML
2249
  // validity anyways. For now, it's an acceptable stopgap solution.
22501905
  if (isset($seen_ids[$id])) {
225191
    $id = $id . '-' . $seen_ids[$id]++;
225291
  }
2253
  else {
22541905
    $seen_ids[$id] = 1;
2255
  }
2256
22571905
  return $id;
22580
}
2259
2260
/**
2261
 * @} End of "defgroup form_api".
2262
 */
2263
2264
/**
2265
 * @defgroup batch Batch operations
2266
 * @{
2267
 * Functions allowing forms processing to be spread out over several page
2268
 * requests, thus ensuring that the processing does not get interrupted
2269
 * because of a PHP timeout, while allowing the user to receive feedback
2270
 * on the progress of the ongoing operations.
2271
 *
2272
 * The API is primarily designed to integrate nicely with the Form API
2273
 * workflow, but can also be used by non-FAPI scripts (like update.php)
2274
 * or even simple page callbacks (which should probably be used
sparingly).
2275
 *
2276
 * Example:
2277
 * @code
2278
 * $batch = array(
2279
 *   'title' => t('Exporting'),
2280
 *   'operations' => array(
2281
 *     array('my_function_1', array($account->uid, 'story')),
2282
 *     array('my_function_2', array()),
2283
 *   ),
2284
 *   'finished' => 'my_finished_callback',
2285
 * );
2286
 * batch_set($batch);
2287
 * // only needed if not inside a form _submit handler :
2288
 * batch_process();
2289
 * @endcode
2290
 *
2291
 * Sample batch operations:
2292
 * @code
2293
 * // Simple and artificial: load a node of a given type for a given user
2294
 * function my_function_1($uid, $type, &$context) {
2295
 *   // The $context array gathers batch context information about the
execution (read),
2296
 *   // as well as 'return values' for the current operation (write)
2297
 *   // The following keys are provided :
2298
 *   // 'results' (read / write): The array of results gathered so far by
2299
 *   //   the batch processing, for the current operation to append its
own.
2300
 *   // 'message' (write): A text message displayed in the progress page.
2301
 *   // The following keys allow for multi-step operations :
2302
 *   // 'sandbox' (read / write): An array that can be freely used to
2303
 *   //   store persistent data between iterations. It is recommended to
2304
 *   //   use this instead of $_SESSION, which is unsafe if the user
2305
 *   //   continues browsing in a separate window while the batch is
processing.
2306
 *   // 'finished' (write): A float number between 0 and 1 informing
2307
 *   //   the processing engine of the completion level for the operation.
2308
 *   //   1 (or no value explicitly set) means the operation is finished
2309
 *   //   and the batch processing can continue to the next operation.
2310
 *
2311
 *   $node = node_load(array('uid' => $uid, 'type' => $type));
2312
 *   $context['results'][] = $node->nid . ' : ' . $node->title;
2313
 *   $context['message'] = $node->title;
2314
 * }
2315
 *
2316
 * // More advanced example: multi-step operation - load all nodes, five by
five
2317
 * function my_function_2(&$context) {
2318
 *   if (empty($context['sandbox'])) {
2319
 *     $context['sandbox']['progress'] = 0;
2320
 *     $context['sandbox']['current_node'] = 0;
2321
 *     $context['sandbox']['max'] = db_result(db_query('SELECT
COUNT(DISTINCT nid) FROM {node}'));
2322
 *   }
2323
 *   $limit = 5;
2324
 *   $result = db_query_range("SELECT nid FROM {node} WHERE nid > %d ORDER
BY nid ASC", $context['sandbox']['current_node'], 0, $limit);
2325
 *   while ($row = db_fetch_array($result)) {
2326
 *     $node = node_load($row['nid'], NULL, TRUE);
2327
 *     $context['results'][] = $node->nid . ' : ' . $node->title;
2328
 *     $context['sandbox']['progress']++;
2329
 *     $context['sandbox']['current_node'] = $node->nid;
2330
 *     $context['message'] = $node->title;
2331
 *   }
2332
 *   if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
2333
 *     $context['finished'] = $context['sandbox']['progress'] /
$context['sandbox']['max'];
2334
 *   }
2335
 * }
2336
 * @endcode
2337
 *
2338
 * Sample 'finished' callback:
2339
 * @code
2340
 * function batch_test_finished($success, $results, $operations) {
2341
 *   if ($success) {
2342
 *     $message = format_plural(count($results), 'One post processed.',
'@count posts processed.');
2343
 *   }
2344
 *   else {
2345
 *     $message = t('Finished with an error.');
2346
 *   }
2347
 *   drupal_set_message($message);
2348
 *   // Providing data for the redirected page is done through $_SESSION.
2349
 *   foreach ($results as $result) {
2350
 *     $items[] = t('Loaded node %title.', array('%title' => $result));
2351
 *   }
2352
 *   $_SESSION['my_batch_results'] = $items;
2353
 * }
2354
 * @endcode
2355
 */
2356
2357
/**
2358
 * Open a new batch.
2359
 *
2360
 * @param $batch
2361
 *   An array defining the batch. The following keys can be used:
2362
 *     'operations': an array of function calls to be performed.
2363
 *        Example:
2364
 *        @code
2365
 *        array(
2366
 *          array('my_function_1', array($arg1)),
2367
 *          array('my_function_2', array($arg2_1, $arg2_2)),
2368
 *        )
2369
 *        @endcode
2370
 *     All the other values below are optional.
2371
 *     batch_init() provides default values for the messages.
2372
 *     'title': title for the progress page.
2373
 *       Defaults to t('Processing').
2374
 *     'init_message': message displayed while the processing is
initialized.
2375
 *       Defaults to t('Initializing.').
2376
 *     'progress_message': message displayed while processing the batch.
2377
 *       Available placeholders are @current, @remaining, @total and
@percent.
2378
 *       Defaults to t('Remaining @remaining of @total.').
2379
 *     'error_message': message displayed if an error occurred while
processing
2380
 *       the batch.
2381
 *       Defaults to t('An error has occurred.').
2382
 *     'finished': the name of a function to be executed after the batch
has
2383
 *       completed. This should be used to perform any result massaging
that
2384
 *       may be needed, and possibly save data in $_SESSION for display
after
2385
 *       final page redirection.
2386
 *     'file': the path to the file containing the definitions of the
2387
 *       'operations' and 'finished' functions, for instance if they don't
2388
 *       reside in the original '.module' file. The path should be relative
to
2389
 *       the base_path(), and thus should be built using
drupal_get_path().
2390
 *     'css' : an array of paths to CSS files to be used on the progress
page.
2391
 *
2392
 * Operations are added as new batch sets. Batch sets are used to ensure
2393
 * clean code independence, ensuring that several batches submitted by
2394
 * different parts of the code (core / contrib modules) can be processed
2395
 * correctly while not interfering or having to cope with each other. Each
2396
 * batch set gets to specify his own UI messages, operates on its own set
2397
 * of operations and results, and triggers its own 'finished' callback.
2398
 * Batch sets are processed sequentially, with the progress bar starting
2399
 * fresh for every new set.
2400
 */
24012027
function batch_set($batch_definition) {
24020
  if ($batch_definition) {
24030
    $batch =& batch_get();
2404
    // Initialize the batch
24050
    if (empty($batch)) {
2406
      $batch = array(
24070
        'sets' => array(),
24080
      );
24090
    }
2410
2411
    $init = array(
24120
      'sandbox' => array(),
24130
      'results' => array(),
24140
      'success' => FALSE,
24150
    );
2416
    // Use get_t() to allow batches at install time.
24170
    $t = get_t();
2418
    $defaults = array(
24190
      'title' => $t('Processing'),
24200
      'init_message' => $t('Initializing.'),
24210
      'progress_message' => $t('Remaining @remaining of @total.'),
24220
      'error_message' => $t('An error has occurred.'),
24230
      'css' => array(),
24240
    );
24250
    $batch_set = $init + $batch_definition + $defaults;
2426
2427
    // Tweak init_message to avoid the bottom of the page flickering down
after init phase.
24280
    $batch_set['init_message'] .= '<br/>&nbsp;';
24290
    $batch_set['total'] = count($batch_set['operations']);
2430
2431
    // If the batch is being processed (meaning we are executing a stored
submit handler),
2432
    // insert the new set after the current one.
24330
    if (isset($batch['current_set'])) {
2434
      // array_insert does not exist...
24350
      $slice1 = array_slice($batch['sets'], 0, $batch['current_set'] + 1);
24360
      $slice2 = array_slice($batch['sets'], $batch['current_set'] + 1);
24370
      $batch['sets'] = array_merge($slice1, array($batch_set), $slice2);
24380
    }
2439
    else {
24400
      $batch['sets'][] = $batch_set;
2441
    }
24420
  }
24430
}
2444
2445
/**
2446
 * Process the batch.
2447
 *
2448
 * Unless the batch has been marked with 'progressive' = FALSE, the
function
2449
 * issues a drupal_goto and thus ends page execution.
2450
 *
2451
 * This function is not needed in form submit handlers; Form API takes
care
2452
 * of batches that were set during form submission.
2453
 *
2454
 * @param $redirect
2455
 *   (optional) Path to redirect to when the batch has finished
processing.
2456
 * @param $url
2457
 *   (optional - should only be used for separate scripts like update.php)
2458
 *   URL of the batch processing page.
2459
 */
24602027
function batch_process($redirect = NULL, $url = NULL) {
24610
  $batch =& batch_get();
2462
24630
  if (isset($batch)) {
2464
    // Add process information
24650
    $url = isset($url) ? $url : 'batch';
2466
    $process_info = array(
24670
      'current_set' => 0,
24680
      'progressive' => TRUE,
24690
      'url' => isset($url) ? $url : 'batch',
24700
      'source_page' => $_GET['q'],
24710
      'redirect' => $redirect,
24720
    );
24730
    $batch += $process_info;
2474
24750
    if ($batch['progressive']) {
2476
      // Clear the way for the drupal_goto redirection to the batch
processing
2477
      // page, by saving and unsetting the 'destination' if any, on both
places
2478
      // drupal_goto looks for it.
24790
      if (isset($_REQUEST['destination'])) {
24800
        $batch['destination'] = $_REQUEST['destination'];
24810
        unset($_REQUEST['destination']);
24820
      }
24830
      elseif (isset($_REQUEST['edit']['destination'])) {
24840
        $batch['destination'] = $_REQUEST['edit']['destination'];
24850
        unset($_REQUEST['edit']['destination']);
24860
      }
2487
2488
      // Initiate db storage in order to get a batch id. We have to
provide
2489
      // at least an empty string for the (not null) 'token' column.
24900
      db_query("INSERT INTO {batch} (token, timestamp) VALUES ('', %d)",
time());
24910
      $batch['id'] = db_last_insert_id('batch', 'bid');
2492
2493
      // Now that we have a batch id, we can generate the redirection link
in
2494
      // the generic error message.
24950
      $t = get_t();
24960
      $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')))));
2497
2498
      // Actually store the batch data and the token generated form the
batch id.
24990
      db_query("UPDATE {batch} SET token = '%s', batch = '%s' WHERE bid =
%d", drupal_get_token($batch['id']), serialize($batch), $batch['id']);
2500
25010
      drupal_goto($batch['url'], 'op=start&id=' . $batch['id']);
25020
    }
2503
    else {
2504
      // Non-progressive execution: bypass the whole progressbar workflow
2505
      // and execute the batch in one pass.
25060
      require_once './includes/batch.inc';
25070
      _batch_process();
2508
    }
25090
  }
25100
}
2511
2512
/**
2513
 * Retrieve the current batch.
2514
 */
25152027
function &batch_get() {
2516427
  static $batch = array();
2517427
  return $batch;
25180
}
2519
2520
/**
2521
 * @} End of "defgroup batch".
2522
 */
25232027