form.inc 31.4 KB
Newer Older
1
<?php
2
3
// $Id$

4
5
6
7
8
9
10
/**
 * @defgroup form Form generation
 * @{
 * Functions to enable output of HTML forms and form elements.
 *
 * Drupal uses these functions to achieve consistency in its form presentation,
 * while at the same time simplifying code and reducing the amount of HTML that
11
12
13
14
15
 * must be explicitly generated by modules. See the
 * <a href="http://drupaldocs.org/api/head/file/contributions/docs/developer/topics/forms_api_reference.html">reference</a>
 * and the
 * <a href="http://drupaldocs.org/api/file/contributions/docs/developer/topics/forms_api.html">quickstart</a>
 * guide for more.
16
17
18
19
20
21
 */

/**
 * Check if the key is a property.
 */
function element_property($key) {
22
  return $key[0] == '#';
23
24
25
}

function element_properties($element) {
Steven Wittens's avatar
Steven Wittens committed
26
  return array_filter(array_keys((array) $element), 'element_property');
27
28
29
30
31
32
}

/**
 * Check if the key is a child.
 */
function element_child($key) {
33
  return $key[0] != '#';
34
35
36
}

function element_children($element) {
Steven Wittens's avatar
Steven Wittens committed
37
  return array_filter(array_keys((array) $element), 'element_child');
38
39
40
41
42
43
}

/**
 * Processes a form array, and produces the HTML output of a form.
 * If there is input in the $_POST['edit'] variable, this function
 * will attempt to validate it, using <code>drupal_validate_form</code>,
44
 * and then submit the form using <code>drupal_submit_form</code>.
45
46
47
48
49
50
51
52
53
54
 *
 * @param $form_id
 *   A unique string identifying the form. Allows each form to be themed.
 * @param $form
 *   An associative array containing the structure of the form.
 * @param $callback
 *   An optional callback that will be used in addition to the form_id.
 *
 */
function drupal_get_form($form_id, &$form, $callback = NULL) {
Dries's avatar
Dries committed
55
  global $form_values, $form_submitted;
56
  $form_values = array();
Dries's avatar
Dries committed
57
  $form_submitted = FALSE;
Steven Wittens's avatar
Steven Wittens committed
58

59
60
  $form['#type'] = 'form';
  if (isset($form['#token'])) {
61
62
63
64
65
    // Make sure that a private key is set:
    if (!variable_get('drupal_private_key', '')) {
      variable_set('drupal_private_key', mt_rand());
    }

66
    $form['form_token'] = array('#type' => 'hidden', '#value' => md5($_SERVER['REMOTE_ADDR'] . $form['#token'] . variable_get('drupal_private_key', '')));
67
  }
Steven Wittens's avatar
Steven Wittens committed
68
69
  $form['form_id'] = array('#type' => 'hidden', '#default_value' => $form_id);

70
71
  $form = array_merge(_element_info('form'), $form);

Dries's avatar
Dries committed
72
73
  if (!isset($form['#validate'])) {
    if (function_exists($form_id .'_validate')) {
74
      $form['#validate'] = array($form_id .'_validate' => array());
Dries's avatar
Dries committed
75
76
    }
    elseif (function_exists($callback .'_validate')) {
77
      $form['#validate'] = array($callback .'_validate' => array());
Dries's avatar
Dries committed
78
79
80
    }
  }

81
82
  if (!isset($form['#submit'])) {
    if (function_exists($form_id .'_submit')) {
83
84
85
      // we set submit here so that it can be altered but use reference for
      // $form_values because it will change later
      $form['#submit'] = array($form_id .'_submit' => array($form_id, &$form_values));
Dries's avatar
Dries committed
86
    }
87
    elseif (function_exists($callback .'_submit')) {
88
      $form['#submit'] = array($callback .'_submit' => array($form_id, &$form_values));
Dries's avatar
Dries committed
89
90
91
    }
  }

92
93
  foreach (module_implements('form_alter') as $module) {
    $function = $module .'_form_alter';
94
    $function($form_id, $form);
95
96
  }

97
  $form = _form_builder($form_id, $form);
Steven Wittens's avatar
Steven Wittens committed
98

99
  if (!empty($_POST['edit']) && (($_POST['edit']['form_id'] == $form_id) || ($_POST['edit']['form_id'] == $callback))) {
100
    drupal_validate_form($form_id, $form, $callback);
Dries's avatar
Dries committed
101
    if ($form_submitted && !form_get_errors()) {
102
      drupal_submit_form($form_id, $form, $callback);
103
    }
104
105
  }

106
  if (theme_get_function($form_id)) {
107
    $form['#theme'] = $form_id;
108
  }
109
  elseif (theme_get_function($callback)) {
110
    $form['#theme'] = $callback;
111
112
113
114
115
116
117
  }
  return form_render($form);
}

function drupal_validate_form($form_id, &$form, $callback = NULL) {
  global $form_values;

118
119
  if (isset($form['#token'])) {
    if ($form_values['form_token'] != md5($_SERVER['REMOTE_ADDR'] . $form['#token'] . variable_get('drupal_private_key', ''))) {
120
121
122
123
124
      // setting this error will cause the form to fail validation
      form_set_error('form_token', t('Validation error, please try again.  If this error persists, please contact the site administrator.'));
    }
  }

125
  _form_validate($form, $form_id);
126
127
}

128
function drupal_submit_form($form_id, $form, $callback = NULL) {
129
130
  global $form_values;

131
  if (isset($form['#submit'])) {
132
133
134
    foreach ($form['#submit'] as $function => $args) {
      if (function_exists($function)) {
        call_user_func_array($function, $args);
Dries's avatar
Dries committed
135
136
      }
    }
137
138
139
  }
}

140
function _form_validate($elements, $form_id = NULL) {
141
  /* Validate the current input */
142
  if (!$elements['#validated'] && ($elements['#input'] || isset($form_id))) {
143
144
145
    // An empty checkbox returns 0, an empty textfield returns '' so we use empty().
    // Unfortunately, empty('0') returns TRUE so we need a special check for the '0' string.
    if ($elements['#required'] && empty($elements['#value']) && $elements['#value'] !== '0') {
146
      form_error($elements, t('%name field is required.', array('%name' => $elements['#title'])));
147
    }
148
149
150
151

    // Add legal choice check if element has #options.
    if (isset($elements['#options']) && isset($elements['#value'])) {
      $message = t('Illegal choice in %title.', array('%title' => theme('placeholder', $elements['#title'])));
152
153
154
155
156
157
      if ($elements['#type'] == 'select') {
        $options = form_options_flatten($elements['#options']);
      }
      else {
        $options = $elements['#options'];
      }
158
159
160
      if (is_array($elements['#value'])) {
        $value = $elements['#type'] == 'checkboxes' ? array_keys(array_filter($elements['#value'])) : $elements['#value'];
        foreach ($value as $v) {
161
          if (!isset($options[$v])) {
162
163
164
165
            form_error($elements, $message);
          }
        }
      }
166
      elseif (!isset($options[$elements['#value']])) {
167
168
169
170
171
        form_error($elements, $message);
      }
    }

    // User-applied checks.
Dries's avatar
Dries committed
172
    if (isset($elements['#validate'])) {
173
174
175
176
177
      foreach ($elements['#validate'] as $function => $args) {
        $args = array_merge(array($elements), $args);
        // for the full form we hand over a copy of $form_values
        if (isset($form_id)) {
          $args = array_merge(array($form_id, $GLOBALS['form_values']), $args);
178
        }
179
180
        if (function_exists($function))  {
          call_user_func_array($function, $args);
181
182
183
        }
      }
    }
184
    $elements['#validated'] = TRUE;
185
  }
186
187
188
189
190
191
192

  // Recurse through all children.
  foreach (element_children($elements) as $key) {
    if (isset($elements[$key]) && $elements[$key]) {
      _form_validate($elements[$key]);
    }
  }
193
194
}

195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/**
 * File an error against a form element. If the name of the element is
 * edit[foo][bar] then you may pass either foo or foo][bar as $name
 * foo will set an error for all its children.
 */
function form_set_error($name = NULL, $message = NULL) {
  static $form = array();
  if (isset($name) && !isset($form[$name])) {
    $form[$name] = $message;
    drupal_set_message($message, 'error');
  }
  return $form;
}

/**
 * Return an associative array of all errors.
 */
function form_get_errors() {
  $form = form_set_error();
  if (!empty($form)) {
    return $form;
  }
}

/**
 * Return the error message filed against the form with the specified name.
 */
function form_get_error($element) {
  $form = form_set_error();
  $key = $element['#parents'][0];
  if (isset($form[$key])) {
    return $form[$key];
  }
  $key = implode('][', $element['#parents']);
  if (isset($form[$key])) {
    return $form[$key];
  }
}

234
235
236
237
/**
 * Flag an element as having an error.
 */
function form_error(&$element, $message) {
238
  $element['#error'] = TRUE;
239
  form_set_error(implode('][', $element['#parents']), $message);
240
241
242
243
244
245
246
}

/**
 * Adds some required properties to each form element, which are used internally in the form api.
 * This function also automatically assigns the value property from the $edit array, provided the
 * element doesn't already have an assigned value.
 */
247
function _form_builder($form_id, $form) {
248
  global $form_values;
Dries's avatar
Dries committed
249
  global $form_submitted;
250
  /* Use element defaults */
251
  if ((!empty($form['#type'])) && ($info = _element_info($form['#type']))) {
252
253
254
    $form += $info;
  }

255
  if ($form['#input']) {
256
257
258
259
260
261
    if (!isset($form['#name'])) {
      $form['#name'] = 'edit[' . implode('][', $form['#parents']) . ']';
    }
    if (!isset($form['#id'])) {
      $form['#id'] =  'edit-' . implode('-', $form['#parents']);
    }
262

263
    $posted = (isset($_POST['edit']) && ($_POST['edit']['form_id'] == $form_id));
264
265
    $edit = $posted ? $_POST['edit'] : array();
    $ref =& $form_values;
266
    foreach ($form['#parents'] as $parent) {
267
268
269
      $edit = isset($edit[$parent]) ? $edit[$parent] : NULL;
      $ref =& $ref[$parent];
    }
270
    if (!isset($form['#value'])) {
271
272
273
274
275
276
277
278
279
      if ($posted) {
        if (isset($edit)) {
          $form['#value'] = $edit; // normal element
        }
        elseif (isset($form['#return_value'])) {
          $form['#value'] = 0; // checkbox unchecked
        }
      }
      if (!isset($form['#value'])) {
280
281
282
283
284
285
286
        $function = $form['#type'] . '_value';
        if (function_exists($function)) {
          $function($form);
        }
        else {
          $form['#value'] = $form['#default_value'];
        }
287
      }
288
    }
Dries's avatar
Dries committed
289
    if (isset($form['#form_submitted'])) {
290
      if ($_POST[$form['#name']] == $form['#value']) {
Dries's avatar
Dries committed
291
        $form_submitted = $form_submitted || $form['#form_submitted'];
292
293
294
295
296
      }
    }
  }

  // Allow for elements to expand to multiple elements. Radios, checkboxes and files for instance.
297
  if (isset($form['#process']) && !$form['#processed']) {
298
299
    foreach ($form['#process'] as $process => $args) {
      if (function_exists($process)) {
300
301
        $args = array_merge(array($form), $args);
        $form = call_user_func_array($process, $args);
302
303
      }
    }
304
    $form['#processed'] = TRUE;
305
306
  }

307
308
309
310
311
312
313
  // Set the $form_values key that gets passed to validate and submit.
  // We call this after #process gets called so that #process has a
  // chance to update #value if desired.
  if ($form['#input']) {
    $ref = $form['#value'];
  }

314
315
316
  // Recurse through all child elements.
  $count  = 0;
  foreach (element_children($form) as $key) {
Steven Wittens's avatar
Steven Wittens committed
317
    // don't squash an existing tree value
318
319
320
    if (!isset($form[$key]['#tree'])) {
      $form[$key]['#tree'] = $form['#tree'];
    }
Steven Wittens's avatar
Steven Wittens committed
321

322
323
    // don't squash existing parents value
    if (!isset($form[$key]['#parents'])) {
324
325
      // Check to see if a tree of child elements is present. If so, continue down the tree if required.
      $form[$key]['#parents'] = $form[$key]['#tree'] && $form['#tree'] ? array_merge($form['#parents'], array($key)) : array($key);
Steven Wittens's avatar
Steven Wittens committed
326
327
    }

328
    # Assign a decimal placeholder weight, to preserve original array order
329
330
331
    if (!isset($form[$key]['#weight'])) {
      $form[$key]['#weight'] = $count/1000;
    }
332
    $form[$key] = _form_builder($form_id, $form[$key]);
333
334
335
    $count++;
  }

336
  if (function_exists($form['#after_build']) && !isset($form['#after_build_done'])) {
337
338
    $function = $form['#after_build'];
    $form = $function($form, $form_values, $ref);
339
    $form['#after_build_done'] = TRUE;
Steven Wittens's avatar
Steven Wittens committed
340
  }
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356

  return $form;
}

/**
 * Renders a HTML form given an form tree.  Recursively iterates over each of
 * each of the form elements generating HTML code.  This function is usually
 * called from within a theme.  To render a form from within a module, use
 * <code>drupal_get_form()</code>.
 *
 * @param $elements
 *   The form tree describing the form.
 * @return
 *   The rendered HTML form.
 */
function form_render(&$elements) {
357
358
  if (!isset($elements)) {
    return NULL;
359
  }
360
361
  $content = '';
  uasort($elements, "_form_sort");
362

363
  if (!$elements['#children']) {
364
    /* render all the children using a theme function */
365
366
367
368
369
370
    if ($elements['#theme'] && !$elements['#theme_used']) {
      $elements['#theme_used'] = TRUE;
      $previous_type = $elements['#type'];
      $elements['#type'] = 'markup';
      $content = theme($elements['#theme'], $elements);
      $elements['#type'] = $previous_type;
371
372
373
374
375
376
377
378
379
    }
    /* render each of the children using form_render and concatenate them */
    if (!$content) {
      foreach (element_children($elements) as $key) {
        $content .= form_render($elements[$key]);
      }
    }
  }
  if ($content) {
380
    $elements['#children'] = $content;
381
382
383
  }

  /* Call the form element renderer */
384
385
386
  if (!$elements['#printed']) {
    $content = theme(($elements['#type']) ? $elements['#type']: 'markup', $elements);
    $elements['#printed'] = TRUE;
387
388
  }

389
390
391
  if ($content) {
    return $elements['#prefix'] . $content . $elements['#suffix'];
  }
392
393
394
395
396
397
}

/**
 * Function used by uasort in form render to sort form via weight.
 */
function _form_sort($a, $b) {
398
399
400
  $a_weight = (is_array($a) && isset($a['#weight'])) ? $a['#weight'] : 0;
  $b_weight = (is_array($b) && isset($b['#weight'])) ? $b['#weight'] : 0;
  if ($a_weight == $b_weight) {
Dries's avatar
Dries committed
401
402
    return 0;
  }
403
  return ($a_weight < $b_weight) ? -1 : 1;
404
405
406
407
408
409
410
}

/**
 * Retrieve the default properties for the defined element type.
 */
function _element_info($type, $refresh = null) {
  static $cache;
Steven Wittens's avatar
Steven Wittens committed
411
412

  $parents = array();
413
  $basic_defaults = array(
414
415
416
    '#description' => NULL,
    '#attributes' => array(),
    '#required' => FALSE,
Steven Wittens's avatar
Steven Wittens committed
417
418
    '#tree' => FALSE,
    '#parents' => $parents
419
  );
420
  if ($refresh || !isset($cache)) {
421
422
423
    $cache = array();
    foreach (module_implements('elements') as $module) {
      $elements = module_invoke($module, 'elements');
424
      if (isset($elements) && is_array($elements)) {
425
        $cache = array_merge_recursive($cache, $elements);
426
427
428
429
      }
    }
    if (sizeof($cache)) {
      foreach ($cache as $element_type => $info) {
430
        $cache[$element_type] = array_merge_recursive($basic_defaults, $info);
431
432
433
434
435
436
437
      }
    }
  }

  return $cache[$type];
}

438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
function form_options_flatten($array, $reset = TRUE) {
  static $return;

  if ($reset) {
    $return = array();
  }

  foreach ($array as $key => $value) {
    if (is_array($value)) {
      form_options_flatten($value, FALSE);
    }
    else {
      $return[$key] = 1;
    }
  }

  return $return;
}

457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
/**
 * Format a dropdown menu or scrolling selection box.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used : title, value, options, description, extra, multiple, required
 * @return
 *   A themed HTML string representing the form element.
 *
 * It is possible to group options together; to do this, change the format of
 * $options to an associative array in which the keys are group labels, and the
 * values are associative arrays in the normal $options format.
 */
function theme_select($element) {
  $select = '';
472
  $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';
473
474
475
476
  // array_key_exists accomodates the rare event where $element['#value'] is NULL.
  // isset() fails in this situation.
  $value_valid = isset($element['#value']) || array_key_exists('#value', $element);
  $value_is_array = is_array($element['#value']);
477
  foreach ($element['#options'] as $key => $choice) {
478
479
480
    if (is_array($choice)) {
      $select .= '<optgroup label="'. $key .'">';
      foreach ($choice as $key => $choice) {
481
        if ($value_valid && ($element['#value'] == $key || ($value_is_array && in_array($key, $element['#value'])))) {
482
483
484
485
486
487
          $selected = ' selected="selected"';
        }
        else {
          $selected = '';
        }
        $select .= '<option value="'. $key .'"'. $selected .'>'. check_plain($choice) .'</option>';
488
489
490
491
      }
      $select .= '</optgroup>';
    }
    else {
492
      if ($value_valid && ($element['#value'] == $key || ($value_is_array && in_array($key, $element['#value'])))) {
493
494
495
496
497
498
        $selected = ' selected="selected"';
      }
      else {
        $selected = '';
      }
      $select .= '<option value="'. $key .'"'. $selected .'>'. check_plain($choice) .'</option>';
499
500
    }
  }
501
  return theme('form_element', $element['#title'], '<select name="'. $element['#name'] .''. ($element['#multiple'] ? '[]' : '') .'"'. ($element['#multiple'] ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) .' id="' . $element['#id'] .'" '. $size .'>'. $select .'</select>', $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
502
503
504
505
506
507
508
509
510
511
512
513
}

/**
 * Format a group of form items.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used : attributes, title, description, children, collapsible, collapsed
 * @return
 *   A themed HTML string representing the form item group.
 */
function theme_fieldset($element) {
514
  if ($element['#collapsible']) {
515
516
    drupal_add_js('misc/collapse.js');

517
518
519
    $element['#attributes']['class'] .= ' collapsible';
    if ($element['#collapsed']) {
     $element['#attributes']['class'] .= ' collapsed';
520
521
522
    }
  }

523
  return '<fieldset' . drupal_attributes($element['#attributes']) .'>' . ($element['#title'] ? '<legend>'. $element['#title'] .'</legend>' : '') . $element['#children'] . $element['#value'] . ($element['#description'] ? '<div class="description">'. $element['#description'] .'</div>' : '') . "</fieldset>\n";
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538

}


/**
 * Format a radio button.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used : required, return_value, value, attributes, title, description
 * @return
 *   A themed HTML string representing the form item group.
 */
function theme_radio($element) {
  $output = '<input type="radio" ';
539
  $output .= 'class="'. _form_get_class('form-radio', $element['#required'], form_get_error($element)) .'" ';
540
541
542
543
544
545
  $output .= 'name="' . $element['#name'] .'" ';
  $output .= 'value="'. $element['#return_value'] .'" ';
  $output .= ($element['#value'] == $element['#return_value']) ? ' checked="checked" ' : ' ';
  $output .= drupal_attributes($element['#attributes']) .' />';
  if (!is_null($element['#title'])) {
    $output = '<label class="option">'. $output .' '. $element['#title'] .'</label>';
546
  }
547
  return theme('form_element', NULL, $output, $element['#description'], $element['#name'], $element['#required'], form_get_error($element));
548
549
550
551
552
553
554
555
556
557
558
559
}

/**
 * Format a set of radio buttons.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used : title, value, options, description, required and attributes.
 * @return
 *   A themed HTML string representing the radio button set.
 */
function theme_radios($element) {
560
  if ($element['#title'] || $element['#description']) {
561
    return theme('form_element', $element['#title'], $element['#children'], $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
562
563
  }
  else {
564
    return $element['#children'];
565
566
567
  }
}

568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
/**
 * Format a password_confirm item.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used: title, value, id, required, error.
 * @return
 *   A themed HTML string representing the form item.
 */
function theme_password_confirm($element) {
  return theme('form_element', $element['#title'], '<div class="container-inline">'. $element['#children']. '</div>', $element['#description'],  $element['#id'], $element['#required'], form_get_error($element));
}

/**
 * Build password_confirm element.
 */
function password_confirm_after_build($form, $form_values, &$ref) {
  if (isset($form_values['pass1'])) {
    $pass1 = trim($form_values['pass1']);
    $pass2 = trim($form_values['pass2']);
    unset($form_values['pass1'], $form_values['pass2']);
    if ($pass1 != $pass2) {
      form_set_error('pass1', t('The specified passwords do not match.'));
    }
    elseif ($form['#required'] && !$pass1) {
      form_set_error('pass1', t('Password field is required.'));
    }
    $ref = $pass1;
  }
  return $form;
}

600
/**
601
 * Format a date selection element.
602
603
604
605
606
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used : title, value, options, description, required and attributes.
 * @return
607
 *   A themed HTML string representing the date selection boxes.
608
609
 */
function theme_date($element) {
610
  $output = '<div class="container-inline">' . $element['#children'] . '</div>';
611
  return theme('form_element', $element['#title'], $output, $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
612
613
614
}

/**
615
 * Roll out a single date element.
616
617
618
 */
function expand_date($element) {
  // Default to current date
619
620
  if (!isset($element['#value'])) {
    $element['#value'] = array('day' => format_date(time(), 'custom', 'j'),
621
622
623
624
                            'month' => format_date(time(), 'custom', 'n'),
                            'year' => format_date(time(), 'custom', 'Y'));
  }

625
626
  $element['#tree'] = TRUE;

627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
  // Determine the order of day, month, year in the site's chosen date format.
  $format = variable_get('date_format_short', 'm/d/Y');
  $sort = array();
  $sort['day'] = max(strpos($format, 'd'), strpos($format, 'j'));
  $sort['month'] = max(strpos($format, 'm'), strpos($format, 'M'));
  $sort['year'] = strpos($format, 'Y');
  asort($sort);
  $order = array_keys($sort);

  // Output multi-selector for date
  foreach ($order as $type) {
    switch ($type) {
      case 'day':
        $options = drupal_map_assoc(range(1, 31));
        break;
      case 'month':
643
        $options = drupal_map_assoc(range(1, 12), 'map_month');
644
645
646
647
648
        break;
      case 'year':
        $options = drupal_map_assoc(range(1900, 2050));
        break;
    }
649
650
651
652
653
654
655
656
    $parents = $element['#parents'];
    $parents[] = $type;
    $element[$type] = array(
      '#type' => 'select',
      '#value' => $element['#value'][$type],
      '#attributes' => $element['#attributes'],
      '#options' => $options,
    );
657
658
659
660
661
  }

  return $element;
}

662
663
664
665
666
667
/**
 * Helper function for usage with drupal_map_assoc to display month names.
 */
function map_month($month) {
  return format_date(gmmktime(0, 0, 0, $month, 2, 1970), 'custom', 'M', 0);
}
668

669
670
671
672
673
674
675
676
677
678
679
/**
 * Helper function to load value from default value for checkboxes
 */
function checkboxes_value(&$form) {
  $value = array();
  foreach ((array)$form['#default_value'] as $key) {
    $value[$key] = 1;
  }
  $form['#value'] = $value;
}

680
/**
681
 * Roll out a single radios element
682
 * to a list of radios, using the options array as index.
683
684
 */
function expand_radios($element) {
685
686
  if (count($element['#options']) > 0) {
    foreach ($element['#options'] as $key => $choice) {
687
      if (!$element[$key]) {
688
        $element[$key] = array('#type' => 'radio', '#title' => $choice, '#return_value' => $key, '#default_value' => $element['#default_value'], '#attributes' => $element['#attributes'], '#parents' => $element['#parents'], '#spawned' => TRUE);
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
      }
    }
  }
  return $element;
}


/**
 * Format a form item.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used :  title, value, description, required, error
 * @return
 *   A themed HTML string representing the form item.
 */
function theme_item($element) {
706
  return theme('form_element', $element['#title'], $element['#value'] . $element['#children'], $element['#description'], $element['#id'], $element['#required'], $element['#error']);
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
}


/**
 * Format a checkbox.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used :  title, value, return_value, description, required
 * @return
 *   A themed HTML string representing the checkbox.
 */
function theme_checkbox($element) {
  $checkbox = '<input ';
  $checkbox .= 'type="checkbox" ';
722
  $checkbox .= 'class="'. _form_get_class('form-checkbox', $element['#required'], form_get_error($element)) . '" ';
723
724
725
  $checkbox .= 'name="'. $element['#name'] .'" ';
  $checkbox .= 'id="'. $element['#id'].'" ' ;
  $checkbox .= 'value="'. $element['#return_value'] .'" ';
726
  $checkbox .= ($element['#return_value'] == $element['#value']) ? ' checked="checked" ' : ' ';
727
728
729
730
  $checkbox .= drupal_attributes($element['#attributes']) . ' />';

  if (!is_null($element['#title'])) {
    $checkbox = '<label class="option">'. $checkbox .' '. $element['#title'] .'</label>';
731
732
  }

733
  return theme('form_element', NULL, $checkbox, $element['#description'], $element['#name'], $element['#required'], form_get_error($element));
734
735
736
737
738
739
740
741
742
743
744
}

/**
 * Format a set of checkboxes.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 * @return
 *   A themed HTML string representing the checkbox set.
 */
function theme_checkboxes($element) {
745
  if ($element['#title'] || $element['#description']) {
746
    return theme('form_element', $element['#title'], $element['#children'], $element['#description'], 'edit-'. $element['#name'], $element['#required'], form_get_error($element));
747
748
  }
  else {
749
    return $element['#children'];
750
751
752
753
  }
}

function expand_checkboxes($element) {
754
  $value = is_array($element['#value']) ? $element['#value'] : array();
Steven Wittens's avatar
Steven Wittens committed
755
  $element['#tree'] = TRUE;
756
757
758
  if (count($element['#options']) > 0) {
    if (!isset($element['#default_value']) || $element['#default_value'] == 0) {
      $element['#default_value'] = array();
759
    }
760
    foreach ($element['#options'] as $key => $choice) {
761
      if (!isset($element[$key])) {
762
        $element[$key] = array('#type' => 'checkbox', '#processed' => TRUE, '#title' => $choice, '#return_value' => $key, '#default_value' => isset($value[$key]), '#attributes' => $element['#attributes']);
763
764
765
766
767
768
769
770
771
772
773
774
      }
    }
  }
  return $element;
}


function theme_submit($element) {
  return theme('button', $element);
}

function theme_button($element) {
775
  return '<input type="submit" class="form-'. $element['#button_type'] .'" name="'. $element['#name'] .'" value="'. check_plain($element['#value']) .'" '. drupal_attributes($element['#attributes']) ." />\n";
776
777
778
779
780
781
782
783
784
785
786
787
}

/**
 * Format a hidden form field.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used :  value, edit
 * @return
 *   A themed HTML string representing the hidden form field.
 */
function theme_hidden($element) {
788
  return '<input type="hidden" name="'. $element['#name'] . '" value="'. check_plain($element['#value']) ."\" " . drupal_attributes($element['#attributes']) ." />\n";
789
790
791
792
793
794
795
796
797
798
799
800
}

/**
 * Format a textfield.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used :  title, value, description, size, maxlength, required, attributes autocomplete_path
 * @return
 *   A themed HTML string representing the textfield.
 */
function theme_textfield($element) {
801
802
  $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';
  if ($element['#autocomplete_path']) {
803
804
    drupal_add_js('misc/autocomplete.js');
    $class = ' form-autocomplete';
805
    $extra =  '<input class="autocomplete" type="hidden" id="'. $element['#id'] .'-autocomplete" value="'. check_url(url($element['#autocomplete_path'], NULL, NULL, TRUE)) .'" disabled="disabled" />';
806
807
  }

808
809
  $output = '<input type="text" maxlength="'. $element['#maxlength'] .'" class="'. _form_get_class("form-text$class", $element['#required'], form_get_error($element)) .'" name="'. $element['#name'] .'" id="'. $element['#id'] .'" '. $size .' value="'. check_plain($element['#value']) .'"'. drupal_attributes($element['#attributes']) .' />';
  return theme('form_element', $element['#title'], $output, $element['#description'], $element['#id'], $element['#required'], form_get_error($element)). $extra;
810
811
812
813
814
815
816
817
818
819
820
821
822
}

/**
 * Format a form.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used : action, method, attributes, children
 * @return
 *   A themed HTML string representing the form.
 */
function theme_form($element) {
  // Anonymous div to satisfy XHTML compliancy.
823
824
  $action = $element['#action'] ? 'action="' . check_url($element['#action']) . '" ' : '';
  return '<form '. $action . ' method="'. $element['#method'] .'" '. drupal_attributes($element['#attributes']) .">\n<div>". $element['#children'] ."\n</div></form>\n";
825
826
827
828
829
830
831
832
833
834
835
836
837
}


/**
 * Format a textarea.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used : title, value, description, rows, cols, required, attributes
 * @return
 *   A themed HTML string representing the textarea.
 */
function theme_textarea($element) {
838
839
840
841
842
843
  $class = 'textarea';
  if ($element['#resizable'] !== false) {
    drupal_add_js('misc/textarea.js');
    $class .= ' resizable';
  }

844
  $cols = $element['#cols'] ? ' cols="'. $element['#cols'] .'"' : '';
845

846
  return theme('form_element', $element['#title'], '<textarea'. $cols .' rows="'. $element['#rows'] .'" name="'. $element['#name'] .'" id="' . $element['#id'] .'" class="'. _form_get_class($class, $element['#required'], form_get_error($element)) .'"'. drupal_attributes($element['#attributes']) .'>'. check_plain($element['#value']) .'</textarea>', $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
}

/**
 * Format HTML markup for use in forms.
 *
 * This is used in more advanced forms, such as theme selection and filter format.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used : prefix, value, children and suffix.
 * @return
 *   A themed HTML string representing the HTML markup.
 */

function theme_markup($element) {
862
  return $element['#value'] . $element['#children'];
863
864
865
866
867
868
869
870
871
872
873
874
875
876
}



/**
* Format a password field.
*
* @param $element
*   An associative array containing the properties of the element.
*   Properties used :  title, value, description, size, maxlength, required, attributes
* @return
*   A themed HTML string representing the form.
*/
function theme_password($element) {
877
  $size = $element['#size'] ? ' size="'. $element['#size'] .'" ' : '';
878

879
  $output = '<input type="password" maxlength="'. $element['#maxlength'] .'" class="'. _form_get_class("form-text $class", $element['#required'], form_get_error($element)) .'" name="'. $element['#name'] .'" id="'. $element['#id'] .'" '. $size . drupal_attributes($element['#attributes']) .' />';
880

881
  return theme('form_element', $element['#title'], $output, $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
882
883
884
885
886
887
888
889
890
891
892
893
}

/**
 * Format a weight selection menu.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used :  title, delta, description
 * @return
 *   A themed HTML string representing the form.
 */
function theme_weight($element) {
894
  for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) {
895
896
    $weights[$n] = $n;
  }
897
898
  $element['#options'] = $weights;
  $element['#type'] = 'select';
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922

  return form_render($element);
}

/**
 * Format a file upload field.
 *
 * @param $title
 *   The label for the file upload field.
 * @param $name
 *   The internal name used to refer to the field.
 * @param $size
 *   A measure of the visible size of the field (passed directly to HTML).
 * @param $description
 *   Explanatory text to display after the form item.
 * @param $required
 *   Whether the user must upload a file to the field.
 * @return
 *   A themed HTML string representing the field.
 *
 * For assistance with handling the uploaded file correctly, see the API
 * provided by file.inc.
 */
function theme_file($element) {
923
  return theme('form_element', $element['#title'], '<input type="file" class="'. _form_get_class('form-file', $element['#required'], form_get_error($element)) .'" name="'. $element['#name'] .'"'. ($element['#attributes'] ? ' '. drupal_attributes($element['#attributes']) : '') .' id="'. form_clean_id($element['#id']) .'" size="'. $element['#size'] ."\" />\n", $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
}
function _form_get_class($name, $required, $error) {
  return $name. ($required ? ' required' : '') . ($error ? ' error' : '');
}

/**
 * Remove invalid characters from an HTML ID attribute string
 *
 * @param $id
 *   The ID to clean
 * @return
 *   The cleaned ID
 */
function form_clean_id($id = NULL) {
  $id = str_replace('][', '-', $id);
  return $id;
}

/**
 * @} End of "defgroup form".
 */