Projet

Général

Profil

Paste
Télécharger (57,6 ko) Statistiques
| Branche: | Révision:

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

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
    '#type' => 'tableselect',
593
    '#header' => $header,
594
    '#options' => $options,
595
    '#multiple' => FALSE,
596
    '#empty' => t('Empty text.'),
597
  );
598

    
599
  $form['submit'] = array(
600
    '#type' => 'submit',
601
    '#value' => t('Submit'),
602
  );
603

    
604
  return $form;
605
}
606

    
607
/**
608
 * Test the tableselect #multiple = TRUE functionality.
609
 */
610
function _form_test_tableselect_multiple_true_form($form, $form_state) {
611
  return _form_test_tableselect_form_builder($form, $form_state, array('#multiple' => TRUE));
612
}
613

    
614
/**
615
 * Process the tableselect #multiple = TRUE submitted values.
616
 */
617
function _form_test_tableselect_multiple_true_form_submit($form, &$form_state) {
618
  $selected = $form_state['values']['tableselect'];
619
  foreach ($selected as $key => $value) {
620
    drupal_set_message(t('Submitted: @key = @value', array('@key' => $key, '@value' => $value)));
621
  }
622
}
623

    
624
/**
625
 * Test the tableselect #multiple = FALSE functionality.
626
 */
627
function _form_test_tableselect_multiple_false_form($form, $form_state) {
628
  return _form_test_tableselect_form_builder($form, $form_state, array('#multiple' => FALSE));
629
}
630

    
631
/**
632
 * Process the tableselect #multiple = FALSE submitted values.
633
 */
634
function _form_test_tableselect_multiple_false_form_submit($form, &$form_state) {
635
  drupal_set_message(t('Submitted: @value', array('@value' => $form_state['values']['tableselect'])));
636
}
637

    
638
/**
639
 * Test functionality of the tableselect #empty property.
640
 */
641
function _form_test_tableselect_empty_form($form, $form_state) {
642
  return _form_test_tableselect_form_builder($form, $form_state, array('#options' => array()));
643
}
644

    
645
/**
646
 * Test functionality of the tableselect #js_select property.
647
 */
648
function _form_test_tableselect_js_select_form($form, $form_state, $action) {
649
  switch ($action) {
650
    case 'multiple-true-default':
651
      $options = array('#multiple' => TRUE);
652
      break;
653

    
654
    case 'multiple-false-default':
655
      $options = array('#multiple' => FALSE);
656
      break;
657

    
658
    case 'multiple-true-no-advanced-select':
659
      $options = array('#multiple' => TRUE, '#js_select' => FALSE);
660
      break;
661

    
662
    case 'multiple-false-advanced-select':
663
      $options = array('#multiple' => FALSE, '#js_select' => TRUE);
664
      break;
665
  }
666

    
667
  return _form_test_tableselect_form_builder($form, $form_state, $options);
668
}
669

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

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

    
731
  $form['title'] = array(
732
    '#type' => 'textfield',
733
    '#title' => 'Title',
734
    '#default_value' => $form_state['storage']['thing']['title'],
735
    '#required' => TRUE,
736
  );
737
  $form['value'] = array(
738
    '#type' => 'textfield',
739
    '#title' => 'Value',
740
    '#default_value' => $form_state['storage']['thing']['value'],
741
    '#element_validate' => array('form_test_storage_element_validate_value_cached'),
742
  );
743
  $form['continue_button'] = array(
744
    '#type' => 'button',
745
    '#value' => 'Reset',
746
    // Rebuilds the form without keeping the values.
747
  );
748
  $form['continue_submit'] = array(
749
    '#type' => 'submit',
750
    '#value' => 'Continue submit',
751
    '#submit' => array('form_storage_test_form_continue_submit'),
752
  );
753
  $form['submit'] = array(
754
    '#type' => 'submit',
755
    '#value' => 'Save',
756
  );
757

    
758
  if (isset($_REQUEST['cache'])) {
759
    // Manually activate caching, so we can test that the storage keeps working
760
    // when it's enabled.
761
    $form_state['cache'] = TRUE;
762
  }
763

    
764
  if (isset($_REQUEST['immutable'])) {
765
    $form_state['build_info']['immutable'] = TRUE;
766
  }
767

    
768
  return $form;
769
}
770

    
771
/**
772
 * Emulate legacy AHAH-style ajax callback.
773
 *
774
 * Drupal 6 AHAH callbacks used to operate directly on forms retrieved using
775
 * form_get_cache and stored using form_set_cache after manipulation. This
776
 * callback helps testing whether form_set_cache prevents resaving of immutable
777
 * forms.
778
 */
779
function form_test_storage_legacy_handler($form_build_id) {
780
  $form_state = array();
781
  $form = form_get_cache($form_build_id, $form_state);
782

    
783
  drupal_json_output(array(
784
    'form' => $form,
785
    'form_state' => $form_state,
786
  ));
787

    
788
  $form['#poisoned'] = TRUE;
789
  $form_state['poisoned'] = TRUE;
790

    
791
  form_set_cache($form_build_id, $form, $form_state);
792
}
793

    
794
/**
795
 * Form element validation handler for 'value' element in form_test_storage_form().
796
 *
797
 * Tests updating of cached form storage during validation.
798
 */
799
function form_test_storage_element_validate_value_cached($element, &$form_state) {
800
  // If caching is enabled and we receive a certain value, change the storage.
801
  // This presumes that another submitted form value triggers a validation error
802
  // elsewhere in the form. Form API should still update the cached form storage
803
  // though.
804
  if (isset($_REQUEST['cache']) && $form_state['values']['value'] == 'change_title') {
805
    $form_state['storage']['thing']['changed'] = TRUE;
806
  }
807
}
808

    
809
/**
810
 * Form submit handler to continue multi-step form.
811
 */
812
function form_storage_test_form_continue_submit($form, &$form_state) {
813
  $form_state['storage']['thing']['title'] = $form_state['values']['title'];
814
  $form_state['storage']['thing']['value'] = $form_state['values']['value'];
815
  $form_state['rebuild'] = TRUE;
816
}
817

    
818
/**
819
 * Form submit handler to finish multi-step form.
820
 */
821
function form_test_storage_form_submit($form, &$form_state) {
822
  drupal_set_message("Title: " . check_plain($form_state['values']['title']));
823
  drupal_set_message("Form constructions: " . $_SESSION['constructions']);
824
  if (isset($form_state['storage']['thing']['changed'])) {
825
    drupal_set_message("The thing has been changed.");
826
  }
827
  $form_state['redirect'] = 'node';
828
}
829

    
830
/**
831
 * A simple form for testing form storage when page caching is enabled.
832
 */
833
function form_test_storage_page_cache_form($form, &$form_state) {
834
  $form['title'] = array(
835
    '#type' => 'textfield',
836
    '#title' => 'Title',
837
    '#required' => TRUE,
838
  );
839

    
840
  $form['test_build_id_old'] = array(
841
    '#type' => 'item',
842
    '#title' => 'Old build id',
843
    '#markup' => 'No old build id',
844
  );
845

    
846
  $form['submit'] = array(
847
    '#type' => 'submit',
848
    '#value' => 'Save',
849
  );
850

    
851
  $form['rebuild'] = array(
852
    '#type' => 'submit',
853
    '#value' => 'Rebuild',
854
    '#submit' => array('form_test_storage_page_cache_rebuild'),
855
  );
856

    
857
  $form['#after_build'] = array('form_test_storage_page_cache_old_build_id');
858
  $form_state['cache'] = TRUE;
859

    
860
  return $form;
861
}
862

    
863
/**
864
 * Form element #after_build callback: output the old form build-id.
865
 */
866
function form_test_storage_page_cache_old_build_id($form) {
867
  if (isset($form['#build_id_old'])) {
868
    $form['test_build_id_old']['#markup'] = check_plain($form['#build_id_old']);
869
  }
870
  return $form;
871
}
872

    
873
/**
874
 * Form submit callback: Rebuild the form and continue.
875
 */
876
function form_test_storage_page_cache_rebuild($form, &$form_state) {
877
  $form_state['rebuild'] = TRUE;
878
}
879

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

    
967
  return $form;
968
}
969

    
970
/**
971
 * Menu callback; Invokes a form builder function with a wrapper callback.
972
 */
973
function form_test_wrapper_callback($form_id) {
974
  $form_state = array(
975
    'build_info' => array('args' => array()),
976
    'wrapper_callback' => 'form_test_wrapper_callback_wrapper',
977
  );
978
  return drupal_build_form($form_id, $form_state);
979
}
980

    
981
/**
982
 * Form wrapper for form_test_wrapper_callback_form().
983
 */
984
function form_test_wrapper_callback_wrapper($form, &$form_state) {
985
  $form['wrapper'] = array('#markup' => 'Form wrapper callback element output.');
986
  return $form;
987
}
988

    
989
/**
990
 * Form builder for form wrapper callback test.
991
 */
992
function form_test_wrapper_callback_form($form, &$form_state) {
993
  $form['builder'] = array('#markup' => 'Form builder element output.');
994
  return $form;
995
}
996

    
997
/**
998
 * Form builder for form_state_values_clean() test.
999
 */
1000
function form_test_form_state_values_clean_form($form, &$form_state) {
1001
  // Build an example form containing multiple submit and button elements; not
1002
  // only on the top-level.
1003
  $form = array('#tree' => TRUE);
1004
  $form['foo'] = array('#type' => 'submit', '#value' => t('Submit'));
1005
  $form['bar'] = array('#type' => 'submit', '#value' => t('Submit'));
1006
  $form['beer'] = array('#type' => 'value', '#value' => 1000);
1007
  $form['baz']['foo'] = array('#type' => 'button', '#value' => t('Submit'));
1008
  $form['baz']['baz'] = array('#type' => 'submit', '#value' => t('Submit'));
1009
  $form['baz']['beer'] = array('#type' => 'value', '#value' => 2000);
1010
  return $form;
1011
}
1012

    
1013
/**
1014
 * Form submit handler for form_state_values_clean() test form.
1015
 */
1016
function form_test_form_state_values_clean_form_submit($form, &$form_state) {
1017
  form_state_values_clean($form_state);
1018
  drupal_json_output($form_state['values']);
1019
  exit;
1020
}
1021

    
1022
/**
1023
 * Form constructor for the form_state_values_clean() test.
1024
 */
1025
function form_test_form_state_values_clean_advanced_form($form, &$form_state) {
1026
  // Build an example form containing a managed file and a submit form element.
1027
  $form['image'] = array(
1028
    '#type' => 'managed_file',
1029
    '#title' => t('Image'),
1030
    '#upload_location' => 'public://',
1031
    '#default_value' => 0,
1032
  );
1033
  $form['submit'] = array(
1034
    '#type' => 'submit',
1035
    '#value' => t('Submit'),
1036
  );
1037
  return $form;
1038
}
1039

    
1040
/**
1041
 * Form submission handler for form_test_form_state_values_clean_advanced_form().
1042
 */
1043
function form_test_form_state_values_clean_advanced_form_submit($form, &$form_state) {
1044
  form_state_values_clean($form_state);
1045
  print t('You WIN!');
1046
  exit;
1047
}
1048

    
1049
/**
1050
 * Build a form to test a checkbox.
1051
 */
1052
function _form_test_checkbox($form, &$form_state) {
1053
  // A required checkbox.
1054
  $form['required_checkbox'] = array(
1055
    '#type' => 'checkbox',
1056
    '#required' => TRUE,
1057
    '#title' => 'required_checkbox',
1058
  );
1059

    
1060
  // A disabled checkbox should get its default value back.
1061
  $form['disabled_checkbox_on'] = array(
1062
    '#type' => 'checkbox',
1063
    '#disabled' => TRUE,
1064
    '#return_value' => 'disabled_checkbox_on',
1065
    '#default_value' => 'disabled_checkbox_on',
1066
    '#title' => 'disabled_checkbox_on',
1067
  );
1068
  $form['disabled_checkbox_off'] = array(
1069
    '#type' => 'checkbox',
1070
    '#disabled' => TRUE,
1071
    '#return_value' => 'disabled_checkbox_off',
1072
    '#default_value' => NULL,
1073
    '#title' => 'disabled_checkbox_off',
1074
  );
1075

    
1076
  // A checkbox is active when #default_value == #return_value.
1077
  $form['checkbox_on'] = array(
1078
    '#type' => 'checkbox',
1079
    '#return_value' => 'checkbox_on',
1080
    '#default_value' => 'checkbox_on',
1081
    '#title' => 'checkbox_on',
1082
  );
1083

    
1084
  // But inactive in any other case.
1085
  $form['checkbox_off'] = array(
1086
    '#type' => 'checkbox',
1087
    '#return_value' => 'checkbox_off',
1088
    '#default_value' => 'checkbox_on',
1089
    '#title' => 'checkbox_off',
1090
  );
1091

    
1092
  // Checkboxes with a #return_value of '0' are supported.
1093
  $form['zero_checkbox_on'] = array(
1094
    '#type' => 'checkbox',
1095
    '#return_value' => '0',
1096
    '#default_value' => '0',
1097
    '#title' => 'zero_checkbox_on',
1098
  );
1099

    
1100
  // In that case, passing a #default_value != '0' means that the checkbox is off.
1101
  $form['zero_checkbox_off'] = array(
1102
    '#type' => 'checkbox',
1103
    '#return_value' => '0',
1104
    '#default_value' => '1',
1105
    '#title' => 'zero_checkbox_off',
1106
  );
1107

    
1108
  $form['submit'] = array(
1109
    '#type' => 'submit',
1110
    '#value' => t('Submit')
1111
  );
1112

    
1113
  return $form;
1114
}
1115

    
1116
/**
1117
 * Return the form values via JSON.
1118
 */
1119
function _form_test_checkbox_submit($form, &$form_state) {
1120
  drupal_json_output($form_state['values']);
1121
  exit();
1122
}
1123

    
1124
/**
1125
 * Builds a form to test #type 'select' validation.
1126
 */
1127
function form_test_select($form, &$form_state) {
1128
  $base = array(
1129
    '#type' => 'select',
1130
    '#options' => drupal_map_assoc(array('one', 'two', 'three')),
1131
  );
1132

    
1133
  $form['select'] = $base + array(
1134
    '#title' => '#default_value one',
1135
    '#default_value' => 'one',
1136
  );
1137
  $form['select_required'] = $base + array(
1138
    '#title' => '#default_value one, #required',
1139
    '#required' => TRUE,
1140
    '#default_value' => 'one',
1141
  );
1142
  $form['select_optional'] = $base + array(
1143
    '#title' => '#default_value one, not #required',
1144
    '#required' => FALSE,
1145
    '#default_value' => 'one',
1146
  );
1147
  $form['empty_value'] = $base + array(
1148
    '#title' => '#default_value one, #required, #empty_value 0',
1149
    '#required' => TRUE,
1150
    '#default_value' => 'one',
1151
    '#empty_value' => 0,
1152
  );
1153
  $form['empty_value_one'] = $base + array(
1154
    '#title' => '#default_value = #empty_value, #required',
1155
    '#required' => TRUE,
1156
    '#default_value' => 'one',
1157
    '#empty_value' => 'one',
1158
  );
1159

    
1160
  $form['no_default'] = $base + array(
1161
    '#title' => 'No #default_value, #required',
1162
    '#required' => TRUE,
1163
  );
1164
  $form['no_default_optional'] = $base + array(
1165
    '#title' => 'No #default_value, not #required',
1166
    '#required' => FALSE,
1167
    '#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.',
1168
  );
1169
  $form['no_default_optional_empty_value'] = $base + array(
1170
    '#title' => 'No #default_value, not #required, #empty_value empty string',
1171
    '#empty_value' => '',
1172
    '#required' => FALSE,
1173
    '#description' => 'Should result in an empty string (due to #empty_value), because it is optional.',
1174
  );
1175

    
1176
  $form['no_default_empty_option'] = $base + array(
1177
    '#title' => 'No #default_value, #required, #empty_option',
1178
    '#required' => TRUE,
1179
    '#empty_option' => '- Choose -',
1180
  );
1181
  $form['no_default_empty_option_optional'] = $base + array(
1182
    '#title' => 'No #default_value, not #required, #empty_option',
1183
    '#empty_option' => '- Dismiss -',
1184
    '#description' => 'Should result in an empty string (default of #empty_value), because it is optional.',
1185
  );
1186

    
1187
  $form['no_default_empty_value'] = $base + array(
1188
    '#title' => 'No #default_value, #required, #empty_value 0',
1189
    '#required' => TRUE,
1190
    '#empty_value' => 0,
1191
    '#description' => 'Should never result in 0.',
1192
  );
1193
  $form['no_default_empty_value_one'] = $base + array(
1194
    '#title' => 'No #default_value, #required, #empty_value one',
1195
    '#required' => TRUE,
1196
    '#empty_value' => 'one',
1197
    '#description' => 'A mistakenly assigned #empty_value contained in #options should not be valid.',
1198
  );
1199
  $form['no_default_empty_value_optional'] = $base + array(
1200
    '#title' => 'No #default_value, not #required, #empty_value 0',
1201
    '#required' => FALSE,
1202
    '#empty_value' => 0,
1203
    '#description' => 'Should result in 0, because it is optional.',
1204
  );
1205

    
1206
  $form['multiple'] = $base + array(
1207
    '#title' => '#multiple, #default_value two',
1208
    '#default_value' => array('two'),
1209
    '#multiple' => TRUE,
1210
  );
1211
  $form['multiple_no_default'] = $base + array(
1212
    '#title' => '#multiple, no #default_value',
1213
    '#multiple' => TRUE,
1214
  );
1215
  $form['multiple_no_default_required'] = $base + array(
1216
    '#title' => '#multiple, #required, no #default_value',
1217
    '#required' => TRUE,
1218
    '#multiple' => TRUE,
1219
  );
1220

    
1221
  $form['submit'] = array('#type' => 'submit', '#value' => 'Submit');
1222
  return $form;
1223
}
1224

    
1225
/**
1226
 * Form submit handler for form_test_select().
1227
 */
1228
function form_test_select_submit($form, &$form_state) {
1229
  drupal_json_output($form_state['values']);
1230
  exit();
1231
}
1232

    
1233
/**
1234
 * Form constructor to test expansion of #type checkboxes and radios.
1235
 */
1236
function form_test_checkboxes_radios($form, &$form_state, $customize = FALSE) {
1237
  $form['#submit'] = array('_form_test_submit_values_json');
1238

    
1239
  // Expand #type checkboxes, setting custom element properties for some but not
1240
  // all options.
1241
  $form['checkboxes'] = array(
1242
    '#type' => 'checkboxes',
1243
    '#title' => 'Checkboxes',
1244
    '#options' => array(
1245
      0 => 'Zero',
1246
      'foo' => 'Foo',
1247
      1 => 'One',
1248
      'bar' => 'Bar',
1249
      '>' => 'Special Char',
1250
    ),
1251
  );
1252
  if ($customize) {
1253
    $form['checkboxes'] += array(
1254
      'foo' => array(
1255
        '#description' => 'Enable to foo.',
1256
      ),
1257
      1 => array(
1258
        '#weight' => 10,
1259
      ),
1260
    );
1261
  }
1262

    
1263
  // Expand #type radios, setting custom element properties for some but not
1264
  // all options.
1265
  $form['radios'] = array(
1266
    '#type' => 'radios',
1267
    '#title' => 'Radios',
1268
    '#options' => array(
1269
      0 => 'Zero',
1270
      'foo' => 'Foo',
1271
      1 => 'One',
1272
      'bar' => 'Bar',
1273
      '>' => 'Special Char',
1274
    ),
1275
  );
1276
  if ($customize) {
1277
    $form['radios'] += array(
1278
      'foo' => array(
1279
        '#description' => 'Enable to foo.',
1280
      ),
1281
      1 => array(
1282
        '#weight' => 10,
1283
      ),
1284
    );
1285
  }
1286

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

    
1289
  return $form;
1290
}
1291

    
1292
/**
1293
 * Build a form to test disabled elements.
1294
 */
1295
function _form_test_disabled_elements($form, &$form_state) {
1296
  // Elements that take a simple default value.
1297
  foreach (array('textfield', 'textarea', 'hidden') as $type) {
1298
    $form[$type] = array(
1299
      '#type' => $type,
1300
      '#title' => $type,
1301
      '#default_value' => $type,
1302
      '#test_hijack_value' => 'HIJACK',
1303
      '#disabled' => TRUE,
1304
    );
1305
  }
1306

    
1307
  // Multiple values option elements.
1308
  foreach (array('checkboxes', 'select') as $type) {
1309
    $form[$type . '_multiple'] = array(
1310
      '#type' => $type,
1311
      '#title' => $type . ' (multiple)',
1312
      '#options' => array(
1313
        'test_1' => 'Test 1',
1314
        'test_2' => 'Test 2',
1315
      ),
1316
      '#multiple' => TRUE,
1317
      '#default_value' => array('test_2' => 'test_2'),
1318
      // The keys of #test_hijack_value need to match the #name of the control.
1319
      // @see FormsTestCase::testDisabledElements()
1320
      '#test_hijack_value' => $type == 'select' ? array('' => 'test_1') : array('test_1' => 'test_1'),
1321
      '#disabled' => TRUE,
1322
    );
1323
  }
1324

    
1325
  // Single values option elements.
1326
  foreach (array('radios', 'select') as $type) {
1327
    $form[$type . '_single'] = array(
1328
      '#type' => $type,
1329
      '#title' => $type . ' (single)',
1330
      '#options' => array(
1331
        'test_1' => 'Test 1',
1332
        'test_2' => 'Test 2',
1333
      ),
1334
      '#multiple' => FALSE,
1335
      '#default_value' => 'test_2',
1336
      '#test_hijack_value' => 'test_1',
1337
      '#disabled' => TRUE,
1338
    );
1339
  }
1340

    
1341
  // Checkbox and radio.
1342
  foreach (array('checkbox', 'radio') as $type) {
1343
    $form[$type . '_unchecked'] = array(
1344
      '#type' => $type,
1345
      '#title' => $type . ' (unchecked)',
1346
      '#return_value' => 1,
1347
      '#default_value' => 0,
1348
      '#test_hijack_value' => 1,
1349
      '#disabled' => TRUE,
1350
    );
1351
    $form[$type . '_checked'] = array(
1352
      '#type' => $type,
1353
      '#title' => $type . ' (checked)',
1354
      '#return_value' => 1,
1355
      '#default_value' => 1,
1356
      '#test_hijack_value' => NULL,
1357
      '#disabled' => TRUE,
1358
    );
1359
  }
1360

    
1361
  // Weight.
1362
  $form['weight'] = array(
1363
    '#type' => 'weight',
1364
    '#title' => 'weight',
1365
    '#default_value' => 10,
1366
    '#test_hijack_value' => 5,
1367
    '#disabled' => TRUE,
1368
  );
1369

    
1370
  // Date.
1371
  $form['date'] = array(
1372
    '#type' => 'date',
1373
    '#title' => 'date',
1374
    '#disabled' => TRUE,
1375
    '#default_value' => array(
1376
      'day' => 19,
1377
      'month' => 11,
1378
      'year' => 1978,
1379
    ),
1380
    '#test_hijack_value' => array(
1381
      'day' => 20,
1382
      'month' => 12,
1383
      'year' => 1979,
1384
    ),
1385
  );
1386

    
1387
  // The #disabled state should propagate to children.
1388
  $form['disabled_container'] = array(
1389
    '#disabled' => TRUE,
1390
  );
1391
  foreach (array('textfield', 'textarea', 'hidden') as $type) {
1392
    $form['disabled_container']['disabled_container_' . $type] = array(
1393
      '#type' => $type,
1394
      '#title' => $type,
1395
      '#default_value' => $type,
1396
      '#test_hijack_value' => 'HIJACK',
1397
    );
1398
  }
1399

    
1400
  // Text format.
1401
  $form['text_format'] = array(
1402
    '#type' => 'text_format',
1403
    '#title' => 'Text format',
1404
    '#disabled' => TRUE,
1405
    '#default_value' => 'Text value',
1406
    '#format' => 'plain_text',
1407
    '#expected_value' => array(
1408
      'value' => 'Text value',
1409
      'format' => 'plain_text',
1410
    ),
1411
    '#test_hijack_value' => array(
1412
      'value' => 'HIJACK',
1413
      'format' => 'filtered_html',
1414
    ),
1415
  );
1416

    
1417
  // Password fields.
1418
  $form['password'] = array(
1419
    '#type' => 'password',
1420
    '#title' => 'Password',
1421
    '#disabled' => TRUE,
1422
  );
1423
  $form['password_confirm'] = array(
1424
    '#type' => 'password_confirm',
1425
    '#title' => 'Password confirm',
1426
    '#disabled' => TRUE,
1427
  );
1428

    
1429
  // Files.
1430
  $form['file'] = array(
1431
    '#type' => 'file',
1432
    '#title' => 'File',
1433
    '#disabled' => TRUE,
1434
  );
1435
  $form['managed_file'] = array(
1436
    '#type' => 'managed_file',
1437
    '#title' => 'Managed file',
1438
    '#disabled' => TRUE,
1439
  );
1440

    
1441
  // Buttons.
1442
  $form['image_button'] = array(
1443
    '#type' => 'image_button',
1444
    '#value' => 'Image button',
1445
    '#disabled' => TRUE,
1446
  );
1447
  $form['button'] = array(
1448
    '#type' => 'button',
1449
    '#value' => 'Button',
1450
    '#disabled' => TRUE,
1451
  );
1452
  $form['submit_disabled'] = array(
1453
    '#type' => 'submit',
1454
    '#value' => 'Submit',
1455
    '#disabled' => TRUE,
1456
  );
1457

    
1458
  $form['submit'] = array(
1459
    '#type' => 'submit',
1460
    '#value' => t('Submit'),
1461
  );
1462

    
1463
  return $form;
1464
}
1465

    
1466
/**
1467
 * Return the form values via JSON.
1468
 */
1469
function _form_test_disabled_elements_submit($form, &$form_state) {
1470
  drupal_json_output($form_state['values']);
1471
  exit();
1472
}
1473

    
1474
/**
1475
 * Build a form to test input forgery of enabled elements.
1476
 */
1477
function _form_test_input_forgery($form, &$form_state) {
1478
  // For testing that a user can't submit a value not matching one of the
1479
  // allowed options.
1480
  $form['checkboxes'] = array(
1481
    '#type' => 'checkboxes',
1482
    '#options' => array(
1483
      'one' => 'One',
1484
      'two' => 'Two',
1485
    ),
1486
  );
1487

    
1488
  $form['submit'] = array(
1489
    '#type' => 'submit',
1490
    '#value' => t('Submit'),
1491
  );
1492
  return $form;
1493
}
1494

    
1495
/**
1496
 * Return the form values via JSON.
1497
 */
1498
function _form_test_input_forgery_submit($form, &$form_state) {
1499
  drupal_json_output($form_state['values']);
1500
  exit();
1501
}
1502

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

    
1564
/**
1565
 * Button submit handler for form_test_form_rebuild_preserve_values_form().
1566
 */
1567
function form_test_form_rebuild_preserve_values_form_add_more($form, &$form_state) {
1568
  // Rebuild, to test preservation of input values.
1569
  $form_state['storage']['add_more'] = TRUE;
1570
  $form_state['rebuild'] = TRUE;
1571
}
1572

    
1573
/**
1574
 * Form submit handler for form_test_form_rebuild_preserve_values_form().
1575
 */
1576
function form_test_form_rebuild_preserve_values_form_submit($form, &$form_state) {
1577
  // Finish the workflow. Do not rebuild.
1578
  drupal_set_message(t('Form values: %values', array('%values' => var_export($form_state['values'], TRUE))));
1579
}
1580

    
1581
/**
1582
 * Form constructor for testing form state persistence.
1583
 */
1584
function form_test_state_persist($form, &$form_state) {
1585
  $form['title'] = array(
1586
    '#type' => 'textfield',
1587
    '#title' => 'title',
1588
    '#default_value' => 'DEFAULT',
1589
    '#required' => TRUE,
1590
  );
1591
  $form_state['value'] = 'State persisted.';
1592

    
1593
  $form['submit'] = array(
1594
    '#type' => 'submit',
1595
    '#value' => t('Submit'),
1596
  );
1597
  return $form;
1598
}
1599

    
1600
/**
1601
 * Submit handler.
1602
 *
1603
 * @see form_test_state_persist()
1604
 */
1605
function form_test_state_persist_submit($form, &$form_state) {
1606
  drupal_set_message($form_state['value']);
1607
  $form_state['rebuild'] = TRUE;
1608
}
1609

    
1610
/**
1611
 * Implements hook_form_FORM_ID_alter().
1612
 *
1613
 * @see form_test_state_persist()
1614
 */
1615
function form_test_form_form_test_state_persist_alter(&$form, &$form_state) {
1616
  // Simulate a form alter implementation inserting form elements that enable
1617
  // caching of the form, e.g. elements having #ajax.
1618
  if (!empty($_REQUEST['cache'])) {
1619
    $form_state['cache'] = TRUE;
1620
  }
1621
}
1622

    
1623
/**
1624
 * Form builder to test programmatic form submissions.
1625
 */
1626
function form_test_programmatic_form($form, &$form_state) {
1627
  $form['textfield'] = array(
1628
    '#title' => 'Textfield',
1629
    '#type' => 'textfield',
1630
  );
1631

    
1632
  $form['checkboxes'] = array(
1633
    '#type' => 'checkboxes',
1634
    '#options' => array(
1635
      1 => 'First checkbox',
1636
      2 => 'Second checkbox',
1637
    ),
1638
    // Both checkboxes are selected by default so that we can test the ability
1639
    // of programmatic form submissions to uncheck them.
1640
    '#default_value' => array(1, 2),
1641
  );
1642

    
1643
  // This is used to test that programmatic form submissions can bypass #access
1644
  // restrictions.
1645
  $form['textfield_no_access'] = array(
1646
    '#type' => 'textfield',
1647
    '#title' => 'Textfield no access',
1648
    '#default_value' => 'default value',
1649
    '#access' => FALSE,
1650
  );
1651

    
1652
  $form['field_to_validate'] = array(
1653
    '#type' => 'radios',
1654
    '#title' => 'Field to validate (in the case of limited validation)',
1655
    '#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.',
1656
    '#options' => array(
1657
      'all' => 'Validate all fields',
1658
      'textfield' => 'Validate the "Textfield" field',
1659
      'field_to_validate' => 'Validate the "Field to validate" field',
1660
    ),
1661
    '#default_value' => 'all',
1662
  );
1663

    
1664
  // The main submit button for the form.
1665
  $form['submit'] = array(
1666
    '#type' => 'submit',
1667
    '#value' => 'Submit',
1668
  );
1669
  // A secondary submit button that allows validation to be limited based on
1670
  // the value of the above radio selector.
1671
  $form['submit_limit_validation'] = array(
1672
    '#type' => 'submit',
1673
    '#value' => 'Submit with limited validation',
1674
    // Use the same submit handler for this button as for the form itself.
1675
    // (This must be set explicitly or otherwise the form API will ignore the
1676
    // #limit_validation_errors property.)
1677
    '#submit' => array('form_test_programmatic_form_submit'),
1678
  );
1679
  if (!empty($form_state['input']['field_to_validate']) && $form_state['input']['field_to_validate'] != 'all') {
1680
    $form['submit_limit_validation']['#limit_validation_errors'] = array(
1681
      array($form_state['input']['field_to_validate']),
1682
    );
1683
  }
1684

    
1685
  return $form;
1686
}
1687

    
1688
/**
1689
 * Form validation handler for programmatic form submissions.
1690
 *
1691
 * To test that the validation handler is correctly executed, the field value is
1692
 * explicitly required here.
1693
 */
1694
function form_test_programmatic_form_validate($form, &$form_state) {
1695
  if (empty($form_state['values']['textfield'])) {
1696
    form_set_error('textfield', t('Textfield is required.'));
1697
  }
1698
}
1699

    
1700
/**
1701
 * Form submit handler for programmatic form submissions.
1702
 *
1703
 * To test that the submission handler is correctly executed, we store the
1704
 * submitted values in a place we can access from the caller context.
1705
 */
1706
function form_test_programmatic_form_submit($form, &$form_state) {
1707
  $form_state['storage']['programmatic_form_submit'] = $form_state['values'];
1708
}
1709

    
1710
/**
1711
 * Form builder to test button click detection.
1712
 */
1713
function form_test_clicked_button($form, &$form_state) {
1714
  // A single text field. In IE, when a form has only one non-button input field
1715
  // and the ENTER key is pressed while that field has focus, the form is
1716
  // submitted without any information identifying the button responsible for
1717
  // the submission. In other browsers, the form is submitted as though the
1718
  // first button were clicked.
1719
  $form['text'] = array(
1720
    '#title' => 'Text',
1721
    '#type' => 'textfield',
1722
  );
1723

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

    
1766
  return $form;
1767
}
1768

    
1769
/**
1770
 * Form validation handler for the form_test_clicked_button() form.
1771
 */
1772
function form_test_clicked_button_validate($form, &$form_state) {
1773
  if (isset($form_state['triggering_element'])) {
1774
    drupal_set_message(t('The clicked button is %name.', array('%name' => $form_state['triggering_element']['#name'])));
1775
  }
1776
  else {
1777
    drupal_set_message('There is no clicked button.');
1778
  }
1779
}
1780

    
1781
/**
1782
 * Form submit handler for the form_test_clicked_button() form.
1783
 */
1784
function form_test_clicked_button_submit($form, &$form_state) {
1785
  drupal_set_message('Submit handler for form_test_clicked_button executed.');
1786
}
1787

    
1788
/**
1789
 * Form builder to detect form redirect.
1790
 */
1791
function form_test_redirect($form, &$form_state) {
1792
  $form['redirection'] = array(
1793
    '#type' => 'checkbox',
1794
    '#title' => t('Use redirection'),
1795
  );
1796
  $form['destination'] = array(
1797
    '#type' => 'textfield',
1798
    '#title' => t('Redirect destination'),
1799
    '#states' => array(
1800
      'visible' => array(
1801
        ':input[name="redirection"]' => array('checked' => TRUE),
1802
      ),
1803
    ),
1804
  );
1805
  $form['submit'] = array(
1806
    '#type' => 'submit',
1807
    '#value' => t('Submit'),
1808
  );
1809

    
1810
  return $form;
1811
}
1812

    
1813
/**
1814
 * Form submit handler to test different redirect behaviours.
1815
 */
1816
function form_test_redirect_submit(&$form, &$form_state) {
1817
  if (!empty($form_state['values']['redirection'])) {
1818
    $form_state['redirect'] = !empty($form_state['values']['destination']) ? $form_state['values']['destination'] : NULL;
1819
  }
1820
  else {
1821
    $form_state['redirect'] = FALSE;
1822
  }
1823
}
1824

    
1825
/**
1826
 * Implements hook_form_FORM_ID_alter() for the registration form.
1827
 */
1828
function form_test_form_user_register_form_alter(&$form, &$form_state) {
1829
  $form['test_rebuild'] = array(
1830
    '#type' => 'submit',
1831
    '#value' => t('Rebuild'),
1832
    '#submit' => array('form_test_user_register_form_rebuild'),
1833
  );
1834
  // If requested, add the test field by attaching the node page form.
1835
  if (!empty($_REQUEST['field'])) {
1836
    $node = (object)array('type' => 'page');
1837
    field_attach_form('node', $node, $form, $form_state);
1838
  }
1839
}
1840

    
1841
/**
1842
 * Submit callback that just lets the form rebuild.
1843
 */
1844
function form_test_user_register_form_rebuild($form, &$form_state) {
1845
  drupal_set_message('Form rebuilt.');
1846
  $form_state['rebuild'] = TRUE;
1847
}
1848

    
1849
/**
1850
 * Menu callback that returns two instances of the node form.
1851
 */
1852
function form_test_two_instances() {
1853
  global $user;
1854
  $node1 = (object) array(
1855
    'uid' => $user->uid,
1856
    'name' => (isset($user->name) ? $user->name : ''),
1857
    'type' => 'page',
1858
    'language' => LANGUAGE_NONE,
1859
  );
1860
  $node2 = clone($node1);
1861
  $return['node_form_1'] = drupal_get_form('page_node_form', $node1);
1862
  $return['node_form_2'] = drupal_get_form('page_node_form', $node2);
1863
  return $return;
1864
}
1865

    
1866
/**
1867
 * Menu callback for testing custom form includes.
1868
 */
1869
function form_test_load_include_custom($form, &$form_state) {
1870
  $form['button'] = array(
1871
    '#type' => 'submit',
1872
    '#value' => t('Save'),
1873
    '#submit' => array('form_test_load_include_submit'),
1874
  );
1875
  // Specify the include file and enable form caching. That way the form is
1876
  // cached when it is submitted, but needs to find the specified submit handler
1877
  // in the include.
1878
  // Filename is a bit weird here: modules/simpletest/tests/form_test.file.inc
1879
  form_load_include($form_state, 'inc', 'form_test', 'form_test.file');
1880
  $form_state['cache'] = TRUE;
1881
  return $form;
1882
}
1883

    
1884
function form_test_checkbox_type_juggling($form, $form_state, $default_value, $return_value) {
1885
  $form['checkbox'] = array(
1886
    '#type' => 'checkbox',
1887
    '#return_value' => $return_value,
1888
    '#default_value' => $default_value,
1889
  );
1890
  return $form;
1891
}
1892

    
1893
function form_test_checkboxes_zero($form, &$form_state, $json = TRUE) {
1894
  $form['checkbox_off'] = array(
1895
    '#type' => 'checkboxes',
1896
    '#options' => array('foo', 'bar', 'baz'),
1897
  );
1898
  $form['checkbox_zero_default'] = array(
1899
    '#type' => 'checkboxes',
1900
    '#options' => array('foo', 'bar', 'baz'),
1901
    '#default_value' => array(0),
1902
  );
1903
  $form['checkbox_string_zero_default'] = array(
1904
    '#type' => 'checkboxes',
1905
    '#options' => array('foo', 'bar', 'baz'),
1906
    '#default_value' => array('0'),
1907
  );
1908
  $form['submit'] = array(
1909
    '#type' => 'submit',
1910
    '#value' => 'Save',
1911
  );
1912
  if ($json) {
1913
    $form['#submit'][] = '_form_test_checkbox_submit';
1914
  }
1915
  else {
1916
    $form['#submit'][] = '_form_test_checkboxes_zero_no_redirect';
1917
  }
1918
  return $form;
1919
}
1920

    
1921
function _form_test_checkboxes_zero_no_redirect($form, &$form_state) {
1922
  $form_state['redirect'] = FALSE;
1923
}
1924

    
1925
/**
1926
 * Menu callback returns two instances of the same form.
1927
 */
1928
function form_test_double_form() {
1929
  return array(
1930
    'form1' => drupal_get_form('form_test_html_id'),
1931
    'form2' => drupal_get_form('form_test_html_id'),
1932
  );
1933
}
1934

    
1935
/**
1936
 * Builds a simple form to test duplicate HTML IDs.
1937
 */
1938
function form_test_html_id($form, &$form_state) {
1939
  $form['name'] = array(
1940
    '#type' => 'textfield',
1941
    '#title' => 'name',
1942
    '#required' => TRUE,
1943
  );
1944
  $form['submit'] = array(
1945
    '#type' => 'submit',
1946
    '#value' => 'Save',
1947
  );
1948
  return $form;
1949
}