Project

General

Profile

Paste
Download (58 KB) Statistics
| Branch: | Revision:

root / drupal7 / modules / simpletest / tests / form_test.module @ b0dc3a2e

1
<?php
2

    
3
/**
4
 * @file
5
 * Helper module for the form API tests.
6
 */
7

    
8
/**
9
 * Implements hook_menu().
10
 */
11
function form_test_menu() {
12
  $items['form-test/alter'] = array(
13
    'title' => 'Form altering test',
14
    'page callback' => 'drupal_get_form',
15
    'page arguments' => array('form_test_alter_form'),
16
    'access arguments' => array('access content'),
17
    'type' => MENU_CALLBACK,
18
  );
19
  $items['form-test/validate'] = array(
20
    'title' => 'Form validation handlers test',
21
    'page callback' => 'drupal_get_form',
22
    'page arguments' => array('form_test_validate_form'),
23
    'access arguments' => array('access content'),
24
    'type' => MENU_CALLBACK,
25
  );
26
  $items['form-test/validate-required'] = array(
27
    'title' => 'Form #required validation',
28
    'page callback' => 'drupal_get_form',
29
    'page arguments' => array('form_test_validate_required_form'),
30
    'access callback' => TRUE,
31
    'type' => MENU_CALLBACK,
32
  );
33
  $items['form-test/validate-required-no-title'] = array(
34
    'title' => 'Form #required validation without #title',
35
    'page callback' => 'drupal_get_form',
36
    'page arguments' => array('form_test_validate_required_form_no_title'),
37
    'access callback' => TRUE,
38
    'type' => MENU_CALLBACK,
39
  );
40
  $items['form-test/limit-validation-errors'] = array(
41
    'title' => 'Form validation with some error suppression',
42
    'page callback' => 'drupal_get_form',
43
    'page arguments' => array('form_test_limit_validation_errors_form'),
44
    'access arguments' => array('access content'),
45
    'type' => MENU_CALLBACK,
46
  );
47

    
48
  $items['form_test/tableselect/multiple-true'] = array(
49
    'title' => 'Tableselect checkboxes test',
50
    'page callback' => 'drupal_get_form',
51
    'page arguments' => array('_form_test_tableselect_multiple_true_form'),
52
    'access arguments' => array('access content'),
53
    'type' => MENU_CALLBACK,
54
  );
55
  $items['form_test/tableselect/multiple-false'] = array(
56
    'title' => 'Tableselect radio button test',
57
    'page callback' => 'drupal_get_form',
58
    'page arguments' => array('_form_test_tableselect_multiple_false_form'),
59
    'access arguments' => array('access content'),
60
    'type' => MENU_CALLBACK,
61
  );
62
  $items['form_test/tableselect/empty-text'] = array(
63
    'title' => 'Tableselect empty text test',
64
    'page callback' => 'drupal_get_form',
65
    'page arguments' => array('_form_test_tableselect_empty_form'),
66
    'access arguments' => array('access content'),
67
    'type' => MENU_CALLBACK,
68
  );
69
  $items['form_test/tableselect/advanced-select'] = array(
70
    'title' => 'Tableselect js_select tests',
71
    'page callback' => 'drupal_get_form',
72
    'page arguments' => array('_form_test_tableselect_js_select_form'),
73
    'access arguments' => array('access content'),
74
    'type' => MENU_CALLBACK,
75
  );
76

    
77
  $items['form_test/vertical-tabs'] = array(
78
    'title' => 'Vertical tabs tests',
79
    'page callback' => 'drupal_get_form',
80
    'page arguments' => array('_form_test_vertical_tabs_form'),
81
    'access arguments' => array('access content'),
82
    'type' => MENU_CALLBACK,
83
  );
84

    
85
  $items['form_test/form-storage'] = array(
86
    'title' => 'Form storage test',
87
    'page callback' => 'drupal_get_form',
88
    'page arguments' => array('form_test_storage_form'),
89
    'access arguments' => array('access content'),
90
    'type' => MENU_CALLBACK,
91
  );
92

    
93
  $items['form_test/form-storage-legacy'] = array(
94
    'title' => 'Emulate legacy AHAH-style ajax callback',
95
    'page callback' => 'form_test_storage_legacy_handler',
96
    'access arguments' => array('access content'),
97
    'type' => MENU_CALLBACK,
98
  );
99

    
100
  $items['form_test/form-storage-page-cache'] = array(
101
    'title' => 'Form storage with page cache test',
102
    'page callback' => 'drupal_get_form',
103
    'page arguments' => array('form_test_storage_page_cache_form'),
104
    'access arguments' => array('access content'),
105
    'type' => MENU_CALLBACK,
106
  );
107

    
108
  $items['form_test/wrapper-callback'] = array(
109
    'title' => 'Form wrapper callback test',
110
    'page callback' => 'form_test_wrapper_callback',
111
    'page arguments' => array('form_test_wrapper_callback_form'),
112
    'access arguments' => array('access content'),
113
    'type' => MENU_CALLBACK,
114
  );
115

    
116
  $items['form_test/form-state-values-clean'] = array(
117
    'title' => 'Form state values clearance test',
118
    'page callback' => 'drupal_get_form',
119
    'page arguments' => array('form_test_form_state_values_clean_form'),
120
    'access arguments' => array('access content'),
121
    'type' => MENU_CALLBACK,
122
  );
123

    
124
  $items['form_test/form-state-values-clean-advanced'] = array(
125
    'title' => 'Form state values clearance advanced test',
126
    'page callback' => 'drupal_get_form',
127
    'page arguments' => array('form_test_form_state_values_clean_advanced_form'),
128
    'access arguments' => array('access content'),
129
    'type' => MENU_CALLBACK,
130
  );
131

    
132
  $items['form-test/checkbox'] = array(
133
    'title' => t('Form test'),
134
    'page callback' => 'drupal_get_form',
135
    'page arguments' => array('_form_test_checkbox'),
136
    'access callback' => TRUE,
137
    'type' => MENU_CALLBACK,
138
  );
139
  $items['form-test/select'] = array(
140
    'title' => t('Select'),
141
    'page callback' => 'drupal_get_form',
142
    'page arguments' => array('form_test_select'),
143
    'access callback' => TRUE,
144
  );
145
  $items['form-test/checkboxes-radios'] = array(
146
    'title' => t('Checkboxes, Radios'),
147
    'page callback' => 'drupal_get_form',
148
    'page arguments' => array('form_test_checkboxes_radios'),
149
    'access callback' => TRUE,
150
  );
151

    
152
  $items['form-test/disabled-elements'] = array(
153
    'title' => t('Form test'),
154
    'page callback' => 'drupal_get_form',
155
    'page arguments' => array('_form_test_disabled_elements'),
156
    'access callback' => TRUE,
157
    'type' => MENU_CALLBACK,
158
  );
159

    
160
  $items['form-test/input-forgery'] = array(
161
    'title' => t('Form test'),
162
    'page callback' => 'drupal_get_form',
163
    'page arguments' => array('_form_test_input_forgery'),
164
    'access callback' => TRUE,
165
    'type' => MENU_CALLBACK,
166
  );
167

    
168
  $items['form-test/form-rebuild-preserve-values'] = array(
169
    'title' => 'Form values preservation during rebuild test',
170
    'page callback' => 'drupal_get_form',
171
    'page arguments' => array('form_test_form_rebuild_preserve_values_form'),
172
    'access arguments' => array('access content'),
173
    'type' => MENU_CALLBACK,
174
  );
175

    
176
  $items['form-test/redirect'] = array(
177
    'title' => 'Redirect test',
178
    'page callback' => 'drupal_get_form',
179
    'page arguments' => array('form_test_redirect'),
180
    'access callback' => TRUE,
181
    'type' => MENU_CALLBACK,
182
  );
183

    
184
  $items['form_test/form-labels'] = array(
185
    'title' => 'Form label test',
186
    'page callback' => 'drupal_get_form',
187
    'page arguments' => array('form_label_test_form'),
188
    'access arguments' => array('access content'),
189
    'type' => MENU_CALLBACK,
190
  );
191

    
192
  $items['form-test/state-persist'] = array(
193
    'title' => 'Form state persistence without storage',
194
    'page callback' => 'drupal_get_form',
195
    'page arguments' => array('form_test_state_persist'),
196
    'access callback' => TRUE,
197
    'type' => MENU_CALLBACK,
198
  );
199

    
200
  $items['form-test/clicked-button'] = array(
201
    'title' => 'Clicked button test',
202
    'page callback' => 'drupal_get_form',
203
    'page arguments' => array('form_test_clicked_button'),
204
    'access callback' => TRUE,
205
    'type' => MENU_CALLBACK,
206
  );
207

    
208
  if (module_exists('node')) {
209
    $items['form-test/two-instances-of-same-form'] = array(
210
      'title' => 'AJAX test with two form instances',
211
      'page callback' => 'form_test_two_instances',
212
      'access callback' => 'node_access',
213
      'access arguments' => array('create', 'page'),
214
      'file path' => drupal_get_path('module', 'node'),
215
      'file' => 'node.pages.inc',
216
      'type' => MENU_CALLBACK,
217
    );
218
  }
219
  $items['form-test/double-form'] = array(
220
    'title' => 'Double form test',
221
    'page callback' => 'form_test_double_form',
222
    'access callback' => TRUE,
223
    'type' => MENU_CALLBACK,
224
  );
225

    
226
  $items['form-test/load-include-menu'] = array(
227
    'title' => 'FAPI test loading includes',
228
    'page callback' => 'drupal_get_form',
229
    'page arguments' => array('form_test_load_include_menu'),
230
    'access callback' => TRUE,
231
    'file' => 'form_test.file.inc',
232
    'type' => MENU_CALLBACK,
233
  );
234

    
235
  $items['form-test/load-include-custom'] = array(
236
    'title' => 'FAPI test loading includes',
237
    'page callback' => 'drupal_get_form',
238
    'page arguments' => array('form_test_load_include_custom'),
239
    'access callback' => TRUE,
240
    'type' => MENU_CALLBACK,
241
  );
242
  $items['form-test/checkboxes-zero'] = array(
243
    'title' => 'FAPI test involving checkboxes and zero',
244
    'page callback' => 'drupal_get_form',
245
    'page arguments' => array('form_test_checkboxes_zero'),
246
    'access callback' => TRUE,
247
    'type' => MENU_CALLBACK,
248
  );
249

    
250
  return $items;
251
}
252

    
253
/**
254
 * Form submit handler to return form values as JSON.
255
 */
256
function _form_test_submit_values_json($form, &$form_state) {
257
  drupal_json_output($form_state['values']);
258
  drupal_exit();
259
}
260

    
261
/**
262
 * Form builder for testing hook_form_alter() and hook_form_FORM_ID_alter().
263
 */
264
function form_test_alter_form($form, &$form_state) {
265
  // Elements can be added as needed for future testing needs, but for now,
266
  // we're only testing alter hooks that do not require any elements added by
267
  // this function.
268
  return $form;
269
}
270

    
271
/**
272
 * Implements hook_form_FORM_ID_alter() on behalf of block.module.
273
 */
274
function block_form_form_test_alter_form_alter(&$form, &$form_state) {
275
  drupal_set_message('block_form_form_test_alter_form_alter() executed.');
276
}
277

    
278
/**
279
 * Implements hook_form_alter().
280
 */
281
function form_test_form_alter(&$form, &$form_state, $form_id) {
282
  if ($form_id == 'form_test_alter_form') {
283
    drupal_set_message('form_test_form_alter() executed.');
284
  }
285
}
286

    
287
/**
288
 * Implements hook_form_FORM_ID_alter().
289
 */
290
function form_test_form_form_test_alter_form_alter(&$form, &$form_state) {
291
  drupal_set_message('form_test_form_form_test_alter_form_alter() executed.');
292
}
293

    
294
/**
295
 * Implements hook_form_FORM_ID_alter() on behalf of system.module.
296
 */
297
function system_form_form_test_alter_form_alter(&$form, &$form_state) {
298
  drupal_set_message('system_form_form_test_alter_form_alter() executed.');
299
}
300

    
301
/**
302
 * Form builder for testing drupal_validate_form().
303
 *
304
 * Serves for testing form processing and alterations by form validation
305
 * handlers, especially for the case of a validation error:
306
 * - form_set_value() should be able to alter submitted values in
307
 *   $form_state['values'] without affecting the form element.
308
 * - #element_validate handlers should be able to alter the $element in the form
309
 *   structure and the alterations should be contained in the rebuilt form.
310
 * - #validate handlers should be able to alter the $form and the alterations
311
 *   should be contained in the rebuilt form.
312
 */
313
function form_test_validate_form($form, &$form_state) {
314
  $form['name'] = array(
315
    '#type' => 'textfield',
316
    '#title' => 'Name',
317
    '#default_value' => '',
318
    '#element_validate' => array('form_test_element_validate_name'),
319
  );
320
  $form['submit'] = array(
321
    '#type' => 'submit',
322
    '#value' => 'Save',
323
  );
324

    
325
  // To simplify this test, enable form caching and use form storage to
326
  // remember our alteration.
327
  $form_state['cache'] = TRUE;
328

    
329
  return $form;
330
}
331

    
332
/**
333
 * Form element validation handler for 'name' in form_test_validate_form().
334
 */
335
function form_test_element_validate_name(&$element, &$form_state) {
336
  $triggered = FALSE;
337
  if ($form_state['values']['name'] == 'element_validate') {
338
    // Alter the form element.
339
    $element['#value'] = '#value changed by #element_validate';
340
    // Alter the submitted value in $form_state.
341
    form_set_value($element, 'value changed by form_set_value() in #element_validate', $form_state);
342

    
343
    $triggered = TRUE;
344
  }
345
  if ($form_state['values']['name'] == 'element_validate_access') {
346
    $form_state['storage']['form_test_name'] = $form_state['values']['name'];
347
    // Alter the form element.
348
    $element['#access'] = FALSE;
349

    
350
    $triggered = TRUE;
351
  }
352
  elseif (!empty($form_state['storage']['form_test_name'])) {
353
    // To simplify this test, just take over the element's value into $form_state.
354
    form_set_value($element, $form_state['storage']['form_test_name'], $form_state);
355

    
356
    $triggered = TRUE;
357
  }
358

    
359
  if ($triggered) {
360
    // Output the element's value from $form_state.
361
    drupal_set_message(t('@label value: @value', array('@label' => $element['#title'], '@value' => $form_state['values']['name'])));
362

    
363
    // Trigger a form validation error to see our changes.
364
    form_set_error('');
365
  }
366
}
367

    
368
/**
369
 * Form validation handler for form_test_validate_form().
370
 */
371
function form_test_validate_form_validate(&$form, &$form_state) {
372
  if ($form_state['values']['name'] == 'validate') {
373
    // Alter the form element.
374
    $form['name']['#value'] = '#value changed by #validate';
375
    // Alter the submitted value in $form_state.
376
    form_set_value($form['name'], 'value changed by form_set_value() in #validate', $form_state);
377
    // Output the element's value from $form_state.
378
    drupal_set_message(t('@label value: @value', array('@label' => $form['name']['#title'], '@value' => $form_state['values']['name'])));
379

    
380
    // Trigger a form validation error to see our changes.
381
    form_set_error('');
382
  }
383
}
384

    
385
/**
386
 * Form constructor to test the #required property.
387
 */
388
function form_test_validate_required_form($form, &$form_state) {
389
  $options = drupal_map_assoc(array('foo', 'bar'));
390

    
391
  $form['textfield'] = array(
392
    '#type' => 'textfield',
393
    '#title' => 'Textfield',
394
    '#required' => TRUE,
395
  );
396
  $form['checkboxes'] = array(
397
    '#type' => 'checkboxes',
398
    '#title' => 'Checkboxes',
399
    '#options' => $options,
400
    '#required' => TRUE,
401
  );
402
  $form['select'] = array(
403
    '#type' => 'select',
404
    '#title' => 'Select',
405
    '#options' => $options,
406
    '#required' => TRUE,
407
  );
408
  $form['radios'] = array(
409
    '#type' => 'radios',
410
    '#title' => 'Radios',
411
    '#options' => $options,
412
    '#required' => TRUE,
413
  );
414
  $form['radios_optional'] = array(
415
    '#type' => 'radios',
416
    '#title' => 'Radios (optional)',
417
    '#options' => $options,
418
  );
419
  $form['radios_optional_default_value_false'] = array(
420
    '#type' => 'radios',
421
    '#title' => 'Radios (optional, with a default value of FALSE)',
422
    '#options' => $options,
423
    '#default_value' => FALSE,
424
  );
425
  $form['actions'] = array('#type' => 'actions');
426
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => 'Submit');
427
  return $form;
428
}
429

    
430
/**
431
 * Form submission handler for form_test_validate_required_form().
432
 */
433
function form_test_validate_required_form_submit($form, &$form_state) {
434
  drupal_set_message('The form_test_validate_required_form form was submitted successfully.');
435
}
436

    
437
/**
438
 * Form constructor to test the #required property without #title.
439
 */
440
function form_test_validate_required_form_no_title($form, &$form_state) {
441
  $form['textfield'] = array(
442
    '#type' => 'textfield',
443
    '#required' => TRUE,
444
  );
445
  $form['actions'] = array('#type' => 'actions');
446
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => 'Submit');
447
  return $form;
448
}
449

    
450
/**
451
 * Form submission handler for form_test_validate_required_form_no_title().
452
 */
453
function form_test_validate_required_form_no_title_submit($form, &$form_state) {
454
  drupal_set_message('The form_test_validate_required_form_no_title form was submitted successfully.');
455
}
456

    
457
/**
458
 * Builds a simple form with a button triggering partial validation.
459
 */
460
function form_test_limit_validation_errors_form($form, &$form_state) {
461
  $form['title'] = array(
462
    '#type' => 'textfield',
463
    '#title' => 'Title',
464
    '#required' => TRUE,
465
  );
466

    
467
  $form['test'] = array(
468
    '#title' => 'Test',
469
    '#type' => 'textfield',
470
    '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
471
  );
472
  $form['test_numeric_index'] = array(
473
    '#tree' => TRUE,
474
  );
475
  $form['test_numeric_index'][0] = array(
476
    '#title' => 'Test (numeric index)',
477
    '#type' => 'textfield',
478
    '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
479
  );
480

    
481
  $form['test_substring'] = array(
482
    '#tree' => TRUE,
483
  );
484
  $form['test_substring']['foo'] = array(
485
    '#title' => 'Test (substring) foo',
486
    '#type' => 'textfield',
487
    '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
488
  );
489
  $form['test_substring']['foobar'] = array(
490
    '#title' => 'Test (substring) foobar',
491
    '#type' => 'textfield',
492
    '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
493
  );
494

    
495
  $form['actions']['partial'] = array(
496
    '#type' => 'submit',
497
    '#limit_validation_errors' => array(array('test')),
498
    '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
499
    '#value' => t('Partial validate'),
500
  );
501
  $form['actions']['partial_numeric_index'] = array(
502
    '#type' => 'submit',
503
    '#limit_validation_errors' => array(array('test_numeric_index', 0)),
504
    '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
505
    '#value' => t('Partial validate (numeric index)'),
506
  );
507
  $form['actions']['substring'] = array(
508
    '#type' => 'submit',
509
    '#limit_validation_errors' => array(array('test_substring', 'foo')),
510
    '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
511
    '#value' => t('Partial validate (substring)'),
512
  );
513
  $form['actions']['full'] = array(
514
    '#type' => 'submit',
515
    '#value' => t('Full validate'),
516
  );
517
  return $form;
518
}
519

    
520
/**
521
 * Form element validation handler for the 'test' element.
522
 */
523
function form_test_limit_validation_errors_element_validate_test(&$element, &$form_state) {
524
  if ($element['#value'] == 'invalid') {
525
    form_error($element, t('@label element is invalid', array('@label' => $element['#title'])));
526
  }
527
}
528

    
529
/**
530
 * Form submit handler for the partial validation submit button.
531
 */
532
function form_test_limit_validation_errors_form_partial_submit($form, $form_state) {
533
  // The title has not been validated, thus its value - in case of the test case
534
  // an empty string - may not be set.
535
  if (!isset($form_state['values']['title']) && isset($form_state['values']['test'])) {
536
    drupal_set_message('Only validated values appear in the form values.');
537
  }
538
}
539

    
540
/**
541
 * Create a header and options array. Helper function for callbacks.
542
 */
543
function _form_test_tableselect_get_data() {
544
  $header = array(
545
    'one' => t('One'),
546
    'two' => t('Two'),
547
    'three' => t('Three'),
548
    'four' => t('Four'),
549
  );
550

    
551
  $options['row1'] = array(
552
    'one' => 'row1col1',
553
    'two' => t('row1col2'),
554
    'three' => t('row1col3'),
555
    'four' => t('row1col4'),
556
  );
557

    
558
  $options['row2'] = array(
559
    'one' => 'row2col1',
560
    'two' => t('row2col2'),
561
    'three' => t('row2col3'),
562
    'four' => t('row2col4'),
563
  );
564

    
565
  $options['row3'] = array(
566
    'one' => 'row3col1',
567
    'two' => t('row3col2'),
568
    'three' => t('row3col3'),
569
    'four' => t('row3col4'),
570
  );
571

    
572
  return array($header, $options);
573
}
574

    
575
/**
576
 * Build a form to test the tableselect element.
577
 *
578
 * @param $form_state
579
 *   The form_state
580
 * @param $element_properties
581
 *   An array of element properties for the tableselect element.
582
 *
583
 * @return
584
 *   A form with a tableselect element and a submit button.
585
 */
586
function _form_test_tableselect_form_builder($form, $form_state, $element_properties) {
587
  list($header, $options) = _form_test_tableselect_get_data();
588

    
589
  $form['tableselect'] = $element_properties;
590

    
591
  $form['tableselect'] += array(
592
    '#prefix' => '<div id="tableselect-wrapper">',
593
    '#suffix' => '</div>',
594
    '#type' => 'tableselect',
595
    '#header' => $header,
596
    '#options' => $options,
597
    '#multiple' => FALSE,
598
    '#empty' => t('Empty text.'),
599
    '#ajax' => array(
600
      'callback' => '_form_test_tableselect_ajax_callback',
601
      'wrapper' => 'tableselect-wrapper',
602
    ),
603
  );
604

    
605
  $form['submit'] = array(
606
    '#type' => 'submit',
607
    '#value' => t('Submit'),
608
  );
609

    
610
  return $form;
611
}
612

    
613
/**
614
 * Test the tableselect #multiple = TRUE functionality.
615
 */
616
function _form_test_tableselect_multiple_true_form($form, $form_state) {
617
  return _form_test_tableselect_form_builder($form, $form_state, array('#multiple' => TRUE));
618
}
619

    
620
/**
621
 * Process the tableselect #multiple = TRUE submitted values.
622
 */
623
function _form_test_tableselect_multiple_true_form_submit($form, &$form_state) {
624
  $selected = $form_state['values']['tableselect'];
625
  foreach ($selected as $key => $value) {
626
    drupal_set_message(t('Submitted: @key = @value', array('@key' => $key, '@value' => $value)));
627
  }
628
}
629

    
630
/**
631
 * Test the tableselect #multiple = FALSE functionality.
632
 */
633
function _form_test_tableselect_multiple_false_form($form, $form_state) {
634
  return _form_test_tableselect_form_builder($form, $form_state, array('#multiple' => FALSE));
635
}
636

    
637
/**
638
 * Process the tableselect #multiple = FALSE submitted values.
639
 */
640
function _form_test_tableselect_multiple_false_form_submit($form, &$form_state) {
641
  drupal_set_message(t('Submitted: @value', array('@value' => $form_state['values']['tableselect'])));
642
}
643

    
644
/**
645
 * Test functionality of the tableselect #empty property.
646
 */
647
function _form_test_tableselect_empty_form($form, $form_state) {
648
  return _form_test_tableselect_form_builder($form, $form_state, array('#options' => array()));
649
}
650

    
651
/**
652
 * Test functionality of the tableselect #js_select property.
653
 */
654
function _form_test_tableselect_js_select_form($form, $form_state, $action) {
655
  switch ($action) {
656
    case 'multiple-true-default':
657
      $options = array('#multiple' => TRUE);
658
      break;
659

    
660
    case 'multiple-false-default':
661
      $options = array('#multiple' => FALSE);
662
      break;
663

    
664
    case 'multiple-true-no-advanced-select':
665
      $options = array('#multiple' => TRUE, '#js_select' => FALSE);
666
      break;
667

    
668
    case 'multiple-false-advanced-select':
669
      $options = array('#multiple' => FALSE, '#js_select' => TRUE);
670
      break;
671
  }
672

    
673
  return _form_test_tableselect_form_builder($form, $form_state, $options);
674
}
675

    
676
/**
677
 * Tests functionality of vertical tabs.
678
 */
679
function _form_test_vertical_tabs_form($form, &$form_state) {
680
  $form['vertical_tabs'] = array(
681
    '#type' => 'vertical_tabs',
682
  );
683
  $form['tab1'] = array(
684
    '#type' => 'fieldset',
685
    '#title' => t('Tab 1'),
686
    '#collapsible' => TRUE,
687
    '#group' => 'vertical_tabs',
688
  );
689
  $form['tab1']['field1'] = array(
690
    '#title' => t('Field 1'),
691
    '#type' => 'textfield',
692
  );
693
  $form['tab2'] = array(
694
    '#type' => 'fieldset',
695
    '#title' => t('Tab 2'),
696
    '#collapsible' => TRUE,
697
    '#group' => 'vertical_tabs',
698
  );
699
  $form['tab2']['field2'] = array(
700
    '#title' => t('Field 2'),
701
    '#type' => 'textfield',
702
  );
703
  return $form;
704
}
705

    
706
/**
707
* Ajax callback that returns the form element.
708
*/
709
function _form_test_tableselect_ajax_callback($form, &$form_state) {
710
  return $form['tableselect'];
711
}
712

    
713
/**
714
 * A multistep form for testing the form storage.
715
 *
716
 * It uses two steps for editing a virtual "thing". Any changes to it are saved
717
 * in the form storage and have to be present during any step. By setting the
718
 * request parameter "cache" the form can be tested with caching enabled, as
719
 * it would be the case, if the form would contain some #ajax callbacks.
720
 *
721
 * @see form_test_storage_form_submit()
722
 */
723
function form_test_storage_form($form, &$form_state) {
724
  if ($form_state['rebuild']) {
725
    $form_state['input'] = array();
726
  }
727
  // Initialize
728
  if (empty($form_state['storage'])) {
729
    if (empty($form_state['input'])) {
730
      $_SESSION['constructions'] = 0;
731
    }
732
    // Put the initial thing into the storage
733
    $form_state['storage'] = array(
734
      'thing' => array(
735
        'title' => 'none',
736
        'value' => '',
737
      ),
738
    );
739
  }
740
  // Count how often the form is constructed.
741
  $_SESSION['constructions']++;
742
  drupal_set_message("Form constructions: " . $_SESSION['constructions']);
743

    
744
  $form['title'] = array(
745
    '#type' => 'textfield',
746
    '#title' => 'Title',
747
    '#default_value' => $form_state['storage']['thing']['title'],
748
    '#required' => TRUE,
749
  );
750
  $form['value'] = array(
751
    '#type' => 'textfield',
752
    '#title' => 'Value',
753
    '#default_value' => $form_state['storage']['thing']['value'],
754
    '#element_validate' => array('form_test_storage_element_validate_value_cached'),
755
  );
756
  $form['continue_button'] = array(
757
    '#type' => 'button',
758
    '#value' => 'Reset',
759
    // Rebuilds the form without keeping the values.
760
  );
761
  $form['continue_submit'] = array(
762
    '#type' => 'submit',
763
    '#value' => 'Continue submit',
764
    '#submit' => array('form_storage_test_form_continue_submit'),
765
  );
766
  $form['submit'] = array(
767
    '#type' => 'submit',
768
    '#value' => 'Save',
769
  );
770

    
771
  if (isset($_REQUEST['cache'])) {
772
    // Manually activate caching, so we can test that the storage keeps working
773
    // when it's enabled.
774
    $form_state['cache'] = TRUE;
775
  }
776

    
777
  if (isset($_REQUEST['immutable'])) {
778
    $form_state['build_info']['immutable'] = TRUE;
779
  }
780

    
781
  return $form;
782
}
783

    
784
/**
785
 * Emulate legacy AHAH-style ajax callback.
786
 *
787
 * Drupal 6 AHAH callbacks used to operate directly on forms retrieved using
788
 * form_get_cache and stored using form_set_cache after manipulation. This
789
 * callback helps testing whether form_set_cache prevents resaving of immutable
790
 * forms.
791
 */
792
function form_test_storage_legacy_handler($form_build_id) {
793
  $form_state = array();
794
  $form = form_get_cache($form_build_id, $form_state);
795

    
796
  drupal_json_output(array(
797
    'form' => $form,
798
    'form_state' => $form_state,
799
  ));
800

    
801
  $form['#poisoned'] = TRUE;
802
  $form_state['poisoned'] = TRUE;
803

    
804
  form_set_cache($form_build_id, $form, $form_state);
805
}
806

    
807
/**
808
 * Form element validation handler for 'value' element in form_test_storage_form().
809
 *
810
 * Tests updating of cached form storage during validation.
811
 */
812
function form_test_storage_element_validate_value_cached($element, &$form_state) {
813
  // If caching is enabled and we receive a certain value, change the storage.
814
  // This presumes that another submitted form value triggers a validation error
815
  // elsewhere in the form. Form API should still update the cached form storage
816
  // though.
817
  if (isset($_REQUEST['cache']) && $form_state['values']['value'] == 'change_title') {
818
    $form_state['storage']['thing']['changed'] = TRUE;
819
  }
820
}
821

    
822
/**
823
 * Form submit handler to continue multi-step form.
824
 */
825
function form_storage_test_form_continue_submit($form, &$form_state) {
826
  $form_state['storage']['thing']['title'] = $form_state['values']['title'];
827
  $form_state['storage']['thing']['value'] = $form_state['values']['value'];
828
  $form_state['rebuild'] = TRUE;
829
}
830

    
831
/**
832
 * Form submit handler to finish multi-step form.
833
 */
834
function form_test_storage_form_submit($form, &$form_state) {
835
  drupal_set_message("Title: " . check_plain($form_state['values']['title']));
836
  drupal_set_message("Form constructions: " . $_SESSION['constructions']);
837
  if (isset($form_state['storage']['thing']['changed'])) {
838
    drupal_set_message("The thing has been changed.");
839
  }
840
  $form_state['redirect'] = 'node';
841
}
842

    
843
/**
844
 * A simple form for testing form storage when page caching is enabled.
845
 */
846
function form_test_storage_page_cache_form($form, &$form_state) {
847
  $form['title'] = array(
848
    '#type' => 'textfield',
849
    '#title' => 'Title',
850
    '#required' => TRUE,
851
  );
852

    
853
  $form['test_build_id_old'] = array(
854
    '#type' => 'item',
855
    '#title' => 'Old build id',
856
    '#markup' => 'No old build id',
857
  );
858

    
859
  $form['submit'] = array(
860
    '#type' => 'submit',
861
    '#value' => 'Save',
862
  );
863

    
864
  $form['rebuild'] = array(
865
    '#type' => 'submit',
866
    '#value' => 'Rebuild',
867
    '#submit' => array('form_test_storage_page_cache_rebuild'),
868
  );
869

    
870
  $form['#after_build'] = array('form_test_storage_page_cache_old_build_id');
871
  $form_state['cache'] = TRUE;
872

    
873
  return $form;
874
}
875

    
876
/**
877
 * Form element #after_build callback: output the old form build-id.
878
 */
879
function form_test_storage_page_cache_old_build_id($form) {
880
  if (isset($form['#build_id_old'])) {
881
    $form['test_build_id_old']['#markup'] = check_plain($form['#build_id_old']);
882
  }
883
  return $form;
884
}
885

    
886
/**
887
 * Form submit callback: Rebuild the form and continue.
888
 */
889
function form_test_storage_page_cache_rebuild($form, &$form_state) {
890
  $form_state['rebuild'] = TRUE;
891
}
892

    
893
/**
894
 * A form for testing form labels and required marks.
895
 */
896
function form_label_test_form() {
897
  $form['form_checkboxes_test'] = array(
898
    '#type' => 'checkboxes',
899
    '#title' => t('Checkboxes test'),
900
    '#options' => array(
901
      'first-checkbox' => t('First checkbox'),
902
      'second-checkbox' => t('Second checkbox'),
903
      'third-checkbox' => t('Third checkbox'),
904
      '0' => t('0'),
905
    ),
906
  );
907
  $form['form_radios_test'] = array(
908
    '#type' => 'radios',
909
    '#title' => t('Radios test'),
910
    '#options' => array(
911
      'first-radio' => t('First radio'),
912
      'second-radio' => t('Second radio'),
913
      'third-radio' => t('Third radio'),
914
      '0' => t('0'),
915
    ),
916
    // Test #field_prefix and #field_suffix placement.
917
    '#field_prefix' => '<span id="form-test-radios-field-prefix">' . t('Radios #field_prefix element') . '</span>',
918
    '#field_suffix' => '<span id="form-test-radios-field-suffix">' . t('Radios #field_suffix element') . '</span>',
919
  );
920
  $form['form_checkbox_test'] = array(
921
    '#type' => 'checkbox',
922
    '#title' => t('Checkbox test'),
923
  );
924
  $form['form_textfield_test_title_and_required'] = array(
925
    '#type' => 'textfield',
926
    '#title' => t('Textfield test for required with title'),
927
    '#required' => TRUE,
928
  );
929
  $form['form_textfield_test_no_title_required'] = array(
930
    '#type' => 'textfield',
931
    // We use an empty title, since not setting #title suppresses the label
932
    // and required marker.
933
    '#title' => '',
934
    '#required' => TRUE,
935
  );
936
  $form['form_textfield_test_title'] = array(
937
    '#type' => 'textfield',
938
    '#title' => t('Textfield test for title only'),
939
    // Not required.
940
    // Test #prefix and #suffix placement.
941
    '#prefix' => '<div id="form-test-textfield-title-prefix">' . t('Textfield #prefix element') . '</div>',
942
    '#suffix' => '<div id="form-test-textfield-title-suffix">' . t('Textfield #suffix element') . '</div>',
943
  );
944
  $form['form_textfield_test_title_after'] = array(
945
    '#type' => 'textfield',
946
    '#title' => t('Textfield test for title after element'),
947
    '#title_display' => 'after',
948
  );
949
  $form['form_textfield_test_title_invisible'] = array(
950
    '#type' => 'textfield',
951
    '#title' => t('Textfield test for invisible title'),
952
    '#title_display' => 'invisible',
953
  );
954
  // Textfield test for title set not to display.
955
  $form['form_textfield_test_title_no_show'] = array(
956
    '#type' => 'textfield',
957
  );
958
  // Checkboxes & radios with title as attribute.
959
  $form['form_checkboxes_title_attribute'] = array(
960
    '#type' => 'checkboxes',
961
    '#title' => 'Checkboxes test',
962
    '#options' => array(
963
      'first-checkbox' => 'First checkbox',
964
      'second-checkbox' => 'Second checkbox',
965
    ),
966
    '#title_display' => 'attribute',
967
    '#required' => TRUE,
968
  );
969
  $form['form_radios_title_attribute'] = array(
970
    '#type' => 'radios',
971
    '#title' => 'Radios test',
972
    '#options' => array(
973
      'first-radio' => 'First radio',
974
      'second-radio' => 'Second radio',
975
    ),
976
    '#title_display' => 'attribute',
977
    '#required' => TRUE,
978
  );
979

    
980
  return $form;
981
}
982

    
983
/**
984
 * Menu callback; Invokes a form builder function with a wrapper callback.
985
 */
986
function form_test_wrapper_callback($form_id) {
987
  $form_state = array(
988
    'build_info' => array('args' => array()),
989
    'wrapper_callback' => 'form_test_wrapper_callback_wrapper',
990
  );
991
  return drupal_build_form($form_id, $form_state);
992
}
993

    
994
/**
995
 * Form wrapper for form_test_wrapper_callback_form().
996
 */
997
function form_test_wrapper_callback_wrapper($form, &$form_state) {
998
  $form['wrapper'] = array('#markup' => 'Form wrapper callback element output.');
999
  return $form;
1000
}
1001

    
1002
/**
1003
 * Form builder for form wrapper callback test.
1004
 */
1005
function form_test_wrapper_callback_form($form, &$form_state) {
1006
  $form['builder'] = array('#markup' => 'Form builder element output.');
1007
  return $form;
1008
}
1009

    
1010
/**
1011
 * Form builder for form_state_values_clean() test.
1012
 */
1013
function form_test_form_state_values_clean_form($form, &$form_state) {
1014
  // Build an example form containing multiple submit and button elements; not
1015
  // only on the top-level.
1016
  $form = array('#tree' => TRUE);
1017
  $form['foo'] = array('#type' => 'submit', '#value' => t('Submit'));
1018
  $form['bar'] = array('#type' => 'submit', '#value' => t('Submit'));
1019
  $form['beer'] = array('#type' => 'value', '#value' => 1000);
1020
  $form['baz']['foo'] = array('#type' => 'button', '#value' => t('Submit'));
1021
  $form['baz']['baz'] = array('#type' => 'submit', '#value' => t('Submit'));
1022
  $form['baz']['beer'] = array('#type' => 'value', '#value' => 2000);
1023
  return $form;
1024
}
1025

    
1026
/**
1027
 * Form submit handler for form_state_values_clean() test form.
1028
 */
1029
function form_test_form_state_values_clean_form_submit($form, &$form_state) {
1030
  form_state_values_clean($form_state);
1031
  drupal_json_output($form_state['values']);
1032
  exit;
1033
}
1034

    
1035
/**
1036
 * Form constructor for the form_state_values_clean() test.
1037
 */
1038
function form_test_form_state_values_clean_advanced_form($form, &$form_state) {
1039
  // Build an example form containing a managed file and a submit form element.
1040
  $form['image'] = array(
1041
    '#type' => 'managed_file',
1042
    '#title' => t('Image'),
1043
    '#upload_location' => 'public://',
1044
    '#default_value' => 0,
1045
  );
1046
  $form['submit'] = array(
1047
    '#type' => 'submit',
1048
    '#value' => t('Submit'),
1049
  );
1050
  return $form;
1051
}
1052

    
1053
/**
1054
 * Form submission handler for form_test_form_state_values_clean_advanced_form().
1055
 */
1056
function form_test_form_state_values_clean_advanced_form_submit($form, &$form_state) {
1057
  form_state_values_clean($form_state);
1058
  print t('You WIN!');
1059
  exit;
1060
}
1061

    
1062
/**
1063
 * Build a form to test a checkbox.
1064
 */
1065
function _form_test_checkbox($form, &$form_state) {
1066
  // A required checkbox.
1067
  $form['required_checkbox'] = array(
1068
    '#type' => 'checkbox',
1069
    '#required' => TRUE,
1070
    '#title' => 'required_checkbox',
1071
  );
1072

    
1073
  // A disabled checkbox should get its default value back.
1074
  $form['disabled_checkbox_on'] = array(
1075
    '#type' => 'checkbox',
1076
    '#disabled' => TRUE,
1077
    '#return_value' => 'disabled_checkbox_on',
1078
    '#default_value' => 'disabled_checkbox_on',
1079
    '#title' => 'disabled_checkbox_on',
1080
  );
1081
  $form['disabled_checkbox_off'] = array(
1082
    '#type' => 'checkbox',
1083
    '#disabled' => TRUE,
1084
    '#return_value' => 'disabled_checkbox_off',
1085
    '#default_value' => NULL,
1086
    '#title' => 'disabled_checkbox_off',
1087
  );
1088

    
1089
  // A checkbox is active when #default_value == #return_value.
1090
  $form['checkbox_on'] = array(
1091
    '#type' => 'checkbox',
1092
    '#return_value' => 'checkbox_on',
1093
    '#default_value' => 'checkbox_on',
1094
    '#title' => 'checkbox_on',
1095
  );
1096

    
1097
  // But inactive in any other case.
1098
  $form['checkbox_off'] = array(
1099
    '#type' => 'checkbox',
1100
    '#return_value' => 'checkbox_off',
1101
    '#default_value' => 'checkbox_on',
1102
    '#title' => 'checkbox_off',
1103
  );
1104

    
1105
  // Checkboxes with a #return_value of '0' are supported.
1106
  $form['zero_checkbox_on'] = array(
1107
    '#type' => 'checkbox',
1108
    '#return_value' => '0',
1109
    '#default_value' => '0',
1110
    '#title' => 'zero_checkbox_on',
1111
  );
1112

    
1113
  // In that case, passing a #default_value != '0' means that the checkbox is off.
1114
  $form['zero_checkbox_off'] = array(
1115
    '#type' => 'checkbox',
1116
    '#return_value' => '0',
1117
    '#default_value' => '1',
1118
    '#title' => 'zero_checkbox_off',
1119
  );
1120

    
1121
  $form['submit'] = array(
1122
    '#type' => 'submit',
1123
    '#value' => t('Submit')
1124
  );
1125

    
1126
  return $form;
1127
}
1128

    
1129
/**
1130
 * Return the form values via JSON.
1131
 */
1132
function _form_test_checkbox_submit($form, &$form_state) {
1133
  drupal_json_output($form_state['values']);
1134
  exit();
1135
}
1136

    
1137
/**
1138
 * Builds a form to test #type 'select' validation.
1139
 */
1140
function form_test_select($form, &$form_state) {
1141
  $base = array(
1142
    '#type' => 'select',
1143
    '#options' => drupal_map_assoc(array('one', 'two', 'three')),
1144
  );
1145

    
1146
  $form['select'] = $base + array(
1147
    '#title' => '#default_value one',
1148
    '#default_value' => 'one',
1149
  );
1150
  $form['select_required'] = $base + array(
1151
    '#title' => '#default_value one, #required',
1152
    '#required' => TRUE,
1153
    '#default_value' => 'one',
1154
  );
1155
  $form['select_optional'] = $base + array(
1156
    '#title' => '#default_value one, not #required',
1157
    '#required' => FALSE,
1158
    '#default_value' => 'one',
1159
  );
1160
  $form['empty_value'] = $base + array(
1161
    '#title' => '#default_value one, #required, #empty_value 0',
1162
    '#required' => TRUE,
1163
    '#default_value' => 'one',
1164
    '#empty_value' => 0,
1165
  );
1166
  $form['empty_value_one'] = $base + array(
1167
    '#title' => '#default_value = #empty_value, #required',
1168
    '#required' => TRUE,
1169
    '#default_value' => 'one',
1170
    '#empty_value' => 'one',
1171
  );
1172

    
1173
  $form['no_default'] = $base + array(
1174
    '#title' => 'No #default_value, #required',
1175
    '#required' => TRUE,
1176
  );
1177
  $form['no_default_optional'] = $base + array(
1178
    '#title' => 'No #default_value, not #required',
1179
    '#required' => FALSE,
1180
    '#description' => 'Should result in "one", because it is not required and there is no #empty_value requested, so default browser behavior of preselecting first option is in effect.',
1181
  );
1182
  $form['no_default_optional_empty_value'] = $base + array(
1183
    '#title' => 'No #default_value, not #required, #empty_value empty string',
1184
    '#empty_value' => '',
1185
    '#required' => FALSE,
1186
    '#description' => 'Should result in an empty string (due to #empty_value), because it is optional.',
1187
  );
1188

    
1189
  $form['no_default_empty_option'] = $base + array(
1190
    '#title' => 'No #default_value, #required, #empty_option',
1191
    '#required' => TRUE,
1192
    '#empty_option' => '- Choose -',
1193
  );
1194
  $form['no_default_empty_option_optional'] = $base + array(
1195
    '#title' => 'No #default_value, not #required, #empty_option',
1196
    '#empty_option' => '- Dismiss -',
1197
    '#description' => 'Should result in an empty string (default of #empty_value), because it is optional.',
1198
  );
1199

    
1200
  $form['no_default_empty_value'] = $base + array(
1201
    '#title' => 'No #default_value, #required, #empty_value 0',
1202
    '#required' => TRUE,
1203
    '#empty_value' => 0,
1204
    '#description' => 'Should never result in 0.',
1205
  );
1206
  $form['no_default_empty_value_one'] = $base + array(
1207
    '#title' => 'No #default_value, #required, #empty_value one',
1208
    '#required' => TRUE,
1209
    '#empty_value' => 'one',
1210
    '#description' => 'A mistakenly assigned #empty_value contained in #options should not be valid.',
1211
  );
1212
  $form['no_default_empty_value_optional'] = $base + array(
1213
    '#title' => 'No #default_value, not #required, #empty_value 0',
1214
    '#required' => FALSE,
1215
    '#empty_value' => 0,
1216
    '#description' => 'Should result in 0, because it is optional.',
1217
  );
1218

    
1219
  $form['multiple'] = $base + array(
1220
    '#title' => '#multiple, #default_value two',
1221
    '#default_value' => array('two'),
1222
    '#multiple' => TRUE,
1223
  );
1224
  $form['multiple_no_default'] = $base + array(
1225
    '#title' => '#multiple, no #default_value',
1226
    '#multiple' => TRUE,
1227
  );
1228
  $form['multiple_no_default_required'] = $base + array(
1229
    '#title' => '#multiple, #required, no #default_value',
1230
    '#required' => TRUE,
1231
    '#multiple' => TRUE,
1232
  );
1233

    
1234
  $form['submit'] = array('#type' => 'submit', '#value' => 'Submit');
1235
  return $form;
1236
}
1237

    
1238
/**
1239
 * Form submit handler for form_test_select().
1240
 */
1241
function form_test_select_submit($form, &$form_state) {
1242
  drupal_json_output($form_state['values']);
1243
  exit();
1244
}
1245

    
1246
/**
1247
 * Form constructor to test expansion of #type checkboxes and radios.
1248
 */
1249
function form_test_checkboxes_radios($form, &$form_state, $customize = FALSE) {
1250
  $form['#submit'] = array('_form_test_submit_values_json');
1251

    
1252
  // Expand #type checkboxes, setting custom element properties for some but not
1253
  // all options.
1254
  $form['checkboxes'] = array(
1255
    '#type' => 'checkboxes',
1256
    '#title' => 'Checkboxes',
1257
    '#options' => array(
1258
      0 => 'Zero',
1259
      'foo' => 'Foo',
1260
      1 => 'One',
1261
      'bar' => 'Bar',
1262
      '>' => 'Special Char',
1263
    ),
1264
  );
1265
  if ($customize) {
1266
    $form['checkboxes'] += array(
1267
      'foo' => array(
1268
        '#description' => 'Enable to foo.',
1269
      ),
1270
      1 => array(
1271
        '#weight' => 10,
1272
      ),
1273
    );
1274
  }
1275

    
1276
  // Expand #type radios, setting custom element properties for some but not
1277
  // all options.
1278
  $form['radios'] = array(
1279
    '#type' => 'radios',
1280
    '#title' => 'Radios',
1281
    '#options' => array(
1282
      0 => 'Zero',
1283
      'foo' => 'Foo',
1284
      1 => 'One',
1285
      'bar' => 'Bar',
1286
      '>' => 'Special Char',
1287
    ),
1288
  );
1289
  if ($customize) {
1290
    $form['radios'] += array(
1291
      'foo' => array(
1292
        '#description' => 'Enable to foo.',
1293
      ),
1294
      1 => array(
1295
        '#weight' => 10,
1296
      ),
1297
    );
1298
  }
1299

    
1300
  $form['submit'] = array('#type' => 'submit', '#value' => 'Submit');
1301

    
1302
  return $form;
1303
}
1304

    
1305
/**
1306
 * Build a form to test disabled elements.
1307
 */
1308
function _form_test_disabled_elements($form, &$form_state) {
1309
  // Elements that take a simple default value.
1310
  foreach (array('textfield', 'textarea', 'hidden') as $type) {
1311
    $form[$type] = array(
1312
      '#type' => $type,
1313
      '#title' => $type,
1314
      '#default_value' => $type,
1315
      '#test_hijack_value' => 'HIJACK',
1316
      '#disabled' => TRUE,
1317
    );
1318
  }
1319

    
1320
  // Multiple values option elements.
1321
  foreach (array('checkboxes', 'select') as $type) {
1322
    $form[$type . '_multiple'] = array(
1323
      '#type' => $type,
1324
      '#title' => $type . ' (multiple)',
1325
      '#options' => array(
1326
        'test_1' => 'Test 1',
1327
        'test_2' => 'Test 2',
1328
      ),
1329
      '#multiple' => TRUE,
1330
      '#default_value' => array('test_2' => 'test_2'),
1331
      // The keys of #test_hijack_value need to match the #name of the control.
1332
      // @see FormsTestCase::testDisabledElements()
1333
      '#test_hijack_value' => $type == 'select' ? array('' => 'test_1') : array('test_1' => 'test_1'),
1334
      '#disabled' => TRUE,
1335
    );
1336
  }
1337

    
1338
  // Single values option elements.
1339
  foreach (array('radios', 'select') as $type) {
1340
    $form[$type . '_single'] = array(
1341
      '#type' => $type,
1342
      '#title' => $type . ' (single)',
1343
      '#options' => array(
1344
        'test_1' => 'Test 1',
1345
        'test_2' => 'Test 2',
1346
      ),
1347
      '#multiple' => FALSE,
1348
      '#default_value' => 'test_2',
1349
      '#test_hijack_value' => 'test_1',
1350
      '#disabled' => TRUE,
1351
    );
1352
  }
1353

    
1354
  // Checkbox and radio.
1355
  foreach (array('checkbox', 'radio') as $type) {
1356
    $form[$type . '_unchecked'] = array(
1357
      '#type' => $type,
1358
      '#title' => $type . ' (unchecked)',
1359
      '#return_value' => 1,
1360
      '#default_value' => 0,
1361
      '#test_hijack_value' => 1,
1362
      '#disabled' => TRUE,
1363
    );
1364
    $form[$type . '_checked'] = array(
1365
      '#type' => $type,
1366
      '#title' => $type . ' (checked)',
1367
      '#return_value' => 1,
1368
      '#default_value' => 1,
1369
      '#test_hijack_value' => NULL,
1370
      '#disabled' => TRUE,
1371
    );
1372
  }
1373

    
1374
  // Weight.
1375
  $form['weight'] = array(
1376
    '#type' => 'weight',
1377
    '#title' => 'weight',
1378
    '#default_value' => 10,
1379
    '#test_hijack_value' => 5,
1380
    '#disabled' => TRUE,
1381
  );
1382

    
1383
  // Date.
1384
  $form['date'] = array(
1385
    '#type' => 'date',
1386
    '#title' => 'date',
1387
    '#disabled' => TRUE,
1388
    '#default_value' => array(
1389
      'day' => 19,
1390
      'month' => 11,
1391
      'year' => 1978,
1392
    ),
1393
    '#test_hijack_value' => array(
1394
      'day' => 20,
1395
      'month' => 12,
1396
      'year' => 1979,
1397
    ),
1398
  );
1399

    
1400
  // The #disabled state should propagate to children.
1401
  $form['disabled_container'] = array(
1402
    '#disabled' => TRUE,
1403
  );
1404
  foreach (array('textfield', 'textarea', 'hidden') as $type) {
1405
    $form['disabled_container']['disabled_container_' . $type] = array(
1406
      '#type' => $type,
1407
      '#title' => $type,
1408
      '#default_value' => $type,
1409
      '#test_hijack_value' => 'HIJACK',
1410
    );
1411
  }
1412

    
1413
  // Text format.
1414
  $form['text_format'] = array(
1415
    '#type' => 'text_format',
1416
    '#title' => 'Text format',
1417
    '#disabled' => TRUE,
1418
    '#default_value' => 'Text value',
1419
    '#format' => 'plain_text',
1420
    '#expected_value' => array(
1421
      'value' => 'Text value',
1422
      'format' => 'plain_text',
1423
    ),
1424
    '#test_hijack_value' => array(
1425
      'value' => 'HIJACK',
1426
      'format' => 'filtered_html',
1427
    ),
1428
  );
1429

    
1430
  // Password fields.
1431
  $form['password'] = array(
1432
    '#type' => 'password',
1433
    '#title' => 'Password',
1434
    '#disabled' => TRUE,
1435
  );
1436
  $form['password_confirm'] = array(
1437
    '#type' => 'password_confirm',
1438
    '#title' => 'Password confirm',
1439
    '#disabled' => TRUE,
1440
  );
1441

    
1442
  // Files.
1443
  $form['file'] = array(
1444
    '#type' => 'file',
1445
    '#title' => 'File',
1446
    '#disabled' => TRUE,
1447
  );
1448
  $form['managed_file'] = array(
1449
    '#type' => 'managed_file',
1450
    '#title' => 'Managed file',
1451
    '#disabled' => TRUE,
1452
  );
1453

    
1454
  // Buttons.
1455
  $form['image_button'] = array(
1456
    '#type' => 'image_button',
1457
    '#value' => 'Image button',
1458
    '#disabled' => TRUE,
1459
  );
1460
  $form['button'] = array(
1461
    '#type' => 'button',
1462
    '#value' => 'Button',
1463
    '#disabled' => TRUE,
1464
  );
1465
  $form['submit_disabled'] = array(
1466
    '#type' => 'submit',
1467
    '#value' => 'Submit',
1468
    '#disabled' => TRUE,
1469
  );
1470

    
1471
  $form['submit'] = array(
1472
    '#type' => 'submit',
1473
    '#value' => t('Submit'),
1474
  );
1475

    
1476
  return $form;
1477
}
1478

    
1479
/**
1480
 * Return the form values via JSON.
1481
 */
1482
function _form_test_disabled_elements_submit($form, &$form_state) {
1483
  drupal_json_output($form_state['values']);
1484
  exit();
1485
}
1486

    
1487
/**
1488
 * Build a form to test input forgery of enabled elements.
1489
 */
1490
function _form_test_input_forgery($form, &$form_state) {
1491
  // For testing that a user can't submit a value not matching one of the
1492
  // allowed options.
1493
  $form['checkboxes'] = array(
1494
    '#type' => 'checkboxes',
1495
    '#options' => array(
1496
      'one' => 'One',
1497
      'two' => 'Two',
1498
    ),
1499
  );
1500

    
1501
  $form['submit'] = array(
1502
    '#type' => 'submit',
1503
    '#value' => t('Submit'),
1504
  );
1505
  return $form;
1506
}
1507

    
1508
/**
1509
 * Return the form values via JSON.
1510
 */
1511
function _form_test_input_forgery_submit($form, &$form_state) {
1512
  drupal_json_output($form_state['values']);
1513
  exit();
1514
}
1515

    
1516
/**
1517
 * Form builder for testing preservation of values during a rebuild.
1518
 */
1519
function form_test_form_rebuild_preserve_values_form($form, &$form_state) {
1520
  // Start the form with two checkboxes, to test different defaults, and a
1521
  // textfield, to test more than one element type.
1522
  $form = array(
1523
    'checkbox_1_default_off' => array(
1524
      '#type' => 'checkbox',
1525
      '#title' => t('This checkbox defaults to unchecked.'),
1526
      '#default_value' => FALSE,
1527
    ),
1528
    'checkbox_1_default_on' => array(
1529
      '#type' => 'checkbox',
1530
      '#title' => t('This checkbox defaults to checked.'),
1531
      '#default_value' => TRUE,
1532
    ),
1533
    'text_1' => array(
1534
      '#type' => 'textfield',
1535
      '#title' => t('This textfield has a non-empty default value.'),
1536
      '#default_value' => 'DEFAULT 1',
1537
    ),
1538
  );
1539
  // Provide an 'add more' button that rebuilds the form with an additional two
1540
  // checkboxes and a textfield. The test is to make sure that the rebuild
1541
  // triggered by this button preserves the user input values for the initial
1542
  // elements and initializes the new elements with the correct default values.
1543
  if (empty($form_state['storage']['add_more'])) {
1544
    $form['add_more'] = array(
1545
      '#type' => 'submit',
1546
      '#value' => 'Add more',
1547
      '#submit' => array('form_test_form_rebuild_preserve_values_form_add_more'),
1548
    );
1549
  }
1550
  else {
1551
    $form += array(
1552
      'checkbox_2_default_off' => array(
1553
        '#type' => 'checkbox',
1554
        '#title' => t('This checkbox defaults to unchecked.'),
1555
        '#default_value' => FALSE,
1556
      ),
1557
      'checkbox_2_default_on' => array(
1558
        '#type' => 'checkbox',
1559
        '#title' => t('This checkbox defaults to checked.'),
1560
        '#default_value' => TRUE,
1561
      ),
1562
      'text_2' => array(
1563
        '#type' => 'textfield',
1564
        '#title' => t('This textfield has a non-empty default value.'),
1565
        '#default_value' => 'DEFAULT 2',
1566
      ),
1567
    );
1568
  }
1569
  // A submit button that finishes the form workflow (does not rebuild).
1570
  $form['submit'] = array(
1571
    '#type' => 'submit',
1572
    '#value' => 'Submit',
1573
  );
1574
  return $form;
1575
}
1576

    
1577
/**
1578
 * Button submit handler for form_test_form_rebuild_preserve_values_form().
1579
 */
1580
function form_test_form_rebuild_preserve_values_form_add_more($form, &$form_state) {
1581
  // Rebuild, to test preservation of input values.
1582
  $form_state['storage']['add_more'] = TRUE;
1583
  $form_state['rebuild'] = TRUE;
1584
}
1585

    
1586
/**
1587
 * Form submit handler for form_test_form_rebuild_preserve_values_form().
1588
 */
1589
function form_test_form_rebuild_preserve_values_form_submit($form, &$form_state) {
1590
  // Finish the workflow. Do not rebuild.
1591
  drupal_set_message(t('Form values: %values', array('%values' => var_export($form_state['values'], TRUE))));
1592
}
1593

    
1594
/**
1595
 * Form constructor for testing form state persistence.
1596
 */
1597
function form_test_state_persist($form, &$form_state) {
1598
  $form['title'] = array(
1599
    '#type' => 'textfield',
1600
    '#title' => 'title',
1601
    '#default_value' => 'DEFAULT',
1602
    '#required' => TRUE,
1603
  );
1604
  $form_state['value'] = 'State persisted.';
1605

    
1606
  $form['submit'] = array(
1607
    '#type' => 'submit',
1608
    '#value' => t('Submit'),
1609
  );
1610
  return $form;
1611
}
1612

    
1613
/**
1614
 * Submit handler.
1615
 *
1616
 * @see form_test_state_persist()
1617
 */
1618
function form_test_state_persist_submit($form, &$form_state) {
1619
  drupal_set_message($form_state['value']);
1620
  $form_state['rebuild'] = TRUE;
1621
}
1622

    
1623
/**
1624
 * Implements hook_form_FORM_ID_alter().
1625
 *
1626
 * @see form_test_state_persist()
1627
 */
1628
function form_test_form_form_test_state_persist_alter(&$form, &$form_state) {
1629
  // Simulate a form alter implementation inserting form elements that enable
1630
  // caching of the form, e.g. elements having #ajax.
1631
  if (!empty($_REQUEST['cache'])) {
1632
    $form_state['cache'] = TRUE;
1633
  }
1634
}
1635

    
1636
/**
1637
 * Form builder to test programmatic form submissions.
1638
 */
1639
function form_test_programmatic_form($form, &$form_state) {
1640
  $form['textfield'] = array(
1641
    '#title' => 'Textfield',
1642
    '#type' => 'textfield',
1643
  );
1644

    
1645
  $form['checkboxes'] = array(
1646
    '#type' => 'checkboxes',
1647
    '#options' => array(
1648
      1 => 'First checkbox',
1649
      2 => 'Second checkbox',
1650
    ),
1651
    // Both checkboxes are selected by default so that we can test the ability
1652
    // of programmatic form submissions to uncheck them.
1653
    '#default_value' => array(1, 2),
1654
  );
1655

    
1656
  // This is used to test that programmatic form submissions can bypass #access
1657
  // restrictions.
1658
  $form['textfield_no_access'] = array(
1659
    '#type' => 'textfield',
1660
    '#title' => 'Textfield no access',
1661
    '#default_value' => 'default value',
1662
    '#access' => FALSE,
1663
  );
1664

    
1665
  $form['field_to_validate'] = array(
1666
    '#type' => 'radios',
1667
    '#title' => 'Field to validate (in the case of limited validation)',
1668
    '#description' => 'If the form is submitted by clicking the "Submit with limited validation" button, then validation can be limited based on the value of this radio button.',
1669
    '#options' => array(
1670
      'all' => 'Validate all fields',
1671
      'textfield' => 'Validate the "Textfield" field',
1672
      'field_to_validate' => 'Validate the "Field to validate" field',
1673
    ),
1674
    '#default_value' => 'all',
1675
  );
1676

    
1677
  // The main submit button for the form.
1678
  $form['submit'] = array(
1679
    '#type' => 'submit',
1680
    '#value' => 'Submit',
1681
  );
1682
  // A secondary submit button that allows validation to be limited based on
1683
  // the value of the above radio selector.
1684
  $form['submit_limit_validation'] = array(
1685
    '#type' => 'submit',
1686
    '#value' => 'Submit with limited validation',
1687
    // Use the same submit handler for this button as for the form itself.
1688
    // (This must be set explicitly or otherwise the form API will ignore the
1689
    // #limit_validation_errors property.)
1690
    '#submit' => array('form_test_programmatic_form_submit'),
1691
  );
1692
  if (!empty($form_state['input']['field_to_validate']) && $form_state['input']['field_to_validate'] != 'all') {
1693
    $form['submit_limit_validation']['#limit_validation_errors'] = array(
1694
      array($form_state['input']['field_to_validate']),
1695
    );
1696
  }
1697

    
1698
  return $form;
1699
}
1700

    
1701
/**
1702
 * Form validation handler for programmatic form submissions.
1703
 *
1704
 * To test that the validation handler is correctly executed, the field value is
1705
 * explicitly required here.
1706
 */
1707
function form_test_programmatic_form_validate($form, &$form_state) {
1708
  if (empty($form_state['values']['textfield'])) {
1709
    form_set_error('textfield', t('Textfield is required.'));
1710
  }
1711
}
1712

    
1713
/**
1714
 * Form submit handler for programmatic form submissions.
1715
 *
1716
 * To test that the submission handler is correctly executed, we store the
1717
 * submitted values in a place we can access from the caller context.
1718
 */
1719
function form_test_programmatic_form_submit($form, &$form_state) {
1720
  $form_state['storage']['programmatic_form_submit'] = $form_state['values'];
1721
}
1722

    
1723
/**
1724
 * Form builder to test button click detection.
1725
 */
1726
function form_test_clicked_button($form, &$form_state) {
1727
  // A single text field. In IE, when a form has only one non-button input field
1728
  // and the ENTER key is pressed while that field has focus, the form is
1729
  // submitted without any information identifying the button responsible for
1730
  // the submission. In other browsers, the form is submitted as though the
1731
  // first button were clicked.
1732
  $form['text'] = array(
1733
    '#title' => 'Text',
1734
    '#type' => 'textfield',
1735
  );
1736

    
1737
  // Loop through each path argument, addding buttons based on the information
1738
  // in the argument. For example, if the path is
1739
  // form-test/clicked-button/s/i/rb, then 3 buttons are added: a 'submit', an
1740
  // 'image_button', and a 'button' with #access=FALSE. This enables form.test
1741
  // to test a variety of combinations.
1742
  $i=0;
1743
  $args = array_slice(arg(), 2);
1744
  foreach ($args as $arg) {
1745
    $name = 'button' . ++$i;
1746
    // 's', 'b', or 'i' in the argument define the button type wanted.
1747
    if (strpos($arg, 's') !== FALSE) {
1748
      $type = 'submit';
1749
    }
1750
    elseif (strpos($arg, 'b') !== FALSE) {
1751
      $type = 'button';
1752
    }
1753
    elseif (strpos($arg, 'i') !== FALSE) {
1754
      $type = 'image_button';
1755
    }
1756
    else {
1757
      $type = NULL;
1758
    }
1759
    if (isset($type)) {
1760
      $form[$name] = array(
1761
        '#type' => $type,
1762
        '#name' => $name,
1763
      );
1764
      // Image buttons need a #src; the others need a #value.
1765
      if ($type == 'image_button') {
1766
        $form[$name]['#src'] = 'misc/druplicon.png';
1767
      }
1768
      else {
1769
        $form[$name]['#value'] = $name;
1770
      }
1771
      // 'r' for restricted, so we can test that button click detection code
1772
      // correctly takes #access security into account.
1773
      if (strpos($arg, 'r') !== FALSE) {
1774
        $form[$name]['#access'] = FALSE;
1775
      }
1776
    }
1777
  }
1778

    
1779
  return $form;
1780
}
1781

    
1782
/**
1783
 * Form validation handler for the form_test_clicked_button() form.
1784
 */
1785
function form_test_clicked_button_validate($form, &$form_state) {
1786
  if (isset($form_state['triggering_element'])) {
1787
    drupal_set_message(t('The clicked button is %name.', array('%name' => $form_state['triggering_element']['#name'])));
1788
  }
1789
  else {
1790
    drupal_set_message('There is no clicked button.');
1791
  }
1792
}
1793

    
1794
/**
1795
 * Form submit handler for the form_test_clicked_button() form.
1796
 */
1797
function form_test_clicked_button_submit($form, &$form_state) {
1798
  drupal_set_message('Submit handler for form_test_clicked_button executed.');
1799
}
1800

    
1801
/**
1802
 * Form builder to detect form redirect.
1803
 */
1804
function form_test_redirect($form, &$form_state) {
1805
  $form['redirection'] = array(
1806
    '#type' => 'checkbox',
1807
    '#title' => t('Use redirection'),
1808
  );
1809
  $form['destination'] = array(
1810
    '#type' => 'textfield',
1811
    '#title' => t('Redirect destination'),
1812
    '#states' => array(
1813
      'visible' => array(
1814
        ':input[name="redirection"]' => array('checked' => TRUE),
1815
      ),
1816
    ),
1817
  );
1818
  $form['submit'] = array(
1819
    '#type' => 'submit',
1820
    '#value' => t('Submit'),
1821
  );
1822

    
1823
  return $form;
1824
}
1825

    
1826
/**
1827
 * Form submit handler to test different redirect behaviours.
1828
 */
1829
function form_test_redirect_submit(&$form, &$form_state) {
1830
  if (!empty($form_state['values']['redirection'])) {
1831
    $form_state['redirect'] = !empty($form_state['values']['destination']) ? $form_state['values']['destination'] : NULL;
1832
  }
1833
  else {
1834
    $form_state['redirect'] = FALSE;
1835
  }
1836
}
1837

    
1838
/**
1839
 * Implements hook_form_FORM_ID_alter() for the registration form.
1840
 */
1841
function form_test_form_user_register_form_alter(&$form, &$form_state) {
1842
  $form['test_rebuild'] = array(
1843
    '#type' => 'submit',
1844
    '#value' => t('Rebuild'),
1845
    '#submit' => array('form_test_user_register_form_rebuild'),
1846
  );
1847
  // If requested, add the test field by attaching the node page form.
1848
  if (!empty($_REQUEST['field'])) {
1849
    $node = (object)array('type' => 'page');
1850
    field_attach_form('node', $node, $form, $form_state);
1851
  }
1852
}
1853

    
1854
/**
1855
 * Submit callback that just lets the form rebuild.
1856
 */
1857
function form_test_user_register_form_rebuild($form, &$form_state) {
1858
  drupal_set_message('Form rebuilt.');
1859
  $form_state['rebuild'] = TRUE;
1860
}
1861

    
1862
/**
1863
 * Menu callback that returns two instances of the node form.
1864
 */
1865
function form_test_two_instances() {
1866
  global $user;
1867
  $node1 = (object) array(
1868
    'uid' => $user->uid,
1869
    'name' => (isset($user->name) ? $user->name : ''),
1870
    'type' => 'page',
1871
    'language' => LANGUAGE_NONE,
1872
  );
1873
  $node2 = clone($node1);
1874
  $return['node_form_1'] = drupal_get_form('page_node_form', $node1);
1875
  $return['node_form_2'] = drupal_get_form('page_node_form', $node2);
1876
  return $return;
1877
}
1878

    
1879
/**
1880
 * Menu callback for testing custom form includes.
1881
 */
1882
function form_test_load_include_custom($form, &$form_state) {
1883
  $form['button'] = array(
1884
    '#type' => 'submit',
1885
    '#value' => t('Save'),
1886
    '#submit' => array('form_test_load_include_submit'),
1887
  );
1888
  // Specify the include file and enable form caching. That way the form is
1889
  // cached when it is submitted, but needs to find the specified submit handler
1890
  // in the include.
1891
  // Filename is a bit weird here: modules/simpletest/tests/form_test.file.inc
1892
  form_load_include($form_state, 'inc', 'form_test', 'form_test.file');
1893
  $form_state['cache'] = TRUE;
1894
  return $form;
1895
}
1896

    
1897
function form_test_checkbox_type_juggling($form, $form_state, $default_value, $return_value) {
1898
  $form['checkbox'] = array(
1899
    '#type' => 'checkbox',
1900
    '#return_value' => $return_value,
1901
    '#default_value' => $default_value,
1902
  );
1903
  return $form;
1904
}
1905

    
1906
function form_test_checkboxes_zero($form, &$form_state, $json = TRUE) {
1907
  $form['checkbox_off'] = array(
1908
    '#type' => 'checkboxes',
1909
    '#options' => array('foo', 'bar', 'baz'),
1910
  );
1911
  $form['checkbox_zero_default'] = array(
1912
    '#type' => 'checkboxes',
1913
    '#options' => array('foo', 'bar', 'baz'),
1914
    '#default_value' => array(0),
1915
  );
1916
  $form['checkbox_string_zero_default'] = array(
1917
    '#type' => 'checkboxes',
1918
    '#options' => array('foo', 'bar', 'baz'),
1919
    '#default_value' => array('0'),
1920
  );
1921
  $form['submit'] = array(
1922
    '#type' => 'submit',
1923
    '#value' => 'Save',
1924
  );
1925
  if ($json) {
1926
    $form['#submit'][] = '_form_test_checkbox_submit';
1927
  }
1928
  else {
1929
    $form['#submit'][] = '_form_test_checkboxes_zero_no_redirect';
1930
  }
1931
  return $form;
1932
}
1933

    
1934
function _form_test_checkboxes_zero_no_redirect($form, &$form_state) {
1935
  $form_state['redirect'] = FALSE;
1936
}
1937

    
1938
/**
1939
 * Menu callback returns two instances of the same form.
1940
 */
1941
function form_test_double_form() {
1942
  return array(
1943
    'form1' => drupal_get_form('form_test_html_id'),
1944
    'form2' => drupal_get_form('form_test_html_id'),
1945
  );
1946
}
1947

    
1948
/**
1949
 * Builds a simple form to test duplicate HTML IDs.
1950
 */
1951
function form_test_html_id($form, &$form_state) {
1952
  $form['name'] = array(
1953
    '#type' => 'textfield',
1954
    '#title' => 'name',
1955
    '#required' => TRUE,
1956
  );
1957
  $form['submit'] = array(
1958
    '#type' => 'submit',
1959
    '#value' => 'Save',
1960
  );
1961
  return $form;
1962
}