Simpletest Coverage - includes/form.inc

1 <?php
2 // $Id: form.inc,v 1.356 2009/08/12 11:45:14 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 and processing an HTML
33 * 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_form_submit() 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 * Wrapper for drupal_build_form() for use when $form_state is not needed.
51 *
52 * @param $form_id
53 * The unique string identifying the desired form. If a function with that
54 * name exists, it is called to build the form array. Modules that need to
55 * generate the same form (or very similar forms) using different $form_ids
56 * can implement hook_forms(), which maps different $form_id values to the
57 * proper form constructor function. Examples may be found in node_forms(),
58 * search_forms(), and user_forms().
59 * @param ...
60 * Any additional arguments are passed on to the functions called by
61 * drupal_get_form(), including the unique form constructor function. For
62 * example, the node_edit form requires that a node object is passed in here
63 * when it is called.
64 * @return
65 * The form array.
66 *
67 * @see drupal_build_form()
68 */
69 function drupal_get_form($form_id) {
70 $form_state = array();
71
72 $args = func_get_args();
73 // Remove $form_id from the arguments.
74 array_shift($args);
75 $form_state['args'] = $args;
76
77 return drupal_build_form($form_id, $form_state);
78 }
79
80 /**
81 * Build and process a form based on a form id.
82 *
83 * The form may also be retrieved from the cache if the form was built in a
84 * previous page-load. The form is then passed on for processing, validation
85 * and submission if there is proper input.
86 *
87 * @param $form_id
88 * The unique string identifying the desired form. If a function with that
89 * name exists, it is called to build the form array. Modules that need to
90 * generate the same form (or very similar forms) using different $form_ids
91 * can implement hook_forms(), which maps different $form_id values to the
92 * proper form constructor function. Examples may be found in node_forms(),
93 * search_forms(), and user_forms().
94 * @param &$form_state
95 * An array which stores information about the form. This is passed as a
96 * reference so that the caller can use it to examine what the form changed
97 * when the form submission process is complete.
98 *
99 * The following parameters may be set in $form_state to affect how the form
100 * is rendered:
101 * - args: An array of arguments to pass to the form builder.
102 * - input: An array of input that corresponds to $_POST or $_GET, depending
103 * on the 'method' chosen (see below).
104 * - method: The HTTP form method to use for finding the input for this form.
105 * May be 'post' or 'get'. Defaults to 'post'. Note that 'get' method
106 * forms do not use form ids so are always considered to be submitted, which
107 * can have unexpected effects. The 'get' method should only be used on
108 * forms that do not change data, as that is exclusively the domain of post.
109 * - no_redirect: If set to TRUE the form will NOT perform a drupal_goto(),
110 * even if a redirect is set.
111 * - always_process: If TRUE and the method is GET, a form_id is not
112 * necessary. This should only be used on RESTful GET forms that do NOT
113 * write data, as this could lead to security issues. It is useful so that
114 * searches do not need to have a form_id in their query arguments to
115 * trigger the search.
116 * - must_validate: Ordinarily, a form is only validated once but there are
117 * times when a form is resubmitted internally and should be validated
118 * again. Setting this to TRUE will force that to happen. This is most
119 * likely to occur during AHAH or AJAX operations.
120 * @return
121 * The rendered form or NULL, depending upon the $form_state flags that were set.
122 */
123 function drupal_build_form($form_id, &$form_state) {
124 // Ensure some defaults; if already set they will not be overridden.
125 $form_state += form_state_defaults();
126
127 if (!isset($form_state['input'])) {
128 $form_state['input'] = $form_state['method'] == 'get' ? $_GET : $_POST;
129 }
130
131 $cacheable = FALSE;
132
133 if (isset($_SESSION['batch_form_state'])) {
134 // We've been redirected here after a batch processing : the form has
135 // already been processed, so we grab the post-process $form_state value
136 // and move on to form display. See _batch_finished() function.
137 $form_state = $_SESSION['batch_form_state'];
138 unset($_SESSION['batch_form_state']);
139 }
140 else {
141 // If the incoming input contains a form_build_id, we'll check the
142 // cache for a copy of the form in question. If it's there, we don't
143 // have to rebuild the form to proceed. In addition, if there is stored
144 // form_state data from a previous step, we'll retrieve it so it can
145 // be passed on to the form processing code.
146 if (isset($form_state['input']['form_id']) && $form_state['input']['form_id'] == $form_id && !empty($form_state['input']['form_build_id'])) {
147 $form = form_get_cache($form_state['input']['form_build_id'], $form_state);
148 }
149
150 // If the previous bit of code didn't result in a populated $form
151 // object, we're hitting the form for the first time and we need
152 // to build it from scratch.
153 if (!isset($form)) {
154 $form = drupal_retrieve_form($form_id, $form_state);
155 $form_build_id = 'form-' . md5(uniqid(mt_rand(), TRUE));
156 $form['#build_id'] = $form_build_id;
157
158 // Fix the form method, if it is 'get' in $form_state, but not in $form.
159 if ($form_state['method'] == 'get' && !isset($form['#method'])) {
160 $form['#method'] = 'get';
161 }
162
163 drupal_prepare_form($form_id, $form, $form_state);
164 // Store a copy of the unprocessed form for caching and indicate that it
165 // is cacheable if #cache will be set.
166 $original_form = $form;
167 $cacheable = TRUE;
168 }
169
170 // Now that we know we have a form, we'll process it (validating,
171 // submitting, and handling the results returned by its submission
172 // handlers. Submit handlers accumulate data in the form_state by
173 // altering the $form_state variable, which is passed into them by
174 // reference.
175 drupal_process_form($form_id, $form, $form_state);
176
177 if ($cacheable && !empty($form_state['cache']) && empty($form['#no_cache'])) {
178 // Caching is done past drupal_process_form so #process callbacks can
179 // set #cache.
180 form_set_cache($form_build_id, $original_form, $form_state);
181 }
182 }
183
184 // Most simple, single-step forms will be finished by this point --
185 // drupal_process_form() usually redirects to another page (or to
186 // a 'fresh' copy of the form) once processing is complete. If one
187 // of the form's handlers has set $form_state['redirect'] to FALSE,
188 // the form will simply be re-rendered with the values still in its
189 // fields.
190 //
191 // If $form_state['storage'] or $form_state['rebuild'] has been set
192 // and the form has been submitted, we know that we're in a complex
193 // multi-part process of some sort and the form's workflow is NOT
194 // complete. We need to construct a fresh copy of the form, passing
195 // in the latest $form_state in addition to any other variables passed
196 // into drupal_get_form().
197
198 if ((!empty($form_state['storage']) || $form_state['rebuild']) && $form_state['submitted'] && !form_get_errors()) {
199 $form = drupal_rebuild_form($form_id, $form_state);
200 }
201
202 // Don't override #theme if someone already set it.
203 if (!isset($form['#theme'])) {
204 drupal_theme_initialize();
205 $registry = theme_get_registry();
206 if (isset($registry[$form_id])) {
207 $form['#theme'] = $form_id;
208 }
209 }
210
211 return $form;
212 }
213
214 /**
215 * Retrieve default values for the $form_state array.
216 */
217 function form_state_defaults() {
218 return array(
219 'rebuild' => FALSE,
220 'redirect' => NULL,
221 'storage' => NULL,
222 'submitted' => FALSE,
223 'programmed' => FALSE,
224 'cache'=> FALSE,
225 'method' => 'post',
226 'groups' => array(),
227 );
228 }
229
230 /**
231 * Retrieves a form, caches it and processes it with an empty $_POST.
232 *
233 * This function clears $_POST and passes the empty $_POST to the form_builder.
234 * To preserve some parts from $_POST, pass them in $form_state.
235 *
236 * If your AHAH callback simulates the pressing of a button, then your AHAH
237 * callback will need to do the same as what drupal_get_form would do when the
238 * button is pressed: get the form from the cache, run drupal_process_form over
239 * it and then if it needs rebuild, run drupal_rebuild_form over it. Then send
240 * back a part of the returned form.
241 * $form_state['clicked_button']['#array_parents'] will help you to find which
242 * part.
243 *
244 * When getting a form from the cache, the $form_id must be shifted off from
245 * $form['#args'], so the resulting array can be given to $form_state['args'].
246 *
247 * @param $form_id
248 * The unique string identifying the desired form. If a function
249 * with that name exists, it is called to build the form array.
250 * Modules that need to generate the same form (or very similar forms)
251 * using different $form_ids can implement hook_forms(), which maps
252 * different $form_id values to the proper form constructor function. Examples
253 * may be found in node_forms(), search_forms(), and user_forms().
254 * @param $form_state
255 * A keyed array containing the current state of the form. Most
256 * important is the $form_state['storage'] collection.
257 * @param $form_build_id
258 * If the AHAH callback calling this function only alters part of the form,
259 * then pass in the existing form_build_id so we can re-cache with the same
260 * csid.
261 * @return
262 * The newly built form.
263 */
264 function drupal_rebuild_form($form_id, &$form_state, $form_build_id = NULL) {
265 $form = drupal_retrieve_form($form_id, $form_state);
266
267 if (!isset($form_build_id)) {
268 // We need a new build_id for the new version of the form.
269 $form_build_id = 'form-' . md5(mt_rand());
270 }
271 $form['#build_id'] = $form_build_id;
272 drupal_prepare_form($form_id, $form, $form_state);
273
274 if (empty($form['#no_cache'])) {
275 // We cache the form structure so it can be retrieved later for validation.
276 // If $form_state['storage'] is populated, we also cache it so that it can
277 // be used to resume complex multi-step processes.
278 form_set_cache($form_build_id, $form, $form_state);
279 }
280
281 // Clear out all post data, as we don't want the previous step's
282 // data to pollute this one and trigger validate/submit handling,
283 // then process the form for rendering.
284 $form_state['input'] = array();
285
286 // Also clear out all group associations as these might be different
287 // when rerendering the form.
288 $form_state['groups'] = array();
289
290 // Do not call drupal_process_form(), since it would prevent the rebuilt form
291 // to submit.
292 $form = form_builder($form_id, $form, $form_state);
293 return $form;
294 }
295
296 /**
297 * Fetch a form from cache.
298 */
299 function form_get_cache($form_build_id, &$form_state) {
300 if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) {
301 $form = $cached->data;
302 global $user;
303 if ((isset($form['#cache_token']) && drupal_valid_token($form['#cache_token'])) || (!isset($form['#cache_token']) && !$user->uid)) {
304 if ($cached = cache_get('storage_' . $form_build_id, 'cache_form')) {
305 $form_state['storage'] = $cached->data;
306 }
307 return $form;
308 }
309 }
310 }
311
312 /**
313 * Store a form in the cache
314 */
315 function form_set_cache($form_build_id, $form, $form_state) {
316 // 6 hours cache life time for forms should be plenty.
317 $expire = 21600;
318 global $user;
319 if ($user->uid) {
320 $form['#cache_token'] = drupal_get_token();
321 }
322 cache_set('form_' . $form_build_id, $form, 'cache_form', REQUEST_TIME + $expire);
323 if (!empty($form_state['storage'])) {
324 cache_set('storage_' . $form_build_id, $form_state['storage'], 'cache_form', REQUEST_TIME + $expire);
325 }
326 }
327
328 /**
329 * Retrieves a form using a form_id, populates it with $form_state['values'],
330 * processes it, and returns any validation errors encountered. This
331 * function is the programmatic counterpart to drupal_get_form().
332 *
333 * @param $form_id
334 * The unique string identifying the desired form. If a function
335 * with that name exists, it is called to build the form array.
336 * Modules that need to generate the same form (or very similar forms)
337 * using different $form_ids can implement hook_forms(), which maps
338 * different $form_id values to the proper form constructor function. Examples
339 * may be found in node_forms(), search_forms(), and user_forms().
340 * @param $form_state
341 * A keyed array containing the current state of the form. Most
342 * important is the $form_state['values'] collection, a tree of data
343 * used to simulate the incoming $_POST information from a user's
344 * form submission.
345 * @param ...
346 * Any additional arguments are passed on to the functions called by
347 * drupal_form_submit(), including the unique form constructor function.
348 * For example, the node_edit form requires that a node object be passed
349 * in here when it is called.
350 * For example:
351 *
352 * @code
353 * // register a new user
354 * $form_state = array();
355 * $form_state['values']['name'] = 'robo-user';
356 * $form_state['values']['mail'] = 'robouser@example.com';
357 * $form_state['values']['pass'] = 'password';
358 * $form_state['values']['op'] = t('Create new account');
359 * drupal_form_submit('user_register', $form_state);
360 *
361 * // Create a new node
362 * $form_state = array();
363 * module_load_include('inc', 'node', 'node.pages');
364 * $node = array('type' => 'story');
365 * $form_state['values']['title'] = 'My node';
366 * $form_state['values']['body'] = 'This is the body text!';
367 * $form_state['values']['name'] = 'robo-user';
368 * $form_state['values']['op'] = t('Save');
369 * drupal_form_submit('story_node_form', $form_state, (object)$node);
370 * @endcode
371 */
372 function drupal_form_submit($form_id, &$form_state) {
373 if (!isset($form_state['args'])) {
374 $args = func_get_args();
375 array_shift($args);
376 array_shift($args);
377 $form_state['args'] = $args;
378 }
379
380 $form = drupal_retrieve_form($form_id, $form_state);
381 $form_state['input'] = $form_state['values'];
382 $form_state['programmed'] = TRUE;
383 // Programmed forms are always submitted.
384 $form_state['submitted'] = TRUE;
385 // Merge in default values.
386 $form_state += form_state_defaults();
387
388 drupal_prepare_form($form_id, $form, $form_state);
389 drupal_process_form($form_id, $form, $form_state);
390 }
391
392 /**
393 * Retrieves the structured array that defines a given form.
394 *
395 * @param $form_id
396 * The unique string identifying the desired form. If a function
397 * with that name exists, it is called to build the form array.
398 * Modules that need to generate the same form (or very similar forms)
399 * using different $form_ids can implement hook_forms(), which maps
400 * different $form_id values to the proper form constructor function.
401 * @param $form_state
402 * A keyed array containing the current state of the form.
403 * @param ...
404 * Any additional arguments needed by the unique form constructor
405 * function. Generally, these are any arguments passed into the
406 * drupal_get_form() or drupal_form_submit() functions after the first
407 * argument. If a module implements hook_forms(), it can examine
408 * these additional arguments and conditionally return different
409 * builder functions as well.
410 */
411 function drupal_retrieve_form($form_id, &$form_state) {
412 $forms = &drupal_static(__FUNCTION__);
413
414 // We save two copies of the incoming arguments: one for modules to use
415 // when mapping form ids to constructor functions, and another to pass to
416 // the constructor function itself.
417 $args = $form_state['args'];
418
419 // We first check to see if there's a function named after the $form_id.
420 // If there is, we simply pass the arguments on to it to get the form.
421 if (!drupal_function_exists($form_id)) {
422 // In cases where many form_ids need to share a central constructor function,
423 // such as the node editing form, modules can implement hook_forms(). It
424 // maps one or more form_ids to the correct constructor functions.
425 //
426 // We cache the results of that hook to save time, but that only works
427 // for modules that know all their form_ids in advance. (A module that
428 // adds a small 'rate this comment' form to each comment in a list
429 // would need a unique form_id for each one, for example.)
430 //
431 // So, we call the hook if $forms isn't yet populated, OR if it doesn't
432 // yet have an entry for the requested form_id.
433 if (!isset($forms) || !isset($forms[$form_id])) {
434 $forms = module_invoke_all('forms', $form_id, $args);
435 }
436 $form_definition = $forms[$form_id];
437 if (isset($form_definition['callback arguments'])) {
438 $args = array_merge($form_definition['callback arguments'], $args);
439 }
440 if (isset($form_definition['callback'])) {
441 $callback = $form_definition['callback'];
442 drupal_function_exists($callback);
443 }
444 }
445
446 $args = array_merge(array(&$form_state), $args);
447
448 // If $callback was returned by a hook_forms() implementation, call it.
449 // Otherwise, call the function named after the form id.
450 $form = call_user_func_array(isset($callback) ? $callback : $form_id, $args);
451 $form['#form_id'] = $form_id;
452 $form['#args'] = $form_state['args'];
453 return $form;
454 }
455
456 /**
457 * This function is the heart of form API. The form gets built, validated and in
458 * appropriate cases, submitted.
459 *
460 * @param $form_id
461 * The unique string identifying the current form.
462 * @param $form
463 * An associative array containing the structure of the form.
464 * @param $form_state
465 * A keyed array containing the current state of the form. This
466 * includes the current persistent storage data for the form, and
467 * any data passed along by earlier steps when displaying a
468 * multi-step form. Additional information, like the sanitized $_POST
469 * data, is also accumulated here.
470 */
471 function drupal_process_form($form_id, &$form, &$form_state) {
472 $form_state['values'] = array();
473
474 // With $_GET, these forms are always submitted if requested.
475 if ($form_state['method'] == 'get' && !empty($form_state['always_process'])) {
476 if (!isset($form_state['input']['form_build_id'])) {
477 $form_state['input']['form_build_id'] = $form['#build_id'];
478 }
479 if (!isset($form_state['input']['form_id'])) {
480 $form_state['input']['form_id'] = $form_id;
481 }
482 if (!isset($form_state['input']['form_token']) && isset($form['#token'])) {
483 $form_state['input']['form_token'] = drupal_get_token($form['#token']);
484 }
485 }
486
487 // Build the form.
488 $form = form_builder($form_id, $form, $form_state);
489
490 // Only process the input if we have a correct form submission.
491 if ($form_state['process_input']) {
492 drupal_validate_form($form_id, $form, $form_state);
493
494 // form_clean_id() maintains a cache of element IDs it has seen,
495 // so it can prevent duplicates. We want to be sure we reset that
496 // cache when a form is processed, so scenarios that result in
497 // the form being built behind the scenes and again for the
498 // browser don't increment all the element IDs needlessly.
499 drupal_static_reset('form_clean_id');
500
501 if ($form_state['submitted'] && !form_get_errors() && !$form_state['rebuild']) {
502 // Execute form submit handlers.
503 form_execute_handlers('submit', $form, $form_state);
504
505 // We'll clear out the cached copies of the form and its stored data
506 // here, as we've finished with them. The in-memory copies are still
507 // here, though.
508 if (variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED && !empty($form_state['values']['form_build_id'])) {
509 cache_clear_all('form_' . $form_state['values']['form_build_id'], 'cache_form');
510 cache_clear_all('storage_' . $form_state['values']['form_build_id'], 'cache_form');
511 }
512
513 // If batches were set in the submit handlers, we process them now,
514 // possibly ending execution. We make sure we do not react to the batch
515 // that is already being processed (if a batch operation performs a
516 // drupal_form_submit).
517 if ($batch =& batch_get() && !isset($batch['current_set'])) {
518 // The batch uses its own copies of $form and $form_state for
519 // late execution of submit handlers and post-batch redirection.
520 $batch['form'] = $form;
521 $batch['form_state'] = $form_state;
522 $batch['progressive'] = !$form_state['programmed'];
523 batch_process();
524 // Execution continues only for programmatic forms.
525 // For 'regular' forms, we get redirected to the batch processing
526 // page. Form redirection will be handled in _batch_finished(),
527 // after the batch is processed.
528 }
529
530 // Set a flag to indicate the the form has been processed and executed.
531 $form_state['executed'] = TRUE;
532
533 // The form is executed. By default, we're finished now and redirect to a
534 // new destination page. The path of the destination page can be set in
535 // $form['#redirect'] or $form_state['redirect']. If neither of the two is
536 // set, the user is redirect to the current page, which means a fresh,
537 // unpopulated copy of the form.
538 // Redirection is skipped, though, if
539 // - the form was called by drupal_form_submit(),
540 // - the form has to be rebuilt because either $form_state['rebuild'] was
541 // set to TRUE or $form_state['storage'] was populated by a submit handler.
542 // - $form_state['no_redirect'] is set to TRUE,
543 // - $form_state['redirect'] or $form['#redirect'] is set to FALSE.
544 if (!$form_state['programmed'] && empty($form_state['rebuild']) && empty($form_state['storage']) && empty($form_state['no_redirect'])) {
545 drupal_redirect_form($form, $form_state['redirect']);
546 }
547 }
548 }
549 }
550
551 /**
552 * Prepares a structured form array by adding required elements,
553 * executing any hook_form_alter functions, and optionally inserting
554 * a validation token to prevent tampering.
555 *
556 * @param $form_id
557 * A unique string identifying the form for validation, submission,
558 * theming, and hook_form_alter functions.
559 * @param $form
560 * An associative array containing the structure of the form.
561 * @param $form_state
562 * A keyed array containing the current state of the form. Passed
563 * in here so that hook_form_alter() calls can use it, as well.
564 */
565 function drupal_prepare_form($form_id, &$form, &$form_state) {
566 global $user;
567
568 $form['#type'] = 'form';
569 $form_state['programmed'] = isset($form_state['programmed']) ? $form_state['programmed'] : FALSE;
570
571 if (isset($form['#build_id'])) {
572 $form['form_build_id'] = array(
573 '#type' => 'hidden',
574 '#value' => $form['#build_id'],
575 '#id' => $form['#build_id'],
576 '#name' => 'form_build_id',
577 );
578 }
579
580 // Add a token, based on either #token or form_id, to any form displayed to
581 // authenticated users. This ensures that any submitted form was actually
582 // requested previously by the user and protects against cross site request
583 // forgeries.
584 if (isset($form['#token'])) {
585 if ($form['#token'] === FALSE || $user->uid == 0 || $form_state['programmed']) {
586 unset($form['#token']);
587 }
588 else {
589 $form['form_token'] = array('#type' => 'token', '#default_value' => drupal_get_token($form['#token']));
590 }
591 }
592 elseif (isset($user->uid) && $user->uid && !$form_state['programmed']) {
593 $form['#token'] = $form_id;
594 $form['form_token'] = array(
595 '#id' => form_clean_id('edit-' . $form_id . '-form-token'),
596 '#type' => 'token',
597 '#default_value' => drupal_get_token($form['#token']),
598 );
599 }
600
601 if (isset($form_id)) {
602 $form['form_id'] = array(
603 '#type' => 'hidden',
604 '#value' => $form_id,
605 '#id' => form_clean_id("edit-$form_id"),
606 );
607 }
608 if (!isset($form['#id'])) {
609 $form['#id'] = form_clean_id($form_id);
610 }
611
612 $form += element_info('form');
613 $form += array('#tree' => FALSE, '#parents' => array());
614
615 if (!isset($form['#validate'])) {
616 if (drupal_function_exists($form_id . '_validate')) {
617 $form['#validate'] = array($form_id . '_validate');
618 }
619 }
620
621 if (!isset($form['#submit'])) {
622 if (drupal_function_exists($form_id . '_submit')) {
623 // We set submit here so that it can be altered.
624 $form['#submit'] = array($form_id . '_submit');
625 }
626 }
627
628 // Normally, we would call drupal_alter($form_id, $form, $form_state).
629 // However, drupal_alter() normally supports just one byref parameter. Using
630 // the __drupal_alter_by_ref key, we can store any additional parameters
631 // that need to be altered, and they'll be split out into additional params
632 // for the hook_form_alter() implementations.
633 // @todo: Remove this in Drupal 7.
634 $data = &$form;
635 $data['__drupal_alter_by_ref'] = array(&$form_state);
636 drupal_alter('form_' . $form_id, $data);
637
638 // __drupal_alter_by_ref is unset in the drupal_alter() function, we need
639 // to repopulate it to ensure both calls get the data.
640 $data['__drupal_alter_by_ref'] = array(&$form_state);
641 drupal_alter('form', $data, $form_id);
642 }
643
644
645 /**
646 * Validates user-submitted form data from the $form_state using
647 * the validate functions defined in a structured form array.
648 *
649 * @param $form_id
650 * A unique string identifying the form for validation, submission,
651 * theming, and hook_form_alter functions.
652 * @param $form
653 * An associative array containing the structure of the form.
654 * @param $form_state
655 * A keyed array containing the current state of the form. The current
656 * user-submitted data is stored in $form_state['values'], though
657 * form validation functions are passed an explicit copy of the
658 * values for the sake of simplicity. Validation handlers can also
659 * $form_state to pass information on to submit handlers. For example:
660 * $form_state['data_for_submision'] = $data;
661 * This technique is useful when validation requires file parsing,
662 * web service requests, or other expensive requests that should
663 * not be repeated in the submission step.
664 */
665 function drupal_validate_form($form_id, $form, &$form_state) {
666 $validated_forms = &drupal_static(__FUNCTION__, array());
667
668 if (isset($validated_forms[$form_id]) && empty($form_state['must_validate'])) {
669 return;
670 }
671
672 // If the session token was set by drupal_prepare_form(), ensure that it
673 // matches the current user's session.
674 if (isset($form['#token'])) {
675 if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) {
676 // Setting this error will cause the form to fail validation.
677 form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));
678 }
679 }
680
681 _form_validate($form, $form_state, $form_id);
682 $validated_forms[$form_id] = TRUE;
683 }
684
685 /**
686 * Redirect the user to a URL after a form has been processed.
687 *
688 * @param $form
689 * An associative array containing the structure of the form.
690 * @param $redirect
691 * An optional value containing the destination path to redirect
692 * to if none is specified by the form.
693 */
694 function drupal_redirect_form($form, $redirect = NULL) {
695 $goto = NULL;
696 if (isset($redirect)) {
697 $goto = $redirect;
698 }
699 if ($goto !== FALSE && isset($form['#redirect'])) {
700 $goto = $form['#redirect'];
701 }
702 if (!isset($goto) || ($goto !== FALSE)) {
703 if (isset($goto)) {
704 if (is_array($goto)) {
705 call_user_func_array('drupal_goto', $goto);
706 }
707 else {
708 // This function can be called from the installer, which guarantees
709 // that $redirect will always be a string, so catch that case here
710 // and use the appropriate redirect function.
711 $function = drupal_installation_attempted() ? 'install_goto' : 'drupal_goto';
712 $function($goto);
713 }
714 }
715 drupal_goto($_GET['q']);
716 }
717 }
718
719 /**
720 * Performs validation on form elements. First ensures required fields are
721 * completed, #maxlength is not exceeded, and selected options were in the
722 * list of options given to the user. Then calls user-defined validators.
723 *
724 * @param $elements
725 * An associative array containing the structure of the form.
726 * @param $form_state
727 * A keyed array containing the current state of the form. The current
728 * user-submitted data is stored in $form_state['values'], though
729 * form validation functions are passed an explicit copy of the
730 * values for the sake of simplicity. Validation handlers can also
731 * $form_state to pass information on to submit handlers. For example:
732 * $form_state['data_for_submision'] = $data;
733 * This technique is useful when validation requires file parsing,
734 * web service requests, or other expensive requests that should
735 * not be repeated in the submission step.
736 * @param $form_id
737 * A unique string identifying the form for validation, submission,
738 * theming, and hook_form_alter functions.
739 */
740 function _form_validate($elements, &$form_state, $form_id = NULL) {
741 // Also used in the installer, pre-database setup.
742 $t = get_t();
743
744 // Recurse through all children.
745 foreach (element_children($elements) as $key) {
746 if (isset($elements[$key]) && $elements[$key]) {
747 _form_validate($elements[$key], $form_state);
748 }
749 }
750 // Validate the current input.
751 if (!isset($elements['#validated']) || !$elements['#validated']) {
752 if (isset($elements['#needs_validation'])) {
753 // Make sure a value is passed when the field is required.
754 // A simple call to empty() will not cut it here as some fields, like
755 // checkboxes, can return a valid value of '0'. Instead, check the
756 // length if it's a string, and the item count if it's an array.
757 if ($elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0))) {
758 form_error($elements, $t('!name field is required.', array('!name' => $elements['#title'])));
759 }
760
761 // Verify that the value is not longer than #maxlength.
762 if (isset($elements['#maxlength']) && drupal_strlen($elements['#value']) > $elements['#maxlength']) {
763 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']))));
764 }
765
766 if (isset($elements['#options']) && isset($elements['#value'])) {
767 if ($elements['#type'] == 'select') {
768 $options = form_options_flatten($elements['#options']);
769 }
770 else {
771 $options = $elements['#options'];
772 }
773 if (is_array($elements['#value'])) {
774 $value = $elements['#type'] == 'checkboxes' ? array_keys(array_filter($elements['#value'])) : $elements['#value'];
775 foreach ($value as $v) {
776 if (!isset($options[$v])) {
777 form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.'));
778 watchdog('form', 'Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
779 }
780 }
781 }
782 elseif (!isset($options[$elements['#value']])) {
783 form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.'));
784 watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
785 }
786 }
787 }
788
789 // Call user-defined form level validators.
790 if (isset($form_id)) {
791 form_execute_handlers('validate', $elements, $form_state);
792 }
793 // Call any element-specific validators. These must act on the element
794 // #value data.
795 elseif (isset($elements['#element_validate'])) {
796 foreach ($elements['#element_validate'] as $function) {
797 if (drupal_function_exists($function)) {
798 $function($elements, $form_state, $form_state['complete form']);
799 }
800 }
801 }
802 $elements['#validated'] = TRUE;
803 }
804 }
805
806 /**
807 * A helper function used to execute custom validation and submission
808 * handlers for a given form. Button-specific handlers are checked
809 * first. If none exist, the function falls back to form-level handlers.
810 *
811 * @param $type
812 * The type of handler to execute. 'validate' or 'submit' are the
813 * defaults used by Form API.
814 * @param $form
815 * An associative array containing the structure of the form.
816 * @param $form_state
817 * A keyed array containing the current state of the form. If the user
818 * submitted the form by clicking a button with custom handler functions
819 * defined, those handlers will be stored here.
820 */
821 function form_execute_handlers($type, &$form, &$form_state) {
822 $return = FALSE;
823 if (isset($form_state[$type . '_handlers'])) {
824 $handlers = $form_state[$type . '_handlers'];
825 }
826 elseif (isset($form['#' . $type])) {
827 $handlers = $form['#' . $type];
828 }
829 else {
830 $handlers = array();
831 }
832
833 foreach ($handlers as $function) {
834 if (drupal_function_exists($function)) {
835 // Check to see if a previous _submit handler has set a batch, but
836 // make sure we do not react to a batch that is already being processed
837 // (for instance if a batch operation performs a drupal_form_submit()).
838 if ($type == 'submit' && ($batch =& batch_get()) && !isset($batch['current_set'])) {
839 // Some previous _submit handler has set a batch. We store the call
840 // in a special 'control' batch set, for execution at the correct
841 // time during the batch processing workflow.
842 $batch['sets'][] = array('form_submit' => $function);
843 }
844 else {
845 $function($form, $form_state);
846 }
847 $return = TRUE;
848 }
849 }
850 return $return;
851 }
852
853 /**
854 * File an error against a form element.
855 *
856 * @param $name
857 * The name of the form element. If the #parents property of your form
858 * element is array('foo', 'bar', 'baz') then you may set an error on 'foo'
859 * or 'foo][bar][baz'. Setting an error on 'foo' sets an error for every
860 * element where the #parents array starts with 'foo'.
861 * @param $message
862 * The error message to present to the user.
863 * @param $reset
864 * Reset the form errors static cache.
865 * @return
866 * Never use the return value of this function, use form_get_errors and
867 * form_get_error instead.
868 */
869 function form_set_error($name = NULL, $message = '') {
870 $form = &drupal_static(__FUNCTION__, array());
871 if (isset($name) && !isset($form[$name])) {
872 $form[$name] = $message;
873 if ($message) {
874 drupal_set_message($message, 'error');
875 }
876 }
877 return $form;
878 }
879
880 /**
881 * Clear all errors against all form elements made by form_set_error().
882 */
883 function form_clear_error() {
884 drupal_static_reset('form_set_error');
885 }
886
887 /**
888 * Return an associative array of all errors.
889 */
890 function form_get_errors() {
891 $form = form_set_error();
892 if (!empty($form)) {
893 return $form;
894 }
895 }
896
897 /**
898 * Return the error message filed against the form with the specified name.
899 */
900 function form_get_error($element) {
901 $form = form_set_error();
902 $key = $element['#parents'][0];
903 if (isset($form[$key])) {
904 return $form[$key];
905 }
906 $key = implode('][', $element['#parents']);
907 if (isset($form[$key])) {
908 return $form[$key];
909 }
910 }
911
912 /**
913 * Flag an element as having an error.
914 */
915 function form_error(&$element, $message = '') {
916 form_set_error(implode('][', $element['#parents']), $message);
917 }
918
919 /**
920 * Walk through the structured form array, adding any required
921 * properties to each element and mapping the incoming input
922 * data to the proper elements. Also, execute any #process handlers
923 * attached to a specific element.
924 *
925 * @param $form_id
926 * A unique string identifying the form for validation, submission,
927 * theming, and hook_form_alter functions.
928 * @param $element
929 * An associative array containing the structure of the current element.
930 * @param $form_state
931 * A keyed array containing the current state of the form. In this
932 * context, it is used to accumulate information about which button
933 * was clicked when the form was submitted, as well as the sanitized
934 * $_POST data.
935 */
936 function form_builder($form_id, $element, &$form_state) {
937 // Initialize as unprocessed.
938 $element['#processed'] = FALSE;
939
940 // Use element defaults.
941 if ((!empty($element['#type'])) && ($info = element_info($element['#type']))) {
942 // Overlay $info onto $element, retaining preexisting keys in $element.
943 $element += $info;
944 $element['#defaults_loaded'] = TRUE;
945 }
946
947 // Special handling if we're on the top level form element.
948 if (isset($element['#type']) && $element['#type'] == 'form') {
949 // Store a complete copy of the form in form_state prior to building the form.
950 $form_state['complete form'] = $element;
951 // Set a flag if we have a correct form submission. This is always TRUE for
952 // programmed forms coming from drupal_form_submit(), or if the form_id coming
953 // from the POST data is set and matches the current form_id.
954 if ($form_state['programmed'] || (!empty($form_state['input']) && (isset($form_state['input']['form_id']) && ($form_state['input']['form_id'] == $form_id)))) {
955 $form_state['process_input'] = TRUE;
956 }
957 else {
958 $form_state['process_input'] = FALSE;
959 }
960 }
961
962 if (!isset($element['#id'])) {
963 $element['#id'] = form_clean_id('edit-' . implode('-', $element['#parents']));
964 }
965 // Handle input elements.
966 if (!empty($element['#input'])) {
967 _form_builder_handle_input_element($form_id, $element, $form_state);
968 }
969 // Allow for elements to expand to multiple elements, e.g., radios,
970 // checkboxes and files.
971 if (isset($element['#process']) && !$element['#processed']) {
972 foreach ($element['#process'] as $process) {
973 if (drupal_function_exists($process)) {
974 $element = $process($element, $form_state, $form_state['complete form']);
975 }
976 }
977 $element['#processed'] = TRUE;
978 }
979
980 // We start off assuming all form elements are in the correct order.
981 $element['#sorted'] = TRUE;
982
983 // Recurse through all child elements.
984 $count = 0;
985 foreach (element_children($element) as $key) {
986 // Don't squash an existing tree value.
987 if (!isset($element[$key]['#tree'])) {
988 $element[$key]['#tree'] = $element['#tree'];
989 }
990
991 // Deny access to child elements if parent is denied.
992 if (isset($element['#access']) && !$element['#access']) {
993 $element[$key]['#access'] = FALSE;
994 }
995
996 // Don't squash existing parents value.
997 if (!isset($element[$key]['#parents'])) {
998 // Check to see if a tree of child elements is present. If so,
999 // continue down the tree if required.
1000 $element[$key]['#parents'] = $element[$key]['#tree'] && $element['#tree'] ? array_merge($element['#parents'], array($key)) : array($key);
1001 $array_parents = isset($element['#array_parents']) ? $element['#array_parents'] : array();
1002 $array_parents[] = $key;
1003 $element[$key]['#array_parents'] = $array_parents;
1004 }
1005
1006 // Assign a decimal placeholder weight to preserve original array order.
1007 if (!isset($element[$key]['#weight'])) {
1008 $element[$key]['#weight'] = $count/1000;
1009 }
1010 else {
1011 // If one of the child elements has a weight then we will need to sort
1012 // later.
1013 unset($element['#sorted']);
1014 }
1015 $element[$key] = form_builder($form_id, $element[$key], $form_state);
1016 $count++;
1017 }
1018
1019 // The #after_build flag allows any piece of a form to be altered
1020 // after normal input parsing has been completed.
1021 if (isset($element['#after_build']) && !isset($element['#after_build_done'])) {
1022 foreach ($element['#after_build'] as $function) {
1023 $element = $function($element, $form_state);
1024 $element['#after_build_done'] = TRUE;
1025 }
1026 }
1027
1028 // Now that we've processed everything, we can go back to handle the funky
1029 // Internet Explorer button-click scenario.
1030 _form_builder_ie_cleanup($element, $form_state);
1031
1032 // We should keep the buttons array until the IE clean up function
1033 // has recognized the submit button so the form has been marked
1034 // as submitted. If we already know which button was submitted,
1035 // we don't need the array.
1036 if (!empty($form_state['submitted'])) {
1037 unset($form_state['buttons']);
1038 }
1039
1040 // If some callback set #cache, we need to flip a flag so later it
1041 // can be found.
1042 if (!empty($element['#cache'])) {
1043 $form_state['cache'] = $element['#cache'];
1044 }
1045
1046 // If there is a file element, we need to flip a flag so later the
1047 // form encoding can be set.
1048 if (isset($element['#type']) && $element['#type'] == 'file') {
1049 $form_state['has_file_element'] = TRUE;
1050 }
1051
1052 if (isset($element['#type']) && $element['#type'] == 'form') {
1053 // We are on the top form.
1054 // If there is a file element, we set the form encoding.
1055 if (isset($form_state['has_file_element'])) {
1056 $element['#attributes']['enctype'] = 'multipart/form-data';
1057 }
1058 // Update the copy of the complete form for usage in validation handlers.
1059 $form_state['complete form'] = $element;
1060 }
1061 return $element;
1062 }
1063
1064 /**
1065 * Populate the #value and #name properties of input elements so they
1066 * can be processed and rendered.
1067 */
1068 function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
1069 if (!isset($element['#name'])) {
1070 $name = array_shift($element['#parents']);
1071 $element['#name'] = $name;
1072 if ($element['#type'] == 'file') {
1073 // To make it easier to handle $_FILES in file.inc, we place all
1074 // file fields in the 'files' array. Also, we do not support
1075 // nested file names.
1076 $element['#name'] = 'files[' . $element['#name'] . ']';
1077 }
1078 elseif (count($element['#parents'])) {
1079 $element['#name'] .= '[' . implode('][', $element['#parents']) . ']';
1080 }
1081 array_unshift($element['#parents'], $name);
1082 }
1083
1084 if (!empty($element['#disabled'])) {
1085 $element['#attributes']['disabled'] = 'disabled';
1086 }
1087
1088 // Set the element's #value property.
1089 if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
1090 $value_callback = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value';
1091
1092 if ($form_state['programmed'] || ($form_state['process_input'] && (!isset($element['#access']) || $element['#access']))) {
1093 $input = $form_state['input'];
1094 foreach ($element['#parents'] as $parent) {
1095 $input = isset($input[$parent]) ? $input[$parent] : NULL;
1096 }
1097 // If we have input for the current element, assign it to the #value property.
1098 if (!$form_state['programmed'] || isset($input)) {
1099 // Call #type_value to set the form value;
1100 if (drupal_function_exists($value_callback)) {
1101 $element['#value'] = $value_callback($element, $input, $form_state);
1102 }
1103 if (!isset($element['#value']) && isset($input)) {
1104 $element['#value'] = $input;
1105 }
1106 }
1107 // Mark all posted values for validation.
1108 if (isset($element['#value']) || (!empty($element['#required']))) {
1109 $element['#needs_validation'] = TRUE;
1110 }
1111 }
1112 // Load defaults.
1113 if (!isset($element['#value'])) {
1114 // Call #type_value without a second argument to request default_value handling.
1115 if (drupal_function_exists($value_callback)) {
1116 $element['#value'] = $value_callback($element, FALSE, $form_state);
1117 }
1118 // Final catch. If we haven't set a value yet, use the explicit default value.
1119 // Avoid image buttons (which come with garbage value), so we only get value
1120 // for the button actually clicked.
1121 if (!isset($element['#value']) && empty($element['#has_garbage_value'])) {
1122 $element['#value'] = isset($element['#default_value']) ? $element['#default_value'] : '';
1123 }
1124 }
1125 }
1126
1127 // Determine which button (if any) was clicked to submit the form.
1128 // We compare the incoming values with the buttons defined in the form,
1129 // and flag the one that matches. We have to do some funky tricks to
1130 // deal with Internet Explorer's handling of single-button forms, though.
1131 if (!empty($form_state['input']) && isset($element['#executes_submit_callback'])) {
1132 // First, accumulate a collection of buttons, divided into two bins:
1133 // those that execute full submit callbacks and those that only validate.
1134 $button_type = $element['#executes_submit_callback'] ? 'submit' : 'button';
1135 $form_state['buttons'][$button_type][] = $element;
1136
1137 if (_form_button_was_clicked($element, $form_state)) {
1138 $form_state['submitted'] = $form_state['submitted'] || $element['#executes_submit_callback'];
1139
1140 // In most cases, we want to use form_set_value() to manipulate
1141 // the global variables. In this special case, we want to make sure that
1142 // the value of this element is listed in $form_variables under 'op'.
1143 $form_state['values'][$element['#name']] = $element['#value'];
1144 $form_state['clicked_button'] = $element;
1145
1146 if (isset($element['#validate'])) {
1147 $form_state['validate_handlers'] = $element['#validate'];
1148 }
1149 if (isset($element['#submit'])) {
1150 $form_state['submit_handlers'] = $element['#submit'];
1151 }
1152 }
1153 }
1154 form_set_value($element, $element['#value'], $form_state);
1155 }
1156
1157 /**
1158 * Helper function to handle the sometimes-convoluted logic of button
1159 * click detection.
1160 *
1161 * In Internet Explorer, if ONLY one submit button is present, AND the
1162 * enter key is used to submit the form, no form value is sent for it
1163 * and we'll never detect a match. That special case is handled by
1164 * _form_builder_ie_cleanup().
1165 */
1166 function _form_button_was_clicked($form, &$form_state) {
1167 // First detect normal 'vanilla' button clicks. Traditionally, all
1168 // standard buttons on a form share the same name (usually 'op'),
1169 // and the specific return value is used to determine which was
1170 // clicked. This ONLY works as long as $form['#name'] puts the
1171 // value at the top level of the tree of $_POST data.
1172 if (isset($form_state['input'][$form['#name']]) && $form_state['input'][$form['#name']] == $form['#value']) {
1173 return TRUE;
1174 }
1175 // When image buttons are clicked, browsers do NOT pass the form element
1176 // value in $_POST. Instead they pass an integer representing the
1177 // coordinates of the click on the button image. This means that image
1178 // buttons MUST have unique $form['#name'] values, but the details of
1179 // their $_POST data should be ignored.
1180 elseif (!empty($form['#has_garbage_value']) && isset($form['#value']) && $form['#value'] !== '') {
1181 return TRUE;
1182 }
1183 return FALSE;
1184 }
1185
1186 /**
1187 * In IE, if only one submit button is present, AND the enter key is
1188 * used to submit the form, no form value is sent for it and our normal
1189 * button detection code will never detect a match. We call this
1190 * function after all other button-detection is complete to check
1191 * for the proper conditions, and treat the single button on the form
1192 * as 'clicked' if they are met.
1193 */
1194 function _form_builder_ie_cleanup($form, &$form_state) {
1195 // Quick check to make sure we're always looking at the full form
1196 // and not a sub-element.
1197 if (!empty($form['#type']) && $form['#type'] == 'form') {
1198 // If we haven't recognized a submission yet, and there's a single
1199 // submit button, we know that we've hit the right conditions. Grab
1200 // the first one and treat it as the clicked button.
1201 if (empty($form_state['submitted']) && !empty($form_state['buttons']['submit']) && empty($form_state['buttons']['button'])) {
1202 $button = $form_state['buttons']['submit'][0];
1203
1204 // Set up all the $form_state information that would have been
1205 // populated had the button been recognized earlier.
1206 $form_state['submitted'] = TRUE;
1207 $form_state['submit_handlers'] = empty($button['#submit']) ? NULL : $button['#submit'];
1208 $form_state['validate_handlers'] = empty($button['#validate']) ? NULL : $button['#validate'];
1209 $form_state['values'][$button['#name']] = $button['#value'];
1210 $form_state['clicked_button'] = $button;
1211 }
1212 }
1213 }
1214
1215 /**
1216 * Helper function to determine the value for an image button form element.
1217 *
1218 * @param $form
1219 * The form element whose value is being populated.
1220 * @param $input
1221 * The incoming input to populate the form element. If this is FALSE,
1222 * the element's default value should be returned.
1223 * @param $form_state
1224 * A keyed array containing the current state of the form.
1225 * @return
1226 * The data that will appear in the $form_state['values'] collection
1227 * for this element. Return nothing to use the default.
1228 */
1229 function form_type_image_button_value($form, $input, $form_state) {
1230 if ($input !== FALSE) {
1231 if (!empty($input)) {
1232 // If we're dealing with Mozilla or Opera, we're lucky. It will
1233 // return a proper value, and we can get on with things.
1234 return $form['#return_value'];
1235 }
1236 else {
1237 // Unfortunately, in IE we never get back a proper value for THIS
1238 // form element. Instead, we get back two split values: one for the
1239 // X and one for the Y coordinates on which the user clicked the
1240 // button. We'll find this element in the #post data, and search
1241 // in the same spot for its name, with '_x'.
1242 $input = $form_state['input'];
1243 foreach (explode('[', $form['#name']) as $element_name) {
1244 // chop off the ] that may exist.
1245 if (substr($element_name, -1) == ']') {
1246 $element_name = substr($element_name, 0, -1);
1247 }
1248
1249 if (!isset($input[$element_name])) {
1250 if (isset($input[$element_name . '_x'])) {
1251 return $form['#return_value'];
1252 }
1253 return NULL;
1254 }
1255 $input = $input[$element_name];
1256 }
1257 return $form['#return_value'];
1258 }
1259 }
1260 }
1261
1262 /**
1263 * Helper function to determine the value for a checkbox form element.
1264 *
1265 * @param $form
1266 * The form element whose value is being populated.
1267 * @param $input
1268 * The incoming input to populate the form element. If this is FALSE,
1269 * the element's default value should be returned.
1270 * @return
1271 * The data that will appear in the $element_state['values'] collection
1272 * for this element. Return nothing to use the default.
1273 */
1274 function form_type_checkbox_value($element, $input = FALSE) {
1275 if ($input !== FALSE) {
1276 if (empty($element['#disabled'])) {
1277 return !empty($input) ? $element['#return_value'] : 0;
1278 }
1279 else {
1280 return $element['#default_value'];
1281 }
1282 }
1283 }
1284
1285 /**
1286 * Helper function to determine the value for a checkboxes form element.
1287 *
1288 * @param $element
1289 * The form element whose value is being populated.
1290 * @param $input
1291 * The incoming input to populate the form element. If this is FALSE,
1292 * the element's default value should be returned.
1293 * @return
1294 * The data that will appear in the $element_state['values'] collection
1295 * for this element. Return nothing to use the default.
1296 */
1297 function form_type_checkboxes_value($element, $input = FALSE) {
1298 if ($input === FALSE) {
1299 $value = array();
1300 $element += array('#default_value' => array());
1301 foreach ($element['#default_value'] as $key) {
1302 $value[$key] = 1;
1303 }
1304 return $value;
1305 }
1306 elseif (!isset($input)) {
1307 return array();
1308 }
1309 }
1310
1311 /**
1312 * Helper function to determine the value for a password_confirm form
1313 * element.
1314 *
1315 * @param $element
1316 * The form element whose value is being populated.
1317 * @param $input
1318 * The incoming input to populate the form element. If this is FALSE,
1319 * the element's default value should be returned.
1320 * @return
1321 * The data that will appear in the $element_state['values'] collection
1322 * for this element. Return nothing to use the default.
1323 */
1324 function form_type_password_confirm_value($element, $input = FALSE) {
1325 if ($input === FALSE) {
1326 $element += array('#default_value' => array());
1327 return $element['#default_value'] + array('pass1' => '', 'pass2' => '');
1328 }
1329 }
1330
1331 /**
1332 * Helper function to determine the value for a select form element.
1333 *
1334 * @param $element
1335 * The form element whose value is being populated.
1336 * @param $input
1337 * The incoming input to populate the form element. If this is FALSE,
1338 * the element's default value should be returned.
1339 * @return
1340 * The data that will appear in the $element_state['values'] collection
1341 * for this element. Return nothing to use the default.
1342 */
1343 function form_type_select_value($element, $input = FALSE) {
1344 if ($input !== FALSE) {
1345 if (isset($element['#multiple']) && $element['#multiple']) {
1346 return (is_array($input)) ? drupal_map_assoc($input) : array();
1347 }
1348 else {
1349 return $input;
1350 }
1351 }
1352 }
1353
1354 /**
1355 * Helper function to determine the value for a textfield form element.
1356 *
1357 * @param $element
1358 * The form element whose value is being populated.
1359 * @param $input
1360 * The incoming input to populate the form element. If this is FALSE,
1361 * the element's default value should be returned.
1362 * @return
1363 * The data that will appear in the $element_state['values'] collection
1364 * for this element. Return nothing to use the default.
1365 */
1366 function form_type_textfield_value($element, $input = FALSE) {
1367 if ($input !== FALSE) {
1368 // Equate $input to the form value to ensure it's marked for
1369 // validation.
1370 return str_replace(array("\r", "\n"), '', $input);
1371 }
1372 }
1373
1374 /**
1375 * Helper function to determine the value for form's token value.
1376 *
1377 * @param $element
1378 * The form element whose value is being populated.
1379 * @param $input
1380 * The incoming input to populate the form element. If this is FALSE,
1381 * the element's default value should be returned.
1382 * @return
1383 * The data that will appear in the $element_state['values'] collection
1384 * for this element. Return nothing to use the default.
1385 */
1386 function form_type_token_value($element, $input = FALSE) {
1387 if ($input !== FALSE) {
1388 return (string)$input;
1389 }
1390 }
1391
1392 /**
1393 * Change submitted form values during the form processing cycle.
1394 *
1395 * Use this function to change the submitted value of a form item in the
1396 * validation phase so that it persists in $form_state through to the
1397 * submission handlers in the submission phase.
1398 *
1399 * Since $form_state['values'] can either be a flat array of values, or a tree
1400 * of nested values, some care must be taken when using this function.
1401 * Specifically, $element['#parents'] is an array that describes the branch of
1402 * the tree whose value should be updated. For example, if we wanted to update
1403 * $form_state['values']['one']['two'] to 'new value', we'd pass in
1404 * $element['#parents'] = array('one', 'two') and $value = 'new value'.
1405 *
1406 * @param $element
1407 * The form item that should have its value updated. Keys used: #parents,
1408 * #value. In most cases you can just pass in the right element from the $form
1409 * array.
1410 * @param $value
1411 * The new value for the form item.
1412 * @param $form_state
1413 * The array where the value change should be recorded.
1414 */
1415 function form_set_value($element, $value, &$form_state) {
1416 _form_set_value($form_state['values'], $element, $element['#parents'], $value);
1417 }
1418
1419 /**
1420 * Helper function for form_set_value().
1421 *
1422 * We iterate over $parents and create nested arrays for them
1423 * in $form_state['values'] if needed. Then we insert the value into
1424 * the right array.
1425 */
1426 function _form_set_value(&$form_values, $element, $parents, $value) {
1427 $parent = array_shift($parents);
1428 if (empty($parents)) {
1429 $form_values[$parent] = $value;
1430 }
1431 else {
1432 if (!isset($form_values[$parent])) {
1433 $form_values[$parent] = array();
1434 }
1435 _form_set_value($form_values[$parent], $element, $parents, $value);
1436 }
1437 }
1438
1439 function form_options_flatten($array, $reset = TRUE) {
1440 // $reset has been ignored here as the function recurses, retaining
1441 // its value while recursing and resetting itself when called.
1442 static $return;
1443
1444 if ($reset) {
1445 $return = array();
1446 }
1447
1448 foreach ($array as $key => $value) {
1449 if (is_object($value)) {
1450 form_options_flatten($value->option, FALSE);
1451 }
1452 elseif (is_array($value)) {
1453 form_options_flatten($value, FALSE);
1454 }
1455 else {
1456 $return[$key] = 1;
1457 }
1458 }
1459
1460 return $return;
1461 }
1462
1463 /**
1464 * Format a dropdown menu or scrolling selection box.
1465 *
1466 * @param $element
1467 * An associative array containing the properties of the element.
1468 * Properties used: title, value, options, description, extra, multiple, required
1469 * @return
1470 * A themed HTML string representing the form element.
1471 *
1472 * @ingroup themeable
1473 *
1474 * It is possible to group options together; to do this, change the format of
1475 * $options to an associative array in which the keys are group labels, and the
1476 * values are associative arrays in the normal $options format.
1477 */
1478 function theme_select($element) {
1479 $select = '';
1480 $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';
1481 _form_set_class($element, array('form-select'));
1482 $multiple = $element['#multiple'];
1483 return '<select name="' . $element['#name'] . '' . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) . ' id="' . $element['#id'] . '" ' . $size . '>' . form_select_options($element) . '</select>';
1484 }
1485
1486 function form_select_options($element, $choices = NULL) {
1487 if (!isset($choices)) {
1488 $choices = $element['#options'];
1489 }
1490 // array_key_exists() accommodates the rare event where $element['#value'] is NULL.
1491 // isset() fails in this situation.
1492 $value_valid = isset($element['#value']) || array_key_exists('#value', $element);
1493 $value_is_array = is_array($element['#value']);
1494 $options = '';
1495 foreach ($choices as $key => $choice) {
1496 if (is_array($choice)) {
1497 $options .= '<optgroup label="' . $key . '">';
1498 $options .= form_select_options($element, $choice);
1499 $options .= '</optgroup>';
1500 }
1501 elseif (is_object($choice)) {
1502 $options .= form_select_options($element, $choice->option);
1503 }
1504 else {
1505 $key = (string)$key;
1506 if ($value_valid && (!$value_is_array && (string)$element['#value'] === $key || ($value_is_array && in_array($key, $element['#value'])))) {
1507 $selected = ' selected="selected"';
1508 }
1509 else {
1510 $selected = '';
1511 }
1512 $options .= '<option value="' . check_plain($key) . '"' . $selected . '>' . check_plain($choice) . '</option>';
1513 }
1514 }
1515 return $options;
1516 }
1517
1518 /**
1519 * Traverses a select element's #option array looking for any values
1520 * that hold the given key. Returns an array of indexes that match.
1521 *
1522 * This function is useful if you need to modify the options that are
1523 * already in a form element; for example, to remove choices which are
1524 * not valid because of additional filters imposed by another module.
1525 * One example might be altering the choices in a taxonomy selector.
1526 * To correctly handle the case of a multiple hierarchy taxonomy,
1527 * #options arrays can now hold an array of objects, instead of a
1528 * direct mapping of keys to labels, so that multiple choices in the
1529 * selector can have the same key (and label). This makes it difficult
1530 * to manipulate directly, which is why this helper function exists.
1531 *
1532 * This function does not support optgroups (when the elements of the
1533 * #options array are themselves arrays), and will return FALSE if
1534 * arrays are found. The caller must either flatten/restore or
1535 * manually do their manipulations in this case, since returning the
1536 * index is not sufficient, and supporting this would make the
1537 * "helper" too complicated and cumbersome to be of any help.
1538 *
1539 * As usual with functions that can return array() or FALSE, do not
1540 * forget to use === and !== if needed.
1541 *
1542 * @param $element
1543 * The select element to search.
1544 * @param $key
1545 * The key to look for.
1546 * @return
1547 * An array of indexes that match the given $key. Array will be
1548 * empty if no elements were found. FALSE if optgroups were found.
1549 */
1550 function form_get_options($element, $key) {
1551 $keys = array();
1552 foreach ($element['#options'] as $index => $choice) {
1553 if (is_array($choice)) {
1554 return FALSE;
1555 }
1556 elseif (is_object($choice)) {
1557 if (isset($choice->option[$key])) {
1558 $keys[] = $index;
1559 }
1560 }
1561 elseif ($index == $key) {
1562 $keys[] = $index;
1563 }
1564 }
1565 return $keys;
1566 }
1567
1568 /**
1569 * Formats a group of items as an HTML fieldset element.
1570 *
1571 * @param $element
1572 * An associative array containing the properties of the element.
1573 * Properties used: #attributes, #children, #collapsed, #collapsible,
1574 * #description, #id, #title, #value.
1575 * @return
1576 * A themed HTML string representing the group of items.
1577 *
1578 * @ingroup themeable
1579 */
1580 function theme_fieldset($element) {
1581 if (!empty($element['#collapsible'])) {
1582 drupal_add_js('misc/collapse.js');
1583
1584 if (!isset($element['#attributes']['class'])) {
1585 $element['#attributes']['class'] = '';
1586 }
1587
1588 $element['#attributes']['class'] .= ' collapsible';
1589 if (!empty($element['#collapsed'])) {
1590 $element['#attributes']['class'] .= ' collapsed';
1591 }
1592 }
1593 $element['#attributes']['id'] = $element['#id'];
1594
1595 return '<fieldset' . drupal_attributes($element['#attributes']) . '>' . ($element['#title'] ? '<legend>' . $element['#title'] . '</legend>' : '') . (isset($element['#description']) && $element['#description'] ? '<div class="fieldset-description">' . $element['#description'] . '</div>' : '') . (!empty($element['#children']) ? $element['#children'] : '') . (isset($element['#value']) ? $element['#value'] : '') . "</fieldset>\n";
1596 }
1597
1598 /**
1599 * Format a radio button.
1600 *
1601 * @param $element
1602 * An associative array containing the properties of the element.
1603 * Properties used: required, return_value, value, attributes, title, description
1604 * @return
1605 * A themed HTML string representing the form item group.
1606 *
1607 * @ingroup themeable
1608 */
1609 function theme_radio($element) {
1610 _form_set_class($element, array('form-radio'));
1611 $output = '<input type="radio" ';
1612 $output .= 'id="' . $element['#id'] . '" ';
1613 $output .= 'name="' . $element['#name'] . '" ';
1614 $output .= 'value="' . $element['#return_value'] . '" ';
1615 $output .= (check_plain($element['#value']) == $element['#return_value']) ? ' checked="checked" ' : ' ';
1616 $output .= drupal_attributes($element['#attributes']) . ' />';
1617 if (!is_null($element['#title'])) {
1618 $output = '<label class="option" for="' . $element['#id'] . '">' . $output . ' ' . $element['#title'] . '</label>';
1619 }
1620
1621 return $output;
1622 }
1623
1624 /**
1625 * Format a set of radio buttons.
1626 *
1627 * @param $element
1628 * An associative array containing the properties of the element.
1629 * Properties used: title, value, options, description, required and attributes.
1630 * @return
1631 * A themed HTML string representing the radio button set.
1632 *
1633 * @ingroup themeable
1634 */
1635 function theme_radios($element) {
1636 $class = 'form-radios';
1637 if (isset($element['#attributes']['class'])) {
1638 $class .= ' ' . $element['#attributes']['class'];
1639 }
1640 $element['#children'] = '<div class="' . $class . '">' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
1641
1642 return $element['#children'];
1643 }
1644
1645 /**
1646 * Expand a password_confirm field into two text boxes.
1647 */
1648 function form_process_password_confirm($element) {
1649 $element['pass1'] = array(
1650 '#type' => 'password',
1651 '#title' => t('Password'),
1652 '#value' => empty($element['#value']) ? NULL : $element['#value']['pass1'],
1653 '#required' => $element['#required'],
1654 '#attributes' => array('class' => 'password-field'),
1655 );
1656 $element['pass2'] = array(
1657 '#type' => 'password',
1658 '#title' => t('Confirm password'),
1659 '#value' => empty($element['#value']) ? NULL : $element['#value']['pass2'],
1660 '#required' => $element['#required'],
1661 '#attributes' => array('class' => 'password-confirm'),
1662 );
1663 $element['#element_validate'] = array('password_confirm_validate');
1664 $element['#tree'] = TRUE;
1665
1666 if (isset($element['#size'])) {
1667 $element['pass1']['#size'] = $element['pass2']['#size'] = $element['#size'];
1668 }
1669
1670 return $element;
1671 }
1672
1673 /**
1674 * Validate password_confirm element.
1675 */
1676 function password_confirm_validate($element, &$element_state) {
1677 $pass1 = trim($element['pass1']['#value']);
1678 if (!empty($pass1)) {
1679 $pass2 = trim($element['pass2']['#value']);
1680 if (strcmp($pass1, $pass2)) {
1681 form_error($element, t('The specified passwords do not match.'));
1682 }
1683 }
1684 elseif ($element['#required'] && !empty($element_state['input'])) {
1685 form_error($element, t('Password field is required.'));
1686 }
1687
1688 // Password field must be converted from a two-element array into a single
1689 // string regardless of validation results.
1690 form_set_value($element['pass1'], NULL, $element_state);
1691 form_set_value($element['pass2'], NULL, $element_state);
1692 form_set_value($element, $pass1, $element_state);
1693
1694 return $element;
1695
1696 }
1697
1698 /**
1699 * Format a date selection element.
1700 *
1701 * @param $element
1702 * An associative array containing the properties of the element.
1703 * Properties used: title, value, options, description, required and attributes.
1704 * @return
1705 * A themed HTML string representing the date selection boxes.
1706 *
1707 * @ingroup themeable
1708 */
1709 function theme_date($element) {
1710 return '<div class="container-inline">' . drupal_render_children($element) . '</div>';
1711 }
1712
1713 /**
1714 * Roll out a single date element.
1715 */
1716 function form_process_date($element) {
1717 // Default to current date
1718 if (empty($element['#value'])) {
1719 $element['#value'] = array('day' => format_date(REQUEST_TIME, 'custom', 'j'),
1720 'month' => format_date(REQUEST_TIME, 'custom', 'n'),
1721 'year' => format_date(REQUEST_TIME, 'custom', 'Y'));
1722 }
1723
1724 $element['#tree'] = TRUE;
1725
1726 // Determine the order of day, month, year in the site's chosen date format.
1727 $format = variable_get('date_format_short', 'm/d/Y - H:i');
1728 $sort = array();
1729 $sort['day'] = max(strpos($format, 'd'), strpos($format, 'j'));
1730 $sort['month'] = max(strpos($format, 'm'), strpos($format, 'M'));
1731 $sort['year'] = strpos($format, 'Y');
1732 asort($sort);
1733 $order = array_keys($sort);
1734
1735 // Output multi-selector for date.
1736 foreach ($order as $type) {
1737 switch ($type) {
1738 case 'day':
1739 $options = drupal_map_assoc(range(1, 31));
1740 break;
1741 case 'month':
1742 $options = drupal_map_assoc(range(1, 12), 'map_month');
1743 break;
1744 case 'year':
1745 $options = drupal_map_assoc(range(1900, 2050));
1746 break;
1747 }
1748 $parents = $element['#parents'];
1749 $parents[] = $type;
1750 $element[$type] = array(
1751 '#type' => 'select',
1752 '#value' => $element['#value'][$type],
1753 '#attributes' => $element['#attributes'],
1754 '#options' => $options,
1755 );
1756 }
1757
1758 return $element;
1759 }
1760
1761 /**
1762 * Validates the date type to stop dates like February 30, 2006.
1763 */
1764 function date_validate($form) {
1765 if (!checkdate($form['#value']['month'], $form['#value']['day'], $form['#value']['year'])) {
1766 form_error($form, t('The specified date is invalid.'));
1767 }
1768 }
1769
1770 /**
1771 * Helper function for usage with drupal_map_assoc to display month names.
1772 */
1773 function map_month($month) {
1774 $months = &drupal_static(__FUNCTION__, array(
1775 1 => 'Jan',
1776 2 => 'Feb',
1777 3 => 'Mar',
1778 4 => 'Apr',
1779 5 => 'May',
1780 6 => 'Jun',
1781 7 => 'Jul',
1782 8 => 'Aug',
1783 9 => 'Sep',
1784 10 => 'Oct',
1785 11 => 'Nov',
1786 12 => 'Dec',
1787 ));
1788 return t($months[$month]);
1789 }
1790
1791 /**
1792 * If no default value is set for weight select boxes, use 0.
1793 */
1794 function weight_value(&$form) {
1795 if (isset($form['#default_value'])) {
1796 $form['#value'] = $form['#default_value'];
1797 }
1798 else {
1799 $form['#value'] = 0;
1800 }
1801 }
1802
1803 /**
1804 * Menu callback for AHAH callbacks through the #ahah['callback'] FAPI property.
1805 */
1806 function form_ahah_callback() {
1807 $form_state = form_state_defaults();
1808
1809 $form_build_id = $_POST['form_build_id'];
1810
1811 // Get the form from the cache.
1812 $form = form_get_cache($form_build_id, $form_state);
1813 if (!$form) {
1814 // If $form cannot be loaded from the cache, the form_build_id in $_POST must
1815 // be invalid, which means that someone performed a POST request onto
1816 // system/ahah without actually viewing the concerned form in the browser.
1817 // This is likely a hacking attempt as it never happens under normal
1818 // circumstances, so we just do nothing.
1819 exit;
1820 }
1821
1822 // We will run some of the submit handlers so we need to disable redirecting.
1823 $form['#redirect'] = FALSE;
1824
1825 // We need to process the form, prepare for that by setting a few internals
1826 // variables.
1827 $form_state['input'] = $_POST;
1828 $form_state['args'] = $form['#args'];
1829 $form_id = $form['#form_id'];
1830
1831 // Build, validate and if possible, submit the form.
1832 drupal_process_form($form_id, $form, $form_state);
1833
1834 // This call recreates the form relying solely on the form_state that the
1835 // drupal_process_form set up.
1836 $form = drupal_rebuild_form($form_id, $form_state, $form_build_id);
1837
1838 // Get the callback function from the clicked button.
1839 $callback = $form_state['clicked_button']['#ahah']['callback'];
1840 if (drupal_function_exists($callback)) {
1841 $callback($form, $form_state);
1842 }
1843 }
1844
1845 /**
1846 * Roll out a single radios element to a list of radios,
1847 * using the options array as index.
1848 */
1849 function form_process_radios($element) {
1850 if (count($element['#options']) > 0) {
1851 foreach ($element['#options'] as $key => $choice) {
1852 if (!isset($element[$key])) {
1853 // Generate the parents as the autogenerator does, so we will have a
1854 // unique id for each radio button.
1855 $parents_for_id = array_merge($element['#parents'], array($key));
1856 $element[$key] = array(
1857 '#type' => 'radio',
1858 '#title' => $choice,
1859 '#return_value' => check_plain($key),
1860 '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : NULL,
1861 '#attributes' => $element['#attributes'],
1862 '#parents' => $element['#parents'],
1863 '#id' => form_clean_id('edit-' . implode('-', $parents_for_id)),
1864 '#ahah' => isset($element['#ahah']) ? $element['#ahah'] : NULL,
1865 );
1866 }
1867 }
1868 }
1869 return $element;
1870 }
1871
1872 /**
1873 * Add text format selector to text elements with the #text_format property.
1874 *
1875 * The #text_format property should be the ID of an text format, found in
1876 * {filter_format}.format, which gets passed to filter_form().
1877 *
1878 * If the property #text_format is set, the form element will be expanded into
1879 * two separate form elements, one holding the content of the element, and the
1880 * other holding the text format selector. The original element is shifted into
1881 * a child element, but is otherwise unaltered, so that the format selector is
1882 * at the same level as the text field which it affects.
1883 *
1884 * For example:
1885 * @code
1886 * // A simple textarea, such as a node body.
1887 * $form['body'] = array(
1888 * '#type' => 'textarea',
1889 * '#title' => t('Body'),
1890 * '#text_format' => isset($node->format) ? $node->format : FILTER_FORMAT_DEFAULT,
1891 * );
1892 * @endcode
1893 *
1894 * Becomes:
1895 * @code
1896 * $form['body'] = array(
1897 * // Type switches to 'markup', as we're only interested in submitting the child elements.
1898 * '#type' => 'markup',
1899 * // 'value' holds the original element.
1900 * 'value' => array(
1901 * '#type' => 'textarea',
1902 * '#title' => t('Body'),
1903 * '#parents' => array('body'),
1904 * ),
1905 * // 'format' holds the text format selector.
1906 * 'format' => array(
1907 * '#parents' => array('body_format'),
1908 * ...
1909 * ),
1910 * );
1911 * @endcode
1912 *
1913 * And would result in:
1914 * @code
1915 * // Original, unaltered form element value.
1916 * $form_state['values']['body'] = 'Example content';
1917 * // Chosen text format.
1918 * $form_state['values']['body_format'] = 1;
1919 * @endcode
1920 *
1921 * @see system_elements(), filter_form()
1922 */
1923 function form_process_text_format($element) {
1924 if (isset($element['#text_format'])) {
1925 // Determine the form element parents and element name to use for the input
1926 // format widget. This simulates the 'element' and 'element_format' pair of
1927 // parents that filter_form() expects.
1928 $element_parents = $element['#parents'];
1929 $element_name = array_pop($element_parents);
1930 $element_parents[] = $element_name . '_format';
1931
1932 // We need to break references, otherwise form_builder recurses infinitely.
1933 $element['value'] = (array)$element;
1934 $element['value']['#weight'] = 0;
1935 unset($element['value']['#description']);
1936 $element['#type'] = 'markup';
1937 $element['#theme'] = NULL;
1938 $element['#theme_wrappers'] = array('text_format_wrapper');
1939 $element['format'] = filter_form($element['#text_format'], 1, $element_parents);
1940
1941 // We need to clear the #text_format from the new child otherwise we
1942 // would get into an infinite loop.
1943 unset($element['value']['#text_format']);
1944 }
1945 return $element;
1946 }
1947
1948 /**
1949 * Return a themed text format form element.
1950 *
1951 * @param element
1952 * An associative array containing the properties of the element.
1953 * Properties used: children, description
1954 * @return
1955 * A string representing the form element.
1956 *
1957 * @ingroup themeable
1958 */
1959 function theme_text_format_wrapper($element) {
1960 $output = '<div class="text-format-wrapper">' . "\n";
1961
1962 $output .= $element['#children'] . "\n";
1963
1964 if (!empty($element['#description'])) {
1965 $output .= '<div class="description">' . $element['#description'] . "</div>\n";
1966 }
1967
1968 $output .= "</div>\n";
1969
1970 return $output;
1971 }
1972
1973 /**
1974 * Add AHAH information about a form element to the page to communicate with
1975 * javascript. If #ahah[path] is set on an element, this additional javascript is
1976 * added to the page header to attach the AHAH behaviors. See ahah.js for more
1977 * information.
1978 *
1979 * @param $element
1980 * An associative array containing the properties of the element.
1981 * Properties used: ahah_event, ahah_path, ahah_wrapper, ahah_parameters,
1982 * ahah_effect.
1983 * @return
1984 * None. Additional code is added to the header of the page using
1985 * drupal_add_js.
1986 */
1987 function form_process_ahah($element) {
1988 $js_added = &drupal_static(__FUNCTION__, array());
1989 // Add a reasonable default event handler if none specified.
1990 if (isset($element['#ahah']) && !isset($element['#ahah']['event'])) {
1991 switch ($element['#type']) {
1992 case 'submit':
1993 case 'button':
1994 case 'image_button':
1995 // Use the mousedown instead of the click event because form
1996 // submission via pressing the enter key triggers a click event on
1997 // submit inputs, inappropriately triggering AHAH behaviors.
1998 $element['#ahah']['event'] = 'mousedown';
1999 // Attach an additional event handler so that AHAH behaviors
2000 // can be triggered still via keyboard input.
2001 $element['#ahah']['keypress'] = TRUE;
2002 break;
2003 case 'password':
2004 case 'textfield':
2005 case 'textarea':
2006 $element['#ahah']['event'] = 'blur';
2007 break;
2008 case 'radio':
2009 case 'checkbox':
2010 case 'select':
2011 $element['#ahah']['event'] = 'change';
2012 break;
2013 default:
2014 return $element;
2015 }
2016 }
2017
2018 // Adding the same javascript settings twice will cause a recursion error,
2019 // we avoid the problem by checking if the javascript has already been added.
2020 if ((isset($element['#ahah']['callback']) || isset($element['#ahah']['path'])) && isset($element['#ahah']['event']) && !isset($js_added[$element['#id']])) {
2021 drupal_add_library('system', 'form');
2022 drupal_add_js('misc/ahah.js');
2023
2024 $ahah_binding = array(
2025 'url' => isset($element['#ahah']['callback']) ? url('system/ahah') : url($element['#ahah']['path']),
2026 'event' => $element['#ahah']['event'],
2027 'keypress' => empty($element['#ahah']['keypress']) ? NULL : $element['#ahah']['keypress'],
2028 'wrapper' => empty($element['#ahah']['wrapper']) ? NULL : $element['#ahah']['wrapper'],
2029 'selector' => empty($element['#ahah']['selector']) ? '#' . $element['#id'] : $element['#ahah']['selector'],
2030 'effect' => empty($element['#ahah']['effect']) ? 'none' : $element['#ahah']['effect'],
2031 'method' => empty($element['#ahah']['method']) ? 'replace' : $element['#ahah']['method'],
2032 'progress' => empty($element['#ahah']['progress']) ? array('type' => 'throbber') : $element['#ahah']['progress'],
2033 'button' => isset($element['#executes_submit_callback']) ? array($element['#name'] => $element['#value']) : FALSE,
2034 );
2035
2036 // Convert a simple #ahah[progress] type string into an array.
2037 if (is_string($ahah_binding['progress'])) {
2038 $ahah_binding['progress'] = array('type' => $ahah_binding['progress']);
2039 }
2040 // Change progress path to a full url.
2041 if (isset($ahah_binding['progress']['path'])) {
2042 $ahah_binding['progress']['url'] = url($ahah_binding['progress']['path']);
2043 }
2044
2045 // Add progress.js if we're doing a bar display.
2046 if ($ahah_binding['progress']['type'] == 'bar') {
2047 drupal_add_js('misc/progress.js', array('cache' => FALSE));
2048 }
2049
2050 drupal_add_js(array('ahah' => array($element['#id'] => $ahah_binding)), 'setting');
2051
2052 $js_added[$element['#id']] = TRUE;
2053 $element['#cache'] = TRUE;
2054 }
2055 return $element;
2056 }
2057
2058 /**
2059 * Format a checkbox.
2060 *
2061 * @param $element
2062 * An associative array containing the properties of the element.
2063 * Properties used: title, value, return_value, description, required
2064 * @return
2065 * A themed HTML string representing the checkbox.
2066 *
2067 * @ingroup themeable
2068 */
2069 function theme_checkbox($element) {
2070 _form_set_class($element, array('form-checkbox'));
2071 $checkbox = '<input ';
2072 $checkbox .= 'type="checkbox" ';
2073 $checkbox .= 'name="' . $element['#name'] . '" ';
2074 $checkbox .= 'id="' . $element['#id'] . '" ' ;
2075 $checkbox .= 'value="' . $element['#return_value'] . '" ';
2076 $checkbox .= $element['#value'] ? ' checked="checked" ' : ' ';
2077 $checkbox .= drupal_attributes($element['#attributes']) . ' />';
2078
2079 if (!is_null($element['#title'])) {
2080 $checkbox = '<label class="option" for="' . $element['#id'] . '">' . $checkbox . ' ' . $element['#title'] . '</label>';
2081 }
2082
2083 return $checkbox;
2084 }
2085
2086 /**
2087 * Format a set of checkboxes.
2088 *
2089 * @param $element
2090 * An associative array containing the properties of the element.
2091 * @return
2092 * A themed HTML string representing the checkbox set.
2093 *
2094 * @ingroup themeable
2095 */
2096 function theme_checkboxes($element) {
2097 $class = 'form-checkboxes';
2098 if (isset($element['#attributes']['class'])) {
2099 $class .= ' ' . $element['#attributes']['class'];
2100 }
2101 $element['#children'] = '<div class="' . $class . '">' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
2102
2103 return $element['#children'];
2104 }
2105
2106 /**
2107 * Add form_element theming to an element if title or description is set.
2108 *
2109 * This is used as a pre render function for checkboxes and radios.
2110 */
2111 function form_pre_render_conditional_form_element($element) {
2112 if ($element['#title'] || $element['#description']) {
2113 unset($element['#id']);
2114 $element['#theme_wrappers'][] = 'form_element';
2115 }
2116 return $element;
2117 }
2118
2119 function form_process_checkboxes($element) {
2120 $value = is_array($element['#value']) ? $element['#value'] : array();
2121 $element['#tree'] = TRUE;
2122 if (count($element['#options']) > 0) {
2123 if (!isset($element['#default_value']) || $element['#default_value'] == 0) {
2124 $element['#default_value'] = array();
2125 }
2126 foreach ($element['#options'] as $key => $choice) {
2127 if (!isset($element[$key])) {
2128 $element[$key] = array(
2129 '#type' => 'checkbox',
2130 '#processed' => TRUE,
2131 '#title' => $choice,
2132 '#return_value' => $key,
2133 '#default_value' => isset($value[$key]),
2134 '#attributes' => $element['#attributes'],
2135 '#ahah' => isset($element['#ahah']) ? $element['#ahah'] : NULL,
2136 );
2137 }
2138 }
2139 }
2140 return $element;
2141 }
2142
2143 /**
2144 * Format a table with radio buttons or checkboxes.
2145 *
2146 * @param $element
2147 * An associative array containing the properties and children of the
2148 * tableselect element.
2149 * Each option in $element['#options'] can contain an array keyed by
2150 * '#attributes' which is added to the row's HTML attributes.
2151 * @see theme_table
2152 * Properties used: header, options, empty, js_select.
2153 * @return
2154 * A themed HTML string representing the table.
2155 *
2156 * Example:
2157 *
2158 * @code
2159 * $options = array();
2160 * $options[0]['title'] = "A red row"
2161 * $options[0]['#attributes'] = array ('class' => 'red-row');
2162 * $options[1]['title'] = "A blue row"
2163 * $options[1]['#attributes'] = array ('class' => 'blue-row');
2164 *
2165 * $form['myselector'] = array (
2166 * '#type' => 'tableselect',
2167 * '#title' => 'My Selector'
2168 * '#options' => $options,
2169 * );
2170 * @ingroup themeable
2171 */
2172 function theme_tableselect($element) {
2173 $rows = array();
2174 if (!empty($element['#options'])) {
2175 // Generate a table row for each selectable item in #options.
2176 foreach ($element['#options'] as $key => $value) {
2177 $row = array();
2178
2179 $row['data'] = array();
2180 if (isset($value['#attributes'])) {
2181 $row += $value['#attributes'];
2182 }
2183 // Render the checkbox / radio element.
2184 $row['data'][] = drupal_render($element[$key]);
2185
2186 // As theme_table only maps header and row columns by order, create the
2187 // correct order by iterating over the header fields.
2188 foreach ($element['#header'] as $fieldname => $title) {
2189 $row['data'][] = $element['#options'][$key][$fieldname];
2190 }
2191 $rows[] = $row;
2192 }
2193 // Add an empty header or a "Select all" checkbox to provide room for the
2194 // checkboxes/radios in the first table column.
2195 $first_col = $element['#js_select'] ? array(theme('table_select_header_cell')) : array('');
2196 $header = array_merge($first_col, $element['#header']);
2197 }
2198 else {
2199 // If there are no selectable options, display the empty text over the
2200 // entire width of the table.
2201 $header = $element['#header'];
2202 $rows[] = array(array('data' => $element['#empty'], 'colspan' => count($header)));
2203 }
2204 return theme('table', $header, $rows);
2205 }
2206
2207 /**
2208 * Create the correct amount of checkbox or radio elements to populate the table.
2209 *
2210 * @param $element
2211 * An associative array containing the properties and children of the
2212 * tableselect element.
2213 * @return
2214 * The processed element.
2215 */
2216 function form_process_tableselect($element) {
2217
2218 if ($element['#multiple']) {
2219 $value = is_array($element['#value']) ? $element['#value'] : array();
2220 }
2221 else {
2222 // Advanced selection behaviour make no sense for radios.
2223 $element['#js_select'] = FALSE;
2224 }
2225
2226 $element['#tree'] = TRUE;
2227
2228 if (count($element['#options']) > 0) {
2229 if (!isset($element['#default_value']) || $element['#default_value'] === 0) {
2230 $element['#default_value'] = array();
2231 }
2232
2233 // Sort the options by their #weight if they have a #weight.
2234 uasort($element['#options'], 'element_sort');
2235 // Create a checkbox or radio for each item in #options in such a way that
2236 // the value of the tableselect element behaves as if it had been of type
2237 // checkboxes or radios.
2238 foreach ($element['#options'] as $key => $choice) {
2239 // Do not overwrite manually created children.
2240 if (!isset($element[$key])) {
2241 if ($element['#multiple']) {
2242 $element[$key] = array(
2243 '#type' => 'checkbox',
2244 '#title' => '',
2245 '#return_value' => $key,
2246 '#default_value' => isset($value[$key]),
2247 '#attributes' => $element['#attributes'],
2248 '#ahah' => isset($element['#ahah']) ? $element['#ahah'] : NULL,
2249 );
2250 }
2251 else {
2252 // Generate the parents as the autogenerator does, so we will have a
2253 // unique id for each radio button.
2254 $parents_for_id = array_merge($element['#parents'], array($key));
2255 $element[$key] = array(
2256 '#type' => 'radio',
2257 '#title' => '',
2258 '#return_value' => $key,
2259 '#default_value' => ($element['#default_value'] == $key) ? $key : NULL,
2260 '#attributes' => $element['#attributes'],
2261 '#parents' => $element['#parents'],
2262 '#id' => form_clean_id('edit-' . implode('-', $parents_for_id)),
2263 '#ahah' => isset($element['#ahah']) ? $element['#ahah'] : NULL,
2264 );
2265 }
2266 }
2267 }
2268 }
2269 else {
2270 $element['#value'] = array();
2271 }
2272 return $element;
2273 }
2274
2275 /**
2276 * Adds fieldsets to the specified group or adds group members to this
2277 * fieldset.
2278 *
2279 * @param $element
2280 * An associative array containing the properties and children of the
2281 * fieldset.
2282 * @param $form_state
2283 * The $form_state array for the form this fieldset belongs to.
2284 * @return
2285 * The processed element.
2286 */
2287 function form_process_fieldset(&$element, &$form_state) {
2288 $parents = implode('][', $element['#parents']);
2289
2290 // Add this fieldset to a group if one is set and if it's not being
2291 // added to itself.
2292 if (isset($element['#group']) && $element['#group'] != $parents) {
2293 if (isset($form_state['groups'][$element['#group']]) && !empty($form_state['groups'][$element['#group']]['#group_exists'])) {
2294 // Trick drupal_render() into believing this has already been output.
2295 // The group widget will rerender this later. This only happens when the
2296 // group is actually defined ('#group_exists' is TRUE). This prevents
2297 // fieldsets from disappearing when the group they are associated to
2298 // does not exist.
2299 // If the group does not exist yet, the element's #printed value is left
2300 // as is. As soon as the group is processed (fieldsets are also groups;
2301 // see below), this element's #printed value is set to TRUE to prevent
2302 // rendering in the original context.
2303 $element['#printed'] = TRUE;
2304 }
2305
2306 // Store a reference to this fieldset for the vertical tabs processing
2307 // function.
2308 $form_state['groups'][$element['#group']][] = &$element;
2309 }
2310
2311 // Each fieldset can be a group itself and gets a reference to all
2312 // elements in its group.
2313 $form_state['groups'][$parents]['#group_exists'] = TRUE;
2314 // There might already be elements associated with this group. Since the
2315 // group did not exist yet at the time they were added to this group, they
2316 // couldn't set #printed to TRUE (see above). We now know that this group
2317 // does in fact exist and set #printed to TRUE to prevent rendering in the
2318 // original context.
2319 foreach (element_children($form_state['groups'][$parents]) as $key) {
2320 $form_state['groups'][$parents][$key]['#printed'] = TRUE;
2321 }
2322 $element['#group_members'] = &$form_state['groups'][$parents];
2323
2324 // Contains form element summary functionalities.
2325 drupal_add_js('misc/form.js', array('weight' => JS_LIBRARY + 1));
2326
2327 return $element;
2328 }
2329
2330 /**
2331 * Adds members of this group as actual elements for rendering.
2332 *
2333 * @param $element
2334 * An associative array containing the properties and children of the
2335 * fieldset.
2336 * @return
2337 * The modified element with all group members.
2338 */
2339 function form_pre_render_fieldset($element) {
2340 if (!empty($element['#group_members'])) {
2341 // Add the group members to this fieldset for rendering purposes only.
2342 foreach (element_children($element['#group_members']) as $key) {
2343 // This was set in form_process_fieldset so that fieldsets which are
2344 // added to groups are not rendered at their original location.
2345 // drupal_render_children() will set this back to TRUE.
2346 unset($element['#group_members'][$key]['#printed']);
2347 $element[] = &$element['#group_members'][$key];
2348 }
2349
2350 // Resort the element's children after the group members have been added.
2351 $element['#sorted'] = FALSE;
2352 }
2353
2354 return $element;
2355 }
2356
2357 /**
2358 * Creates a group formatted as vertical tabs.
2359 *
2360 * @param $element
2361 * An associative array containing the properties and children of the
2362 * fieldset.
2363 * @param $form_state
2364 * The $form_state array for the form this vertical tab widget belongs to.
2365 * @return
2366 * The processed element.
2367 */
2368 function form_process_vertical_tabs($element, &$form_state) {
2369 // To save us from modifying the existing element and changing its #type,
2370 // a new form element is created as a child. The default #process hooks
2371 // are called automatically by the form renderer and we don't have to do
2372 // that manually.
2373 $element['group'] = array(
2374 '#type' => 'fieldset',
2375 '#theme_wrappers' => array(),
2376 '#parents' => $element['#parents'],
2377 );
2378
2379 // The JavaScript stores the currently selected tab in this hidden
2380 // field so that the active tab can be restored the next time the
2381 // form is rendered, e.g. on preview pages or when form validation
2382 // fails.
2383 $name = implode('__', $element['#parents']);
2384 if (isset($form_state['values'][$name . '__active_tab'])) {
2385 $element['#default_tab'] = $form_state['values'][$name . '__active_tab'];
2386 }
2387 $element[$name . '__active_tab'] = array(
2388 '#type' => 'hidden',
2389 '#default_value' => $element['#default_tab'],
2390 '#attributes' => array('class' => 'vertical-tabs-active-tab'),
2391 );
2392
2393 return $element;
2394 }
2395
2396 /**
2397 * Makes the element's children fieldsets be vertical tabs.
2398 *
2399 * @param $element
2400 * An associative array containing the properties and children of the
2401 * fieldset.
2402 */
2403 function theme_vertical_tabs($element) {
2404 // Add required JavaScript and Stylesheet.
2405 drupal_add_js('misc/vertical-tabs.js', array('weight' => JS_DEFAULT - 1));
2406 drupal_add_css('misc/vertical-tabs.css');
2407
2408 return '<div class="vertical-tabs-panes">' . $element['#children'] . '</div>';
2409 }
2410
2411 /**
2412 * Theme a form submit button.
2413 *
2414 * @ingroup themeable
2415 */
2416 function theme_submit($element) {
2417 return theme('button', $element);
2418 }
2419
2420 /**
2421 * Theme a form button.
2422 *
2423 * @ingroup themeable
2424 */
2425 function theme_button($element) {
2426 // Make sure not to overwrite classes.
2427 if (isset($element['#attributes']['class'])) {
2428 $element['#attributes']['class'] = 'form-' . $element['#button_type'] . ' ' . $element['#attributes']['class'];
2429 }
2430 else {
2431 $element['#attributes']['class'] = 'form-' . $element['#button_type'];
2432 }
2433
2434 return '<input type="submit" ' . (empty($element['#name']) ? '' : 'name="' . $element['#name'] . '" ') . 'id="' . $element['#id'] . '" value="' . check_plain($element['#value']) . '" ' . drupal_attributes($element['#attributes']) . " />\n";
2435 }
2436
2437 /**
2438 * Theme a form image button.
2439 *
2440 * @ingroup themeable
2441 */
2442 function theme_image_button($element) {
2443 // Make sure not to overwrite classes.
2444 if (isset($element['#attributes']['class'])) {
2445 $element['#attributes']['class'] = 'form-' . $element['#button_type'] . ' ' . $element['#attributes']['class'];
2446 }
2447 else {
2448 $element['#attributes']['class'] = 'form-' . $element['#button_type'];
2449 }
2450
2451 return '<input type="image" name="' . $element['#name'] . '" ' .
2452 (!empty($element['#value']) ? ('value="' . check_plain($element['#value']) . '" ') : '') .
2453 'id="' . $element['#id'] . '" ' .
2454 drupal_attributes($element['#attributes']) .
2455 ' src="' . base_path() . $element['#src'] . '" ' .
2456 (!empty($element['#title']) ? 'alt="' . check_plain($element['#title']) . '" title="' . check_plain($element['#title']) . '" ' : '' ) .
2457 "/>\n";
2458 }
2459
2460 /**
2461 * Format a hidden form field.
2462 *
2463 * @param $element
2464 * An associative array containing the properties of the element.
2465 * @return
2466 * A themed HTML string representing the hidden form field.
2467 *
2468 * @ingroup themeable
2469 */
2470 function theme_hidden($element) {
2471 return '<input type="hidden" name="' . $element['#name'] . '" id="' . $element['#id'] . '" value="' . check_plain($element['#value']) . "\" " . drupal_attributes($element['#attributes']) . " />\n";
2472 }
2473
2474 /**
2475 * Format a textfield.
2476 *
2477 * @param $element
2478 * An associative array containing the properties of the element.
2479 * Properties used: title, value, description, size, maxlength, required, attributes autocomplete_path
2480 * @return
2481 * A themed HTML string representing the textfield.
2482 *
2483 * @ingroup themeable
2484 */
2485 function theme_textfield($element) {
2486 $size = empty($element['#size']) ? '' : ' size="' . $element['#size'] . '"';
2487 $maxlength = empty($element['#maxlength']) ? '' : ' maxlength="' . $element['#maxlength'] . '"';
2488 $class = array('form-text');
2489 $extra = '';
2490 $output = '';
2491
2492 if ($element['#autocomplete_path'] && menu_valid_path(array('link_path' => $element['#autocomplete_path']))) {
2493 drupal_add_js('misc/autocomplete.js');
2494 $class[] = 'form-autocomplete';
2495 $extra = '<input class="autocomplete" type="hidden" id="' . $element['#id'] . '-autocomplete" value="' . check_url(url($element['#autocomplete_path'], array('absolute' => TRUE))) . '" disabled="disabled" />';
2496 }
2497 _form_set_class($element, $class);
2498
2499 if (isset($element['#field_prefix'])) {
2500 $output .= '<span class="field-prefix">' . $element['#field_prefix'] . '</span> ';
2501 }
2502
2503 $output .= '<input type="text"' . $maxlength . ' name="' . $element['#name'] . '" id="' . $element['#id'] . '"' . $size . ' value="' . check_plain($element['#value']) . '"' . drupal_attributes($element['#attributes']) . ' />';
2504
2505 if (isset($element['#field_suffix'])) {
2506 $output .= ' <span class="field-suffix">' . $element['#field_suffix'] . '</span>';
2507 }
2508 return $output . $extra;
2509 }
2510
2511 /**
2512 * Format a form.
2513 *
2514 * @param $element
2515 * An associative array containing the properties of the element.
2516 * Properties used: action, method, attributes, children
2517 * @return
2518 * A themed HTML string representing the form.
2519 *
2520 * @ingroup themeable
2521 */
2522 function theme_form($element) {
2523 // Anonymous div to satisfy XHTML compliance.
2524 $action = $element['#action'] ? 'action="' . check_url($element['#action']) . '" ' : '';
2525 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";
2526 }
2527
2528 /**
2529 * Format a textarea.
2530 *
2531 * @param $element
2532 * An associative array containing the properties of the element.
2533 * Properties used: title, value, description, rows, cols, required, attributes
2534 * @return
2535 * A themed HTML string representing the textarea.
2536 *
2537 * @ingroup themeable
2538 */
2539 function theme_textarea($element) {
2540 $class = array('form-textarea');
2541
2542 // Add resizable behavior
2543 if ($element['#resizable'] !== FALSE) {
2544 drupal_add_js('misc/textarea.js');
2545 $class[] = 'resizable';
2546 }
2547
2548 _form_set_class($element, $class);
2549 return '<textarea cols="' . $element['#cols'] . '" rows="' . $element['#rows'] . '" name="' . $element['#name'] . '" id="' . $element['#id'] . '" ' . drupal_attributes($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>';
2550 }
2551
2552 /**
2553 * Format HTML markup for use in forms.
2554 *
2555 * @param $element
2556 * An associative array containing the properties of the element.
2557 * Properties used: markup, children.
2558 * @return
2559 * A themed HTML string representing the HTML markup.
2560 *
2561 * @ingroup themeable
2562 */
2563
2564 function theme_markup($element) {
2565 return (!empty($element['#markup']) ? $element['#markup'] : '') . drupal_render_children($element);
2566 }
2567
2568 /**
2569 * Format a password field.
2570 *
2571 * @param $element
2572 * An associative array containing the properties of the element.
2573 * Properties used: title, value, description, size, maxlength, required, attributes
2574 * @return
2575 * A themed HTML string representing the form.
2576 *
2577 * @ingroup themeable
2578 */
2579 function theme_password($element) {
2580 $size = $element['#size'] ? ' size="' . $element['#size'] . '" ' : '';
2581 $maxlength = $element['#maxlength'] ? ' maxlength="' . $element['#maxlength'] . '" ' : '';
2582
2583 _form_set_class($element, array('form-text'));
2584 $output = '<input type="password" name="' . $element['#name'] . '" id="' . $element['#id'] . '" ' . $maxlength . $size . drupal_attributes($element['#attributes']) . ' />';
2585 return $output;
2586 }
2587
2588 /**
2589 * Expand weight elements into selects.
2590 */
2591 function form_process_weight($element) {
2592 for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) {
2593 $weights[$n] = $n;
2594 }
2595 $element['#options'] = $weights;
2596 $element['#type'] = 'select';
2597 $element['#is_weight'] = TRUE;
2598 $element += element_info('select');
2599 return $element;
2600 }
2601
2602 /**
2603 * Format a file upload field.
2604 *
2605 * @param $title
2606 * The label for the file upload field.
2607 * @param $name
2608 * The internal name used to refer to the field.
2609 * @param $size
2610 * A measure of the visible size of the field (passed directly to HTML).
2611 * @param $description
2612 * Explanatory text to display after the form item.
2613 * @param $required
2614 * Whether the user must upload a file to the field.
2615 * @return
2616 * A themed HTML string representing the field.
2617 *
2618 * @ingroup themeable
2619 *
2620 * For assistance with handling the uploaded file correctly, see the API
2621 * provided by file.inc.
2622 */
2623 function theme_file($element) {
2624 _form_set_class($element, array('form-file'));
2625 return '<input type="file" name="' . $element['#name'] . '"' . ($element['#attributes'] ? ' ' . drupal_attributes($element['#attributes']) : '') . ' id="' . $element['#id'] . '" size="' . $element['#size'] . "\" />\n";
2626 }
2627
2628 /**
2629 * Return a themed form element.
2630 *
2631 * @param element
2632 * An associative array containing the properties of the element.
2633 * Properties used: title, description, id, required, children
2634 * @return
2635 * A string representing the form element.
2636 *
2637 * @ingroup themeable
2638 */
2639 function theme_form_element($element) {
2640 // This is also used in the installer, pre-database setup.
2641 $t = get_t();
2642
2643 // Add element's #type and #name as class to aid with JS/CSS selectors.
2644 $class = array('form-item');
2645 if (!empty($element['#type'])) {
2646 $class[] = 'form-item-' . strtr($element['#type'], array('_' => '-'));
2647 }
2648 if (!empty($element['#name'])) {
2649 $class[] = strtr($element['#name'], array(' ' => '-', '_' => '-', '[' => '-', ']' => '')) . '-wrapper';
2650 }
2651
2652 $output = '<div class="' . implode(' ', $class) . '">' . "\n";
2653 $required = !empty($element['#required']) ? '<span class="form-required" title="' . $t('This field is required.') . '">*</span>' : '';
2654
2655 if (!empty($element['#title']) && empty($element['#form_element_skip_title'])) {
2656 $title = $element['#title'];
2657 if (!empty($element['#id'])) {
2658 $output .= ' <label for="' . $element['#id'] . '">' . $t('!title !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
2659 }
2660 else {
2661 $output .= ' <label>' . $t('!title !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
2662 }
2663 }
2664
2665 $output .= " " . $element['#children'] . "\n";
2666
2667 if (!empty($element['#description'])) {
2668 $output .= ' <div class="description">' . $element['#description'] . "</div>\n";
2669 }
2670
2671 $output .= "</div>\n";
2672
2673 return $output;
2674 }
2675
2676 /**
2677 * Sets a form element's class attribute.
2678 *
2679 * Adds 'required' and 'error' classes as needed.
2680 *
2681 * @param &$element
2682 * The form element.
2683 * @param $name
2684 * Array of new class names to be added.
2685 */
2686 function _form_set_class(&$element, $class = array()) {
2687 if ($element['#required']) {
2688 $class[] = 'required';
2689 }
2690 if (form_get_error($element)) {
2691 $class[] = 'error';
2692 }
2693 if (isset($element['#attributes']['class'])) {
2694 $class[] = $element['#attributes']['class'];
2695 }
2696 $element['#attributes']['class'] = implode(' ', $class);
2697 }
2698
2699 /**
2700 * Prepare an HTML ID attribute string for a form item.
2701 *
2702 * Remove invalid characters and guarantee uniqueness.
2703 *
2704 * @param $id
2705 * The ID to clean.
2706 * @param $flush
2707 * If set to TRUE, the function will flush and reset the static array
2708 * which is built to test the uniqueness of element IDs. This is only
2709 * used if a form has completed the validation process. This parameter
2710 * should never be set to TRUE if this function is being called to
2711 * assign an ID to the #ID element.
2712 * @return
2713 * The cleaned ID.
2714 */
2715 function form_clean_id($id = NULL) {
2716 $seen_ids = &drupal_static(__FUNCTION__, array());
2717 $id = str_replace(array('][', '_', ' '), '-', $id);
2718
2719 // Ensure IDs are unique. The first occurrence is held but left alone.
2720 // Subsequent occurrences get a number appended to them. This incrementing
2721 // will almost certainly break code that relies on explicit HTML IDs in
2722 // forms that appear more than once on the page, but the alternative is
2723 // outputting duplicate IDs, which would break JS code and XHTML
2724 // validity anyways. For now, it's an acceptable stopgap solution.
2725 if (isset($seen_ids[$id])) {
2726 $id = $id . '-' . $seen_ids[$id]++;
2727 }
2728 else {
2729 $seen_ids[$id] = 1;
2730 }
2731
2732 return $id;
2733 }
2734
2735 /**
2736 * @} End of "defgroup form_api".
2737 */
2738
2739 /**
2740 * @defgroup batch Batch operations
2741 * @{
2742 * Functions allowing forms processing to be spread out over several page
2743 * requests, thus ensuring that the processing does not get interrupted
2744 * because of a PHP timeout, while allowing the user to receive feedback
2745 * on the progress of the ongoing operations.
2746 *
2747 * The API is primarily designed to integrate nicely with the Form API
2748 * workflow, but can also be used by non-FAPI scripts (like update.php)
2749 * or even simple page callbacks (which should probably be used sparingly).
2750 *
2751 * Example:
2752 * @code
2753 * $batch = array(
2754 * 'title' => t('Exporting'),
2755 * 'operations' => array(
2756 * array('my_function_1', array($account->uid, 'story')),
2757 * array('my_function_2', array()),
2758 * ),
2759 * 'finished' => 'my_finished_callback',
2760 * );
2761 * batch_set($batch);
2762 * // only needed if not inside a form _submit handler :
2763 * batch_process();
2764 * @endcode
2765 *
2766 * Note - if the batch 'title', 'init_message', 'progress_message',
2767 * or 'error_message' could contain any user input, it is the responsibility of
2768 * the code calling batch_set() to sanitize them first with a function like
2769 * check_plain() or filter_xss().
2770 *
2771 * Sample batch operations:
2772 * @code
2773 * // Simple and artificial: load a node of a given type for a given user
2774 * function my_function_1($uid, $type, &$context) {
2775 * // The $context array gathers batch context information about the execution (read),
2776 * // as well as 'return values' for the current operation (write)
2777 * // The following keys are provided :
2778 * // 'results' (read / write): The array of results gathered so far by
2779 * // the batch processing, for the current operation to append its own.
2780 * // 'message' (write): A text message displayed in the progress page.
2781 * // The following keys allow for multi-step operations :
2782 * // 'sandbox' (read / write): An array that can be freely used to
2783 * // store persistent data between iterations. It is recommended to
2784 * // use this instead of $_SESSION, which is unsafe if the user
2785 * // continues browsing in a separate window while the batch is processing.
2786 * // 'finished' (write): A float number between 0 and 1 informing
2787 * // the processing engine of the completion level for the operation.
2788 * // 1 (or no value explicitly set) means the operation is finished
2789 * // and the batch processing can continue to the next operation.
2790 *
2791 * $node = node_load(array('uid' => $uid, 'type' => $type));
2792 * $context['results'][] = $node->nid . ' : ' . $node->title;
2793 * $context['message'] = $node->title;
2794 * }
2795 *
2796 * // More advanced example: multi-step operation - load all nodes, five by five
2797 * function my_function_2(&$context) {
2798 * if (empty($context['sandbox'])) {
2799 * $context['sandbox']['progress'] = 0;
2800 * $context['sandbox']['current_node'] = 0;
2801 * $context['sandbox']['max'] = db_query('SELECT COUNT(DISTINCT nid) FROM {node}')->fetchField();
2802 * }
2803 * $limit = 5;
2804 * $result = db_select('node')
2805 * ->fields('node', array('nid'))
2806 * ->condition('nid', $context['sandbox']['current_node'], '>')
2807 * ->orderBy('nid')
2808 * ->range(0, $limit)
2809 * ->execute();
2810 * foreach ($result as $row) {
2811 * $node = node_load($row->nid, NULL, TRUE);
2812 * $context['results'][] = $node->nid . ' : ' . $node->title;
2813 * $context['sandbox']['progress']++;
2814 * $context['sandbox']['current_node'] = $node->nid;
2815 * $context['message'] = $node->title;
2816 * }
2817 * if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
2818 * $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
2819 * }
2820 * }
2821 * @endcode
2822 *
2823 * Sample 'finished' callback:
2824 * @code
2825 * function batch_test_finished($success, $results, $operations) {
2826 * if ($success) {
2827 * $message = format_plural(count($results), 'One post processed.', '@count posts processed.');
2828 * }
2829 * else {
2830 * $message = t('Finished with an error.');
2831 * }
2832 * drupal_set_message($message);
2833 * // Providing data for the redirected page is done through $_SESSION.
2834 * foreach ($results as $result) {
2835 * $items[] = t('Loaded node %title.', array('%title' => $result));
2836 * }
2837 * $_SESSION['my_batch_results'] = $items;
2838 * }
2839 * @endcode
2840 */
2841
2842 /**
2843 * Open a new batch.
2844 *
2845 * @param $batch
2846 * An array defining the batch. The following keys can be used:
2847 * 'operations': an array of function calls to be performed.
2848 * Example:
2849 * @code
2850 * array(
2851 * array('my_function_1', array($arg1)),
2852 * array('my_function_2', array($arg2_1, $arg2_2)),
2853 * )
2854 * @endcode
2855 * All the other values below are optional.
2856 * batch_init() provides default values for the messages.
2857 * 'title': title for the progress page. Only safe strings should be passed.
2858 * Defaults to t('Processing').
2859 * 'init_message': message displayed while the processing is initialized.
2860 * Defaults to t('Initializing.').
2861 * 'progress_message': message displayed while processing the batch.
2862 * Available placeholders are @current, @remaining, @total, @percentage,
2863 * @estimate and @elapsed.
2864 * Defaults to t('Completed @current of @total.').
2865 * 'error_message': message displayed if an error occurred while processing
2866 * the batch.
2867 * Defaults to t('An error has occurred.').
2868 * 'finished': the name of a function to be executed after the batch has
2869 * completed. This should be used to perform any result massaging that
2870 * may be needed, and possibly save data in $_SESSION for display after
2871 * final page redirection.
2872 * 'file': the path to the file containing the definitions of the
2873 * 'operations' and 'finished' functions, for instance if they don't
2874 * reside in the original '.module' file. The path should be relative to
2875 * the base_path(), and thus should be built using drupal_get_path().
2876 * 'css' : an array of paths to CSS files to be used on the progress page.
2877 *
2878 * Operations are added as new batch sets. Batch sets are used to ensure
2879 * clean code independence, ensuring that several batches submitted by
2880 * different parts of the code (core / contrib modules) can be processed
2881 * correctly while not interfering or having to cope with each other. Each
2882 * batch set gets to specify his own UI messages, operates on its own set
2883 * of operations and results, and triggers its own 'finished' callback.
2884 * Batch sets are processed sequentially, with the progress bar starting
2885 * fresh for every new set.
2886 */
2887 function batch_set($batch_definition) {
2888 if ($batch_definition) {
2889 $batch =& batch_get();
2890 // Initialize the batch
2891 if (empty($batch)) {
2892 $batch = array(
2893 'sets' => array(),
2894 );
2895 }
2896
2897 $init = array(
2898 'sandbox' => array(),
2899 'results' => array(),
2900 'success' => FALSE,
2901 'start' => microtime(TRUE),
2902 'elapsed' => 0,
2903 );
2904 // Use get_t() to allow batches at install time.
2905 $t = get_t();
2906 $defaults = array(
2907 'title' => $t('Processing'),
2908 'init_message' => $t('Initializing.'),
2909 'progress_message' => $t('Completed @current of @total.'),
2910 'error_message' => $t('An error has occurred.'),
2911 'css' => array(),
2912 );
2913 $batch_set = $init + $batch_definition + $defaults;
2914
2915 // Tweak init_message to avoid the bottom of the page flickering down after init phase.
2916 $batch_set['init_message'] .= '<br/>&nbsp;';
2917 $batch_set['total'] = count($batch_set['operations']);
2918
2919 // If the batch is being processed (meaning we are executing a stored submit handler),
2920 // insert the new set after the current one.
2921 if (isset($batch['current_set'])) {
2922 // array_insert does not exist...
2923 $slice1 = array_slice($batch['sets'], 0, $batch['current_set'] + 1);
2924 $slice2 = array_slice($batch['sets'], $batch['current_set'] + 1);
2925 $batch['sets'] = array_merge($slice1, array($batch_set), $slice2);
2926 }
2927 else {
2928 $batch['sets'][] = $batch_set;
2929 }
2930 }
2931 }
2932
2933 /**
2934 * Process the batch.
2935 *
2936 * Unless the batch has been marked with 'progressive' = FALSE, the function
2937 * issues a drupal_goto and thus ends page execution.
2938 *
2939 * This function is generally not needed in form submit handlers;
2940 * Form API takes care of batches that were set during form submission.
2941 *
2942 * @param $redirect
2943 * (optional) Path to redirect to when the batch has finished processing.
2944 * @param $url
2945 * (optional - should only be used for separate scripts like update.php)
2946 * URL of the batch processing page.
2947 */
2948 function batch_process($redirect = NULL, $url = NULL) {
2949 $batch =& batch_get();
2950
2951 if (isset($batch)) {
2952 // Add process information
2953 $url = isset($url) ? $url : 'batch';
2954 $process_info = array(
2955 'current_set' => 0,
2956 'progressive' => TRUE,
2957 'url' => isset($url) ? $url : 'batch',
2958 'source_page' => $_GET['q'],
2959 'redirect' => $redirect,
2960 );
2961 $batch += $process_info;
2962
2963 if ($batch['progressive']) {
2964 // Clear the way for the drupal_goto redirection to the batch processing
2965 // page, by saving and unsetting the 'destination' if any, on both places
2966 // drupal_goto looks for it.
2967 if (isset($_REQUEST['destination'])) {
2968 $batch['destination'] = $_REQUEST['destination'];
2969 unset($_REQUEST['destination']);
2970 }
2971
2972 // Initiate db storage in order to get a batch id. We have to provide
2973 // at least an empty string for the (not null) 'token' column.
2974 $batch['id'] = db_insert('batch')
2975 ->fields(array(
2976 'token' => '',
2977 'timestamp' => REQUEST_TIME,
2978 ))
2979 ->execute();
2980
2981 // Now that we have a batch id, we can generate the redirection link in
2982 // the generic error message.
2983 $t = get_t();
2984 $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')))));
2985
2986 // Actually store the batch data and the token generated form the batch id.
2987 db_update('batch')
2988 ->condition('bid', $batch['id'])
2989 ->fields(array(
2990 'token' => drupal_get_token($batch['id']),
2991 'batch' => serialize($batch),
2992 ))
2993 ->execute();
2994
2995 // Set the batch number in the session to guarantee that it will stay alive.
2996 $_SESSION['batches'][$batch['id']] = TRUE;
2997
2998 drupal_goto($batch['url'], 'op=start&id=' . $batch['id']);
2999 }
3000 else {
3001 // Non-progressive execution: bypass the whole progressbar workflow
3002 // and execute the batch in one pass.
3003 require_once DRUPAL_ROOT . '/includes/batch.inc';
3004 _batch_process();
3005 }
3006 }
3007 }
3008
3009 /**
3010 * Retrieve the current batch.
3011 */
3012 function &batch_get() {
3013 $batch = &drupal_static(__FUNCTION__, array());
3014 return $batch;
3015 }
3016
3017 /**
3018 * @} End of "defgroup batch".
3019 */
3020

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.