Projet

Général

Profil

Paste
Télécharger (55,2 ko) Statistiques
| Branche: | Révision:

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

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/wrapper-callback'] = array(
94
    'title' => 'Form wrapper callback test',
95
    'page callback' => 'form_test_wrapper_callback',
96
    'page arguments' => array('form_test_wrapper_callback_form'),
97
    'access arguments' => array('access content'),
98
    'type' => MENU_CALLBACK,
99
  );
100

    
101
  $items['form_test/form-state-values-clean'] = array(
102
    'title' => 'Form state values clearance test',
103
    'page callback' => 'drupal_get_form',
104
    'page arguments' => array('form_test_form_state_values_clean_form'),
105
    'access arguments' => array('access content'),
106
    'type' => MENU_CALLBACK,
107
  );
108

    
109
  $items['form_test/form-state-values-clean-advanced'] = array(
110
    'title' => 'Form state values clearance advanced test',
111
    'page callback' => 'drupal_get_form',
112
    'page arguments' => array('form_test_form_state_values_clean_advanced_form'),
113
    'access arguments' => array('access content'),
114
    'type' => MENU_CALLBACK,
115
  );
116

    
117
  $items['form-test/checkbox'] = array(
118
    'title' => t('Form test'),
119
    'page callback' => 'drupal_get_form',
120
    'page arguments' => array('_form_test_checkbox'),
121
    'access callback' => TRUE,
122
    'type' => MENU_CALLBACK,
123
  );
124
  $items['form-test/select'] = array(
125
    'title' => t('Select'),
126
    'page callback' => 'drupal_get_form',
127
    'page arguments' => array('form_test_select'),
128
    'access callback' => TRUE,
129
  );
130
  $items['form-test/checkboxes-radios'] = array(
131
    'title' => t('Checkboxes, Radios'),
132
    'page callback' => 'drupal_get_form',
133
    'page arguments' => array('form_test_checkboxes_radios'),
134
    'access callback' => TRUE,
135
  );
136

    
137
  $items['form-test/disabled-elements'] = array(
138
    'title' => t('Form test'),
139
    'page callback' => 'drupal_get_form',
140
    'page arguments' => array('_form_test_disabled_elements'),
141
    'access callback' => TRUE,
142
    'type' => MENU_CALLBACK,
143
  );
144

    
145
  $items['form-test/input-forgery'] = array(
146
    'title' => t('Form test'),
147
    'page callback' => 'drupal_get_form',
148
    'page arguments' => array('_form_test_input_forgery'),
149
    'access callback' => TRUE,
150
    'type' => MENU_CALLBACK,
151
  );
152

    
153
  $items['form-test/form-rebuild-preserve-values'] = array(
154
    'title' => 'Form values preservation during rebuild test',
155
    'page callback' => 'drupal_get_form',
156
    'page arguments' => array('form_test_form_rebuild_preserve_values_form'),
157
    'access arguments' => array('access content'),
158
    'type' => MENU_CALLBACK,
159
  );
160

    
161
  $items['form-test/redirect'] = array(
162
    'title' => 'Redirect test',
163
    'page callback' => 'drupal_get_form',
164
    'page arguments' => array('form_test_redirect'),
165
    'access callback' => TRUE,
166
    'type' => MENU_CALLBACK,
167
  );
168

    
169
  $items['form_test/form-labels'] = array(
170
    'title' => 'Form label test',
171
    'page callback' => 'drupal_get_form',
172
    'page arguments' => array('form_label_test_form'),
173
    'access arguments' => array('access content'),
174
    'type' => MENU_CALLBACK,
175
  );
176

    
177
  $items['form-test/state-persist'] = array(
178
    'title' => 'Form state persistence without storage',
179
    'page callback' => 'drupal_get_form',
180
    'page arguments' => array('form_test_state_persist'),
181
    'access callback' => TRUE,
182
    'type' => MENU_CALLBACK,
183
  );
184

    
185
  $items['form-test/clicked-button'] = array(
186
    'title' => 'Clicked button test',
187
    'page callback' => 'drupal_get_form',
188
    'page arguments' => array('form_test_clicked_button'),
189
    'access callback' => TRUE,
190
    'type' => MENU_CALLBACK,
191
  );
192

    
193
  if (module_exists('node')) {
194
    $items['form-test/two-instances-of-same-form'] = array(
195
      'title' => 'AJAX test with two form instances',
196
      'page callback' => 'form_test_two_instances',
197
      'access callback' => 'node_access',
198
      'access arguments' => array('create', 'page'),
199
      'file path' => drupal_get_path('module', 'node'),
200
      'file' => 'node.pages.inc',
201
      'type' => MENU_CALLBACK,
202
    );
203
  }
204
  $items['form-test/double-form'] = array(
205
    'title' => 'Double form test',
206
    'page callback' => 'form_test_double_form',
207
    'access callback' => TRUE,
208
    'type' => MENU_CALLBACK,
209
  );
210

    
211
  $items['form-test/load-include-menu'] = array(
212
    'title' => 'FAPI test loading includes',
213
    'page callback' => 'drupal_get_form',
214
    'page arguments' => array('form_test_load_include_menu'),
215
    'access callback' => TRUE,
216
    'file' => 'form_test.file.inc',
217
    'type' => MENU_CALLBACK,
218
  );
219

    
220
  $items['form-test/load-include-custom'] = array(
221
    'title' => 'FAPI test loading includes',
222
    'page callback' => 'drupal_get_form',
223
    'page arguments' => array('form_test_load_include_custom'),
224
    'access callback' => TRUE,
225
    'type' => MENU_CALLBACK,
226
  );
227
  $items['form-test/checkboxes-zero'] = array(
228
    'title' => 'FAPI test involving checkboxes and zero',
229
    'page callback' => 'drupal_get_form',
230
    'page arguments' => array('form_test_checkboxes_zero'),
231
    'access callback' => TRUE,
232
    'type' => MENU_CALLBACK,
233
  );
234

    
235
  return $items;
236
}
237

    
238
/**
239
 * Form submit handler to return form values as JSON.
240
 */
241
function _form_test_submit_values_json($form, &$form_state) {
242
  drupal_json_output($form_state['values']);
243
  drupal_exit();
244
}
245

    
246
/**
247
 * Form builder for testing hook_form_alter() and hook_form_FORM_ID_alter().
248
 */
249
function form_test_alter_form($form, &$form_state) {
250
  // Elements can be added as needed for future testing needs, but for now,
251
  // we're only testing alter hooks that do not require any elements added by
252
  // this function.
253
  return $form;
254
}
255

    
256
/**
257
 * Implements hook_form_FORM_ID_alter() on behalf of block.module.
258
 */
259
function block_form_form_test_alter_form_alter(&$form, &$form_state) {
260
  drupal_set_message('block_form_form_test_alter_form_alter() executed.');
261
}
262

    
263
/**
264
 * Implements hook_form_alter().
265
 */
266
function form_test_form_alter(&$form, &$form_state, $form_id) {
267
  if ($form_id == 'form_test_alter_form') {
268
    drupal_set_message('form_test_form_alter() executed.');
269
  }
270
}
271

    
272
/**
273
 * Implements hook_form_FORM_ID_alter().
274
 */
275
function form_test_form_form_test_alter_form_alter(&$form, &$form_state) {
276
  drupal_set_message('form_test_form_form_test_alter_form_alter() executed.');
277
}
278

    
279
/**
280
 * Implements hook_form_FORM_ID_alter() on behalf of system.module.
281
 */
282
function system_form_form_test_alter_form_alter(&$form, &$form_state) {
283
  drupal_set_message('system_form_form_test_alter_form_alter() executed.');
284
}
285

    
286
/**
287
 * Form builder for testing drupal_validate_form().
288
 *
289
 * Serves for testing form processing and alterations by form validation
290
 * handlers, especially for the case of a validation error:
291
 * - form_set_value() should be able to alter submitted values in
292
 *   $form_state['values'] without affecting the form element.
293
 * - #element_validate handlers should be able to alter the $element in the form
294
 *   structure and the alterations should be contained in the rebuilt form.
295
 * - #validate handlers should be able to alter the $form and the alterations
296
 *   should be contained in the rebuilt form.
297
 */
298
function form_test_validate_form($form, &$form_state) {
299
  $form['name'] = array(
300
    '#type' => 'textfield',
301
    '#title' => 'Name',
302
    '#default_value' => '',
303
    '#element_validate' => array('form_test_element_validate_name'),
304
  );
305
  $form['submit'] = array(
306
    '#type' => 'submit',
307
    '#value' => 'Save',
308
  );
309

    
310
  // To simplify this test, enable form caching and use form storage to
311
  // remember our alteration.
312
  $form_state['cache'] = TRUE;
313

    
314
  return $form;
315
}
316

    
317
/**
318
 * Form element validation handler for 'name' in form_test_validate_form().
319
 */
320
function form_test_element_validate_name(&$element, &$form_state) {
321
  $triggered = FALSE;
322
  if ($form_state['values']['name'] == 'element_validate') {
323
    // Alter the form element.
324
    $element['#value'] = '#value changed by #element_validate';
325
    // Alter the submitted value in $form_state.
326
    form_set_value($element, 'value changed by form_set_value() in #element_validate', $form_state);
327

    
328
    $triggered = TRUE;
329
  }
330
  if ($form_state['values']['name'] == 'element_validate_access') {
331
    $form_state['storage']['form_test_name'] = $form_state['values']['name'];
332
    // Alter the form element.
333
    $element['#access'] = FALSE;
334

    
335
    $triggered = TRUE;
336
  }
337
  elseif (!empty($form_state['storage']['form_test_name'])) {
338
    // To simplify this test, just take over the element's value into $form_state.
339
    form_set_value($element, $form_state['storage']['form_test_name'], $form_state);
340

    
341
    $triggered = TRUE;
342
  }
343

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

    
348
    // Trigger a form validation error to see our changes.
349
    form_set_error('');
350
  }
351
}
352

    
353
/**
354
 * Form validation handler for form_test_validate_form().
355
 */
356
function form_test_validate_form_validate(&$form, &$form_state) {
357
  if ($form_state['values']['name'] == 'validate') {
358
    // Alter the form element.
359
    $form['name']['#value'] = '#value changed by #validate';
360
    // Alter the submitted value in $form_state.
361
    form_set_value($form['name'], 'value changed by form_set_value() in #validate', $form_state);
362
    // Output the element's value from $form_state.
363
    drupal_set_message(t('@label value: @value', array('@label' => $form['name']['#title'], '@value' => $form_state['values']['name'])));
364

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

    
370
/**
371
 * Form constructor to test the #required property.
372
 */
373
function form_test_validate_required_form($form, &$form_state) {
374
  $options = drupal_map_assoc(array('foo', 'bar'));
375

    
376
  $form['textfield'] = array(
377
    '#type' => 'textfield',
378
    '#title' => 'Textfield',
379
    '#required' => TRUE,
380
  );
381
  $form['checkboxes'] = array(
382
    '#type' => 'checkboxes',
383
    '#title' => 'Checkboxes',
384
    '#options' => $options,
385
    '#required' => TRUE,
386
  );
387
  $form['select'] = array(
388
    '#type' => 'select',
389
    '#title' => 'Select',
390
    '#options' => $options,
391
    '#required' => TRUE,
392
  );
393
  $form['radios'] = array(
394
    '#type' => 'radios',
395
    '#title' => 'Radios',
396
    '#options' => $options,
397
    '#required' => TRUE,
398
  );
399
  $form['radios_optional'] = array(
400
    '#type' => 'radios',
401
    '#title' => 'Radios (optional)',
402
    '#options' => $options,
403
  );
404
  $form['radios_optional_default_value_false'] = array(
405
    '#type' => 'radios',
406
    '#title' => 'Radios (optional, with a default value of FALSE)',
407
    '#options' => $options,
408
    '#default_value' => FALSE,
409
  );
410
  $form['actions'] = array('#type' => 'actions');
411
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => 'Submit');
412
  return $form;
413
}
414

    
415
/**
416
 * Form submission handler for form_test_validate_required_form().
417
 */
418
function form_test_validate_required_form_submit($form, &$form_state) {
419
  drupal_set_message('The form_test_validate_required_form form was submitted successfully.');
420
}
421

    
422
/**
423
 * Form constructor to test the #required property without #title.
424
 */
425
function form_test_validate_required_form_no_title($form, &$form_state) {
426
  $form['textfield'] = array(
427
    '#type' => 'textfield',
428
    '#required' => TRUE,
429
  );
430
  $form['actions'] = array('#type' => 'actions');
431
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => 'Submit');
432
  return $form;
433
}
434

    
435
/**
436
 * Form submission handler for form_test_validate_required_form_no_title().
437
 */
438
function form_test_validate_required_form_no_title_submit($form, &$form_state) {
439
  drupal_set_message('The form_test_validate_required_form_no_title form was submitted successfully.');
440
}
441

    
442
/**
443
 * Builds a simple form with a button triggering partial validation.
444
 */
445
function form_test_limit_validation_errors_form($form, &$form_state) {
446
  $form['title'] = array(
447
    '#type' => 'textfield',
448
    '#title' => 'Title',
449
    '#required' => TRUE,
450
  );
451

    
452
  $form['test'] = array(
453
    '#title' => 'Test',
454
    '#type' => 'textfield',
455
    '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
456
  );
457
  $form['test_numeric_index'] = array(
458
    '#tree' => TRUE,
459
  );
460
  $form['test_numeric_index'][0] = array(
461
    '#title' => 'Test (numeric index)',
462
    '#type' => 'textfield',
463
    '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
464
  );
465

    
466
  $form['test_substring'] = array(
467
    '#tree' => TRUE,
468
  );
469
  $form['test_substring']['foo'] = array(
470
    '#title' => 'Test (substring) foo',
471
    '#type' => 'textfield',
472
    '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
473
  );
474
  $form['test_substring']['foobar'] = array(
475
    '#title' => 'Test (substring) foobar',
476
    '#type' => 'textfield',
477
    '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
478
  );
479

    
480
  $form['actions']['partial'] = array(
481
    '#type' => 'submit',
482
    '#limit_validation_errors' => array(array('test')),
483
    '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
484
    '#value' => t('Partial validate'),
485
  );
486
  $form['actions']['partial_numeric_index'] = array(
487
    '#type' => 'submit',
488
    '#limit_validation_errors' => array(array('test_numeric_index', 0)),
489
    '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
490
    '#value' => t('Partial validate (numeric index)'),
491
  );
492
  $form['actions']['substring'] = array(
493
    '#type' => 'submit',
494
    '#limit_validation_errors' => array(array('test_substring', 'foo')),
495
    '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
496
    '#value' => t('Partial validate (substring)'),
497
  );
498
  $form['actions']['full'] = array(
499
    '#type' => 'submit',
500
    '#value' => t('Full validate'),
501
  );
502
  return $form;
503
}
504

    
505
/**
506
 * Form element validation handler for the 'test' element.
507
 */
508
function form_test_limit_validation_errors_element_validate_test(&$element, &$form_state) {
509
  if ($element['#value'] == 'invalid') {
510
    form_error($element, t('@label element is invalid', array('@label' => $element['#title'])));
511
  }
512
}
513

    
514
/**
515
 * Form submit handler for the partial validation submit button.
516
 */
517
function form_test_limit_validation_errors_form_partial_submit($form, $form_state) {
518
  // The title has not been validated, thus its value - in case of the test case
519
  // an empty string - may not be set.
520
  if (!isset($form_state['values']['title']) && isset($form_state['values']['test'])) {
521
    drupal_set_message('Only validated values appear in the form values.');
522
  }
523
}
524

    
525
/**
526
 * Create a header and options array. Helper function for callbacks.
527
 */
528
function _form_test_tableselect_get_data() {
529
  $header = array(
530
    'one' => t('One'),
531
    'two' => t('Two'),
532
    'three' => t('Three'),
533
    'four' => t('Four'),
534
  );
535

    
536
  $options['row1'] = array(
537
    'one' => 'row1col1',
538
    'two' => t('row1col2'),
539
    'three' => t('row1col3'),
540
    'four' => t('row1col4'),
541
  );
542

    
543
  $options['row2'] = array(
544
    'one' => 'row2col1',
545
    'two' => t('row2col2'),
546
    'three' => t('row2col3'),
547
    'four' => t('row2col4'),
548
  );
549

    
550
  $options['row3'] = array(
551
    'one' => 'row3col1',
552
    'two' => t('row3col2'),
553
    'three' => t('row3col3'),
554
    'four' => t('row3col4'),
555
  );
556

    
557
  return array($header, $options);
558
}
559

    
560
/**
561
 * Build a form to test the tableselect element.
562
 *
563
 * @param $form_state
564
 *   The form_state
565
 * @param $element_properties
566
 *   An array of element properties for the tableselect element.
567
 *
568
 * @return
569
 *   A form with a tableselect element and a submit button.
570
 */
571
function _form_test_tableselect_form_builder($form, $form_state, $element_properties) {
572
  list($header, $options) = _form_test_tableselect_get_data();
573

    
574
  $form['tableselect'] = $element_properties;
575

    
576
  $form['tableselect'] += array(
577
    '#type' => 'tableselect',
578
    '#header' => $header,
579
    '#options' => $options,
580
    '#multiple' => FALSE,
581
    '#empty' => t('Empty text.'),
582
  );
583

    
584
  $form['submit'] = array(
585
    '#type' => 'submit',
586
    '#value' => t('Submit'),
587
  );
588

    
589
  return $form;
590
}
591

    
592
/**
593
 * Test the tableselect #multiple = TRUE functionality.
594
 */
595
function _form_test_tableselect_multiple_true_form($form, $form_state) {
596
  return _form_test_tableselect_form_builder($form, $form_state, array('#multiple' => TRUE));
597
}
598

    
599
/**
600
 * Process the tableselect #multiple = TRUE submitted values.
601
 */
602
function _form_test_tableselect_multiple_true_form_submit($form, &$form_state) {
603
  $selected = $form_state['values']['tableselect'];
604
  foreach ($selected as $key => $value) {
605
    drupal_set_message(t('Submitted: @key = @value', array('@key' => $key, '@value' => $value)));
606
  }
607
}
608

    
609
/**
610
 * Test the tableselect #multiple = FALSE functionality.
611
 */
612
function _form_test_tableselect_multiple_false_form($form, $form_state) {
613
  return _form_test_tableselect_form_builder($form, $form_state, array('#multiple' => FALSE));
614
}
615

    
616
/**
617
 * Process the tableselect #multiple = FALSE submitted values.
618
 */
619
function _form_test_tableselect_multiple_false_form_submit($form, &$form_state) {
620
  drupal_set_message(t('Submitted: @value', array('@value' => $form_state['values']['tableselect'])));
621
}
622

    
623
/**
624
 * Test functionality of the tableselect #empty property.
625
 */
626
function _form_test_tableselect_empty_form($form, $form_state) {
627
  return _form_test_tableselect_form_builder($form, $form_state, array('#options' => array()));
628
}
629

    
630
/**
631
 * Test functionality of the tableselect #js_select property.
632
 */
633
function _form_test_tableselect_js_select_form($form, $form_state, $action) {
634
  switch ($action) {
635
    case 'multiple-true-default':
636
      $options = array('#multiple' => TRUE);
637
      break;
638

    
639
    case 'multiple-false-default':
640
      $options = array('#multiple' => FALSE);
641
      break;
642

    
643
    case 'multiple-true-no-advanced-select':
644
      $options = array('#multiple' => TRUE, '#js_select' => FALSE);
645
      break;
646

    
647
    case 'multiple-false-advanced-select':
648
      $options = array('#multiple' => FALSE, '#js_select' => TRUE);
649
      break;
650
  }
651

    
652
  return _form_test_tableselect_form_builder($form, $form_state, $options);
653
}
654

    
655
/**
656
 * Tests functionality of vertical tabs.
657
 */
658
function _form_test_vertical_tabs_form($form, &$form_state) {
659
  $form['vertical_tabs'] = array(
660
    '#type' => 'vertical_tabs',
661
  );
662
  $form['tab1'] = array(
663
    '#type' => 'fieldset',
664
    '#title' => t('Tab 1'),
665
    '#collapsible' => TRUE,
666
    '#group' => 'vertical_tabs',
667
  );
668
  $form['tab1']['field1'] = array(
669
    '#title' => t('Field 1'),
670
    '#type' => 'textfield',
671
  );
672
  $form['tab2'] = array(
673
    '#type' => 'fieldset',
674
    '#title' => t('Tab 2'),
675
    '#collapsible' => TRUE,
676
    '#group' => 'vertical_tabs',
677
  );
678
  $form['tab2']['field2'] = array(
679
    '#title' => t('Field 2'),
680
    '#type' => 'textfield',
681
  );
682
  return $form;
683
}
684

    
685
/**
686
 * A multistep form for testing the form storage.
687
 *
688
 * It uses two steps for editing a virtual "thing". Any changes to it are saved
689
 * in the form storage and have to be present during any step. By setting the
690
 * request parameter "cache" the form can be tested with caching enabled, as
691
 * it would be the case, if the form would contain some #ajax callbacks.
692
 *
693
 * @see form_test_storage_form_submit()
694
 */
695
function form_test_storage_form($form, &$form_state) {
696
  if ($form_state['rebuild']) {
697
    $form_state['input'] = array();
698
  }
699
  // Initialize
700
  if (empty($form_state['storage'])) {
701
    if (empty($form_state['input'])) {
702
      $_SESSION['constructions'] = 0;
703
    }
704
    // Put the initial thing into the storage
705
    $form_state['storage'] = array(
706
      'thing' => array(
707
        'title' => 'none',
708
        'value' => '',
709
      ),
710
    );
711
  }
712
  // Count how often the form is constructed.
713
  $_SESSION['constructions']++;
714
  drupal_set_message("Form constructions: " . $_SESSION['constructions']);
715

    
716
  $form['title'] = array(
717
    '#type' => 'textfield',
718
    '#title' => 'Title',
719
    '#default_value' => $form_state['storage']['thing']['title'],
720
    '#required' => TRUE,
721
  );
722
  $form['value'] = array(
723
    '#type' => 'textfield',
724
    '#title' => 'Value',
725
    '#default_value' => $form_state['storage']['thing']['value'],
726
    '#element_validate' => array('form_test_storage_element_validate_value_cached'),
727
  );
728
  $form['continue_button'] = array(
729
    '#type' => 'button',
730
    '#value' => 'Reset',
731
    // Rebuilds the form without keeping the values.
732
  );
733
  $form['continue_submit'] = array(
734
    '#type' => 'submit',
735
    '#value' => 'Continue submit',
736
    '#submit' => array('form_storage_test_form_continue_submit'),
737
  );
738
  $form['submit'] = array(
739
    '#type' => 'submit',
740
    '#value' => 'Save',
741
  );
742

    
743
  if (isset($_REQUEST['cache'])) {
744
    // Manually activate caching, so we can test that the storage keeps working
745
    // when it's enabled.
746
    $form_state['cache'] = TRUE;
747
  }
748

    
749
  return $form;
750
}
751

    
752
/**
753
 * Form element validation handler for 'value' element in form_test_storage_form().
754
 *
755
 * Tests updating of cached form storage during validation.
756
 */
757
function form_test_storage_element_validate_value_cached($element, &$form_state) {
758
  // If caching is enabled and we receive a certain value, change the storage.
759
  // This presumes that another submitted form value triggers a validation error
760
  // elsewhere in the form. Form API should still update the cached form storage
761
  // though.
762
  if (isset($_REQUEST['cache']) && $form_state['values']['value'] == 'change_title') {
763
    $form_state['storage']['thing']['changed'] = TRUE;
764
  }
765
}
766

    
767
/**
768
 * Form submit handler to continue multi-step form.
769
 */
770
function form_storage_test_form_continue_submit($form, &$form_state) {
771
  $form_state['storage']['thing']['title'] = $form_state['values']['title'];
772
  $form_state['storage']['thing']['value'] = $form_state['values']['value'];
773
  $form_state['rebuild'] = TRUE;
774
}
775

    
776
/**
777
 * Form submit handler to finish multi-step form.
778
 */
779
function form_test_storage_form_submit($form, &$form_state) {
780
  drupal_set_message("Title: " . check_plain($form_state['values']['title']));
781
  drupal_set_message("Form constructions: " . $_SESSION['constructions']);
782
  if (isset($form_state['storage']['thing']['changed'])) {
783
    drupal_set_message("The thing has been changed.");
784
  }
785
  $form_state['redirect'] = 'node';
786
}
787

    
788
/**
789
 * A form for testing form labels and required marks.
790
 */
791
function form_label_test_form() {
792
  $form['form_checkboxes_test'] = array(
793
    '#type' => 'checkboxes',
794
    '#title' => t('Checkboxes test'),
795
    '#options' => array(
796
      'first-checkbox' => t('First checkbox'),
797
      'second-checkbox' => t('Second checkbox'),
798
      'third-checkbox' => t('Third checkbox'),
799
      '0' => t('0'),
800
    ),
801
  );
802
  $form['form_radios_test'] = array(
803
    '#type' => 'radios',
804
    '#title' => t('Radios test'),
805
    '#options' => array(
806
      'first-radio' => t('First radio'),
807
      'second-radio' => t('Second radio'),
808
      'third-radio' => t('Third radio'),
809
      '0' => t('0'),
810
    ),
811
    // Test #field_prefix and #field_suffix placement.
812
    '#field_prefix' => '<span id="form-test-radios-field-prefix">' . t('Radios #field_prefix element') . '</span>',
813
    '#field_suffix' => '<span id="form-test-radios-field-suffix">' . t('Radios #field_suffix element') . '</span>',
814
  );
815
  $form['form_checkbox_test'] = array(
816
    '#type' => 'checkbox',
817
    '#title' => t('Checkbox test'),
818
  );
819
  $form['form_textfield_test_title_and_required'] = array(
820
    '#type' => 'textfield',
821
    '#title' => t('Textfield test for required with title'),
822
    '#required' => TRUE,
823
  );
824
  $form['form_textfield_test_no_title_required'] = array(
825
    '#type' => 'textfield',
826
    // We use an empty title, since not setting #title suppresses the label
827
    // and required marker.
828
    '#title' => '',
829
    '#required' => TRUE,
830
  );
831
  $form['form_textfield_test_title'] = array(
832
    '#type' => 'textfield',
833
    '#title' => t('Textfield test for title only'),
834
    // Not required.
835
    // Test #prefix and #suffix placement.
836
    '#prefix' => '<div id="form-test-textfield-title-prefix">' . t('Textfield #prefix element') . '</div>',
837
    '#suffix' => '<div id="form-test-textfield-title-suffix">' . t('Textfield #suffix element') . '</div>',
838
  );
839
  $form['form_textfield_test_title_after'] = array(
840
    '#type' => 'textfield',
841
    '#title' => t('Textfield test for title after element'),
842
    '#title_display' => 'after',
843
  );
844
  $form['form_textfield_test_title_invisible'] = array(
845
    '#type' => 'textfield',
846
    '#title' => t('Textfield test for invisible title'),
847
    '#title_display' => 'invisible',
848
  );
849
  // Textfield test for title set not to display.
850
  $form['form_textfield_test_title_no_show'] = array(
851
    '#type' => 'textfield',
852
  );
853
  // Checkboxes & radios with title as attribute.
854
  $form['form_checkboxes_title_attribute'] = array(
855
    '#type' => 'checkboxes',
856
    '#title' => 'Checkboxes test',
857
    '#options' => array(
858
      'first-checkbox' => 'First checkbox',
859
      'second-checkbox' => 'Second checkbox',
860
    ),
861
    '#title_display' => 'attribute',
862
    '#required' => TRUE,
863
  );
864
  $form['form_radios_title_attribute'] = array(
865
    '#type' => 'radios',
866
    '#title' => 'Radios test',
867
    '#options' => array(
868
      'first-radio' => 'First radio',
869
      'second-radio' => 'Second radio',
870
    ),
871
    '#title_display' => 'attribute',
872
    '#required' => TRUE,
873
  );
874

    
875
  return $form;
876
}
877

    
878
/**
879
 * Menu callback; Invokes a form builder function with a wrapper callback.
880
 */
881
function form_test_wrapper_callback($form_id) {
882
  $form_state = array(
883
    'build_info' => array('args' => array()),
884
    'wrapper_callback' => 'form_test_wrapper_callback_wrapper',
885
  );
886
  return drupal_build_form($form_id, $form_state);
887
}
888

    
889
/**
890
 * Form wrapper for form_test_wrapper_callback_form().
891
 */
892
function form_test_wrapper_callback_wrapper($form, &$form_state) {
893
  $form['wrapper'] = array('#markup' => 'Form wrapper callback element output.');
894
  return $form;
895
}
896

    
897
/**
898
 * Form builder for form wrapper callback test.
899
 */
900
function form_test_wrapper_callback_form($form, &$form_state) {
901
  $form['builder'] = array('#markup' => 'Form builder element output.');
902
  return $form;
903
}
904

    
905
/**
906
 * Form builder for form_state_values_clean() test.
907
 */
908
function form_test_form_state_values_clean_form($form, &$form_state) {
909
  // Build an example form containing multiple submit and button elements; not
910
  // only on the top-level.
911
  $form = array('#tree' => TRUE);
912
  $form['foo'] = array('#type' => 'submit', '#value' => t('Submit'));
913
  $form['bar'] = array('#type' => 'submit', '#value' => t('Submit'));
914
  $form['beer'] = array('#type' => 'value', '#value' => 1000);
915
  $form['baz']['foo'] = array('#type' => 'button', '#value' => t('Submit'));
916
  $form['baz']['baz'] = array('#type' => 'submit', '#value' => t('Submit'));
917
  $form['baz']['beer'] = array('#type' => 'value', '#value' => 2000);
918
  return $form;
919
}
920

    
921
/**
922
 * Form submit handler for form_state_values_clean() test form.
923
 */
924
function form_test_form_state_values_clean_form_submit($form, &$form_state) {
925
  form_state_values_clean($form_state);
926
  drupal_json_output($form_state['values']);
927
  exit;
928
}
929

    
930
/**
931
 * Form constructor for the form_state_values_clean() test.
932
 */
933
function form_test_form_state_values_clean_advanced_form($form, &$form_state) {
934
  // Build an example form containing a managed file and a submit form element.
935
  $form['image'] = array(
936
    '#type' => 'managed_file',
937
    '#title' => t('Image'),
938
    '#upload_location' => 'public://',
939
    '#default_value' => 0,
940
  );
941
  $form['submit'] = array(
942
    '#type' => 'submit',
943
    '#value' => t('Submit'),
944
  );
945
  return $form;
946
}
947

    
948
/**
949
 * Form submission handler for form_test_form_state_values_clean_advanced_form().
950
 */
951
function form_test_form_state_values_clean_advanced_form_submit($form, &$form_state) {
952
  form_state_values_clean($form_state);
953
  print t('You WIN!');
954
  exit;
955
}
956

    
957
/**
958
 * Build a form to test a checkbox.
959
 */
960
function _form_test_checkbox($form, &$form_state) {
961
  // A required checkbox.
962
  $form['required_checkbox'] = array(
963
    '#type' => 'checkbox',
964
    '#required' => TRUE,
965
    '#title' => 'required_checkbox',
966
  );
967

    
968
  // A disabled checkbox should get its default value back.
969
  $form['disabled_checkbox_on'] = array(
970
    '#type' => 'checkbox',
971
    '#disabled' => TRUE,
972
    '#return_value' => 'disabled_checkbox_on',
973
    '#default_value' => 'disabled_checkbox_on',
974
    '#title' => 'disabled_checkbox_on',
975
  );
976
  $form['disabled_checkbox_off'] = array(
977
    '#type' => 'checkbox',
978
    '#disabled' => TRUE,
979
    '#return_value' => 'disabled_checkbox_off',
980
    '#default_value' => NULL,
981
    '#title' => 'disabled_checkbox_off',
982
  );
983

    
984
  // A checkbox is active when #default_value == #return_value.
985
  $form['checkbox_on'] = array(
986
    '#type' => 'checkbox',
987
    '#return_value' => 'checkbox_on',
988
    '#default_value' => 'checkbox_on',
989
    '#title' => 'checkbox_on',
990
  );
991

    
992
  // But inactive in any other case.
993
  $form['checkbox_off'] = array(
994
    '#type' => 'checkbox',
995
    '#return_value' => 'checkbox_off',
996
    '#default_value' => 'checkbox_on',
997
    '#title' => 'checkbox_off',
998
  );
999

    
1000
  // Checkboxes with a #return_value of '0' are supported.
1001
  $form['zero_checkbox_on'] = array(
1002
    '#type' => 'checkbox',
1003
    '#return_value' => '0',
1004
    '#default_value' => '0',
1005
    '#title' => 'zero_checkbox_on',
1006
  );
1007

    
1008
  // In that case, passing a #default_value != '0' means that the checkbox is off.
1009
  $form['zero_checkbox_off'] = array(
1010
    '#type' => 'checkbox',
1011
    '#return_value' => '0',
1012
    '#default_value' => '1',
1013
    '#title' => 'zero_checkbox_off',
1014
  );
1015

    
1016
  $form['submit'] = array(
1017
    '#type' => 'submit',
1018
    '#value' => t('Submit')
1019
  );
1020

    
1021
  return $form;
1022
}
1023

    
1024
/**
1025
 * Return the form values via JSON.
1026
 */
1027
function _form_test_checkbox_submit($form, &$form_state) {
1028
  drupal_json_output($form_state['values']);
1029
  exit();
1030
}
1031

    
1032
/**
1033
 * Builds a form to test #type 'select' validation.
1034
 */
1035
function form_test_select($form, &$form_state) {
1036
  $base = array(
1037
    '#type' => 'select',
1038
    '#options' => drupal_map_assoc(array('one', 'two', 'three')),
1039
  );
1040

    
1041
  $form['select'] = $base + array(
1042
    '#title' => '#default_value one',
1043
    '#default_value' => 'one',
1044
  );
1045
  $form['select_required'] = $base + array(
1046
    '#title' => '#default_value one, #required',
1047
    '#required' => TRUE,
1048
    '#default_value' => 'one',
1049
  );
1050
  $form['select_optional'] = $base + array(
1051
    '#title' => '#default_value one, not #required',
1052
    '#required' => FALSE,
1053
    '#default_value' => 'one',
1054
  );
1055
  $form['empty_value'] = $base + array(
1056
    '#title' => '#default_value one, #required, #empty_value 0',
1057
    '#required' => TRUE,
1058
    '#default_value' => 'one',
1059
    '#empty_value' => 0,
1060
  );
1061
  $form['empty_value_one'] = $base + array(
1062
    '#title' => '#default_value = #empty_value, #required',
1063
    '#required' => TRUE,
1064
    '#default_value' => 'one',
1065
    '#empty_value' => 'one',
1066
  );
1067

    
1068
  $form['no_default'] = $base + array(
1069
    '#title' => 'No #default_value, #required',
1070
    '#required' => TRUE,
1071
  );
1072
  $form['no_default_optional'] = $base + array(
1073
    '#title' => 'No #default_value, not #required',
1074
    '#required' => FALSE,
1075
    '#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.',
1076
  );
1077
  $form['no_default_optional_empty_value'] = $base + array(
1078
    '#title' => 'No #default_value, not #required, #empty_value empty string',
1079
    '#empty_value' => '',
1080
    '#required' => FALSE,
1081
    '#description' => 'Should result in an empty string (due to #empty_value), because it is optional.',
1082
  );
1083

    
1084
  $form['no_default_empty_option'] = $base + array(
1085
    '#title' => 'No #default_value, #required, #empty_option',
1086
    '#required' => TRUE,
1087
    '#empty_option' => '- Choose -',
1088
  );
1089
  $form['no_default_empty_option_optional'] = $base + array(
1090
    '#title' => 'No #default_value, not #required, #empty_option',
1091
    '#empty_option' => '- Dismiss -',
1092
    '#description' => 'Should result in an empty string (default of #empty_value), because it is optional.',
1093
  );
1094

    
1095
  $form['no_default_empty_value'] = $base + array(
1096
    '#title' => 'No #default_value, #required, #empty_value 0',
1097
    '#required' => TRUE,
1098
    '#empty_value' => 0,
1099
    '#description' => 'Should never result in 0.',
1100
  );
1101
  $form['no_default_empty_value_one'] = $base + array(
1102
    '#title' => 'No #default_value, #required, #empty_value one',
1103
    '#required' => TRUE,
1104
    '#empty_value' => 'one',
1105
    '#description' => 'A mistakenly assigned #empty_value contained in #options should not be valid.',
1106
  );
1107
  $form['no_default_empty_value_optional'] = $base + array(
1108
    '#title' => 'No #default_value, not #required, #empty_value 0',
1109
    '#required' => FALSE,
1110
    '#empty_value' => 0,
1111
    '#description' => 'Should result in 0, because it is optional.',
1112
  );
1113

    
1114
  $form['multiple'] = $base + array(
1115
    '#title' => '#multiple, #default_value two',
1116
    '#default_value' => array('two'),
1117
    '#multiple' => TRUE,
1118
  );
1119
  $form['multiple_no_default'] = $base + array(
1120
    '#title' => '#multiple, no #default_value',
1121
    '#multiple' => TRUE,
1122
  );
1123
  $form['multiple_no_default_required'] = $base + array(
1124
    '#title' => '#multiple, #required, no #default_value',
1125
    '#required' => TRUE,
1126
    '#multiple' => TRUE,
1127
  );
1128

    
1129
  $form['submit'] = array('#type' => 'submit', '#value' => 'Submit');
1130
  return $form;
1131
}
1132

    
1133
/**
1134
 * Form submit handler for form_test_select().
1135
 */
1136
function form_test_select_submit($form, &$form_state) {
1137
  drupal_json_output($form_state['values']);
1138
  exit();
1139
}
1140

    
1141
/**
1142
 * Form constructor to test expansion of #type checkboxes and radios.
1143
 */
1144
function form_test_checkboxes_radios($form, &$form_state, $customize = FALSE) {
1145
  $form['#submit'] = array('_form_test_submit_values_json');
1146

    
1147
  // Expand #type checkboxes, setting custom element properties for some but not
1148
  // all options.
1149
  $form['checkboxes'] = array(
1150
    '#type' => 'checkboxes',
1151
    '#title' => 'Checkboxes',
1152
    '#options' => array(
1153
      0 => 'Zero',
1154
      'foo' => 'Foo',
1155
      1 => 'One',
1156
      'bar' => 'Bar',
1157
      '>' => 'Special Char',
1158
    ),
1159
  );
1160
  if ($customize) {
1161
    $form['checkboxes'] += array(
1162
      'foo' => array(
1163
        '#description' => 'Enable to foo.',
1164
      ),
1165
      1 => array(
1166
        '#weight' => 10,
1167
      ),
1168
    );
1169
  }
1170

    
1171
  // Expand #type radios, setting custom element properties for some but not
1172
  // all options.
1173
  $form['radios'] = array(
1174
    '#type' => 'radios',
1175
    '#title' => 'Radios',
1176
    '#options' => array(
1177
      0 => 'Zero',
1178
      'foo' => 'Foo',
1179
      1 => 'One',
1180
      'bar' => 'Bar',
1181
      '>' => 'Special Char',
1182
    ),
1183
  );
1184
  if ($customize) {
1185
    $form['radios'] += array(
1186
      'foo' => array(
1187
        '#description' => 'Enable to foo.',
1188
      ),
1189
      1 => array(
1190
        '#weight' => 10,
1191
      ),
1192
    );
1193
  }
1194

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

    
1197
  return $form;
1198
}
1199

    
1200
/**
1201
 * Build a form to test disabled elements.
1202
 */
1203
function _form_test_disabled_elements($form, &$form_state) {
1204
  // Elements that take a simple default value.
1205
  foreach (array('textfield', 'textarea', 'hidden') as $type) {
1206
    $form[$type] = array(
1207
      '#type' => $type,
1208
      '#title' => $type,
1209
      '#default_value' => $type,
1210
      '#test_hijack_value' => 'HIJACK',
1211
      '#disabled' => TRUE,
1212
    );
1213
  }
1214

    
1215
  // Multiple values option elements.
1216
  foreach (array('checkboxes', 'select') as $type) {
1217
    $form[$type . '_multiple'] = array(
1218
      '#type' => $type,
1219
      '#title' => $type . ' (multiple)',
1220
      '#options' => array(
1221
        'test_1' => 'Test 1',
1222
        'test_2' => 'Test 2',
1223
      ),
1224
      '#multiple' => TRUE,
1225
      '#default_value' => array('test_2' => 'test_2'),
1226
      // The keys of #test_hijack_value need to match the #name of the control.
1227
      // @see FormsTestCase::testDisabledElements()
1228
      '#test_hijack_value' => $type == 'select' ? array('' => 'test_1') : array('test_1' => 'test_1'),
1229
      '#disabled' => TRUE,
1230
    );
1231
  }
1232

    
1233
  // Single values option elements.
1234
  foreach (array('radios', 'select') as $type) {
1235
    $form[$type . '_single'] = array(
1236
      '#type' => $type,
1237
      '#title' => $type . ' (single)',
1238
      '#options' => array(
1239
        'test_1' => 'Test 1',
1240
        'test_2' => 'Test 2',
1241
      ),
1242
      '#multiple' => FALSE,
1243
      '#default_value' => 'test_2',
1244
      '#test_hijack_value' => 'test_1',
1245
      '#disabled' => TRUE,
1246
    );
1247
  }
1248

    
1249
  // Checkbox and radio.
1250
  foreach (array('checkbox', 'radio') as $type) {
1251
    $form[$type . '_unchecked'] = array(
1252
      '#type' => $type,
1253
      '#title' => $type . ' (unchecked)',
1254
      '#return_value' => 1,
1255
      '#default_value' => 0,
1256
      '#test_hijack_value' => 1,
1257
      '#disabled' => TRUE,
1258
    );
1259
    $form[$type . '_checked'] = array(
1260
      '#type' => $type,
1261
      '#title' => $type . ' (checked)',
1262
      '#return_value' => 1,
1263
      '#default_value' => 1,
1264
      '#test_hijack_value' => NULL,
1265
      '#disabled' => TRUE,
1266
    );
1267
  }
1268

    
1269
  // Weight.
1270
  $form['weight'] = array(
1271
    '#type' => 'weight',
1272
    '#title' => 'weight',
1273
    '#default_value' => 10,
1274
    '#test_hijack_value' => 5,
1275
    '#disabled' => TRUE,
1276
  );
1277

    
1278
  // Date.
1279
  $form['date'] = array(
1280
    '#type' => 'date',
1281
    '#title' => 'date',
1282
    '#disabled' => TRUE,
1283
    '#default_value' => array(
1284
      'day' => 19,
1285
      'month' => 11,
1286
      'year' => 1978,
1287
    ),
1288
    '#test_hijack_value' => array(
1289
      'day' => 20,
1290
      'month' => 12,
1291
      'year' => 1979,
1292
    ),
1293
  );
1294

    
1295
  // The #disabled state should propagate to children.
1296
  $form['disabled_container'] = array(
1297
    '#disabled' => TRUE,
1298
  );
1299
  foreach (array('textfield', 'textarea', 'hidden') as $type) {
1300
    $form['disabled_container']['disabled_container_' . $type] = array(
1301
      '#type' => $type,
1302
      '#title' => $type,
1303
      '#default_value' => $type,
1304
      '#test_hijack_value' => 'HIJACK',
1305
    );
1306
  }
1307

    
1308
  // Text format.
1309
  $form['text_format'] = array(
1310
    '#type' => 'text_format',
1311
    '#title' => 'Text format',
1312
    '#disabled' => TRUE,
1313
    '#default_value' => 'Text value',
1314
    '#format' => 'plain_text',
1315
    '#expected_value' => array(
1316
      'value' => 'Text value',
1317
      'format' => 'plain_text',
1318
    ),
1319
    '#test_hijack_value' => array(
1320
      'value' => 'HIJACK',
1321
      'format' => 'filtered_html',
1322
    ),
1323
  );
1324

    
1325
  // Password fields.
1326
  $form['password'] = array(
1327
    '#type' => 'password',
1328
    '#title' => 'Password',
1329
    '#disabled' => TRUE,
1330
  );
1331
  $form['password_confirm'] = array(
1332
    '#type' => 'password_confirm',
1333
    '#title' => 'Password confirm',
1334
    '#disabled' => TRUE,
1335
  );
1336

    
1337
  // Files.
1338
  $form['file'] = array(
1339
    '#type' => 'file',
1340
    '#title' => 'File',
1341
    '#disabled' => TRUE,
1342
  );
1343
  $form['managed_file'] = array(
1344
    '#type' => 'managed_file',
1345
    '#title' => 'Managed file',
1346
    '#disabled' => TRUE,
1347
  );
1348

    
1349
  // Buttons.
1350
  $form['image_button'] = array(
1351
    '#type' => 'image_button',
1352
    '#value' => 'Image button',
1353
    '#disabled' => TRUE,
1354
  );
1355
  $form['button'] = array(
1356
    '#type' => 'button',
1357
    '#value' => 'Button',
1358
    '#disabled' => TRUE,
1359
  );
1360
  $form['submit_disabled'] = array(
1361
    '#type' => 'submit',
1362
    '#value' => 'Submit',
1363
    '#disabled' => TRUE,
1364
  );
1365

    
1366
  $form['submit'] = array(
1367
    '#type' => 'submit',
1368
    '#value' => t('Submit'),
1369
  );
1370

    
1371
  return $form;
1372
}
1373

    
1374
/**
1375
 * Return the form values via JSON.
1376
 */
1377
function _form_test_disabled_elements_submit($form, &$form_state) {
1378
  drupal_json_output($form_state['values']);
1379
  exit();
1380
}
1381

    
1382
/**
1383
 * Build a form to test input forgery of enabled elements.
1384
 */
1385
function _form_test_input_forgery($form, &$form_state) {
1386
  // For testing that a user can't submit a value not matching one of the
1387
  // allowed options.
1388
  $form['checkboxes'] = array(
1389
    '#type' => 'checkboxes',
1390
    '#options' => array(
1391
      'one' => 'One',
1392
      'two' => 'Two',
1393
    ),
1394
  );
1395

    
1396
  $form['submit'] = array(
1397
    '#type' => 'submit',
1398
    '#value' => t('Submit'),
1399
  );
1400
  return $form;
1401
}
1402

    
1403
/**
1404
 * Return the form values via JSON.
1405
 */
1406
function _form_test_input_forgery_submit($form, &$form_state) {
1407
  drupal_json_output($form_state['values']);
1408
  exit();
1409
}
1410

    
1411
/**
1412
 * Form builder for testing preservation of values during a rebuild.
1413
 */
1414
function form_test_form_rebuild_preserve_values_form($form, &$form_state) {
1415
  // Start the form with two checkboxes, to test different defaults, and a
1416
  // textfield, to test more than one element type.
1417
  $form = array(
1418
    'checkbox_1_default_off' => array(
1419
      '#type' => 'checkbox',
1420
      '#title' => t('This checkbox defaults to unchecked.'),
1421
      '#default_value' => FALSE,
1422
    ),
1423
    'checkbox_1_default_on' => array(
1424
      '#type' => 'checkbox',
1425
      '#title' => t('This checkbox defaults to checked.'),
1426
      '#default_value' => TRUE,
1427
    ),
1428
    'text_1' => array(
1429
      '#type' => 'textfield',
1430
      '#title' => t('This textfield has a non-empty default value.'),
1431
      '#default_value' => 'DEFAULT 1',
1432
    ),
1433
  );
1434
  // Provide an 'add more' button that rebuilds the form with an additional two
1435
  // checkboxes and a textfield. The test is to make sure that the rebuild
1436
  // triggered by this button preserves the user input values for the initial
1437
  // elements and initializes the new elements with the correct default values.
1438
  if (empty($form_state['storage']['add_more'])) {
1439
    $form['add_more'] = array(
1440
      '#type' => 'submit',
1441
      '#value' => 'Add more',
1442
      '#submit' => array('form_test_form_rebuild_preserve_values_form_add_more'),
1443
    );
1444
  }
1445
  else {
1446
    $form += array(
1447
      'checkbox_2_default_off' => array(
1448
        '#type' => 'checkbox',
1449
        '#title' => t('This checkbox defaults to unchecked.'),
1450
        '#default_value' => FALSE,
1451
      ),
1452
      'checkbox_2_default_on' => array(
1453
        '#type' => 'checkbox',
1454
        '#title' => t('This checkbox defaults to checked.'),
1455
        '#default_value' => TRUE,
1456
      ),
1457
      'text_2' => array(
1458
        '#type' => 'textfield',
1459
        '#title' => t('This textfield has a non-empty default value.'),
1460
        '#default_value' => 'DEFAULT 2',
1461
      ),
1462
    );
1463
  }
1464
  // A submit button that finishes the form workflow (does not rebuild).
1465
  $form['submit'] = array(
1466
    '#type' => 'submit',
1467
    '#value' => 'Submit',
1468
  );
1469
  return $form;
1470
}
1471

    
1472
/**
1473
 * Button submit handler for form_test_form_rebuild_preserve_values_form().
1474
 */
1475
function form_test_form_rebuild_preserve_values_form_add_more($form, &$form_state) {
1476
  // Rebuild, to test preservation of input values.
1477
  $form_state['storage']['add_more'] = TRUE;
1478
  $form_state['rebuild'] = TRUE;
1479
}
1480

    
1481
/**
1482
 * Form submit handler for form_test_form_rebuild_preserve_values_form().
1483
 */
1484
function form_test_form_rebuild_preserve_values_form_submit($form, &$form_state) {
1485
  // Finish the workflow. Do not rebuild.
1486
  drupal_set_message(t('Form values: %values', array('%values' => var_export($form_state['values'], TRUE))));
1487
}
1488

    
1489
/**
1490
 * Form constructor for testing form state persistence.
1491
 */
1492
function form_test_state_persist($form, &$form_state) {
1493
  $form['title'] = array(
1494
    '#type' => 'textfield',
1495
    '#title' => 'title',
1496
    '#default_value' => 'DEFAULT',
1497
    '#required' => TRUE,
1498
  );
1499
  $form_state['value'] = 'State persisted.';
1500

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

    
1508
/**
1509
 * Submit handler.
1510
 *
1511
 * @see form_test_state_persist()
1512
 */
1513
function form_test_state_persist_submit($form, &$form_state) {
1514
  drupal_set_message($form_state['value']);
1515
  $form_state['rebuild'] = TRUE;
1516
}
1517

    
1518
/**
1519
 * Implements hook_form_FORM_ID_alter().
1520
 *
1521
 * @see form_test_state_persist()
1522
 */
1523
function form_test_form_form_test_state_persist_alter(&$form, &$form_state) {
1524
  // Simulate a form alter implementation inserting form elements that enable
1525
  // caching of the form, e.g. elements having #ajax.
1526
  if (!empty($_REQUEST['cache'])) {
1527
    $form_state['cache'] = TRUE;
1528
  }
1529
}
1530

    
1531
/**
1532
 * Form builder to test programmatic form submissions.
1533
 */
1534
function form_test_programmatic_form($form, &$form_state) {
1535
  $form['textfield'] = array(
1536
    '#title' => 'Textfield',
1537
    '#type' => 'textfield',
1538
  );
1539

    
1540
  $form['checkboxes'] = array(
1541
    '#type' => 'checkboxes',
1542
    '#options' => array(
1543
      1 => 'First checkbox',
1544
      2 => 'Second checkbox',
1545
    ),
1546
    // Both checkboxes are selected by default so that we can test the ability
1547
    // of programmatic form submissions to uncheck them.
1548
    '#default_value' => array(1, 2),
1549
  );
1550

    
1551
  // This is used to test that programmatic form submissions can bypass #access
1552
  // restrictions.
1553
  $form['textfield_no_access'] = array(
1554
    '#type' => 'textfield',
1555
    '#title' => 'Textfield no access',
1556
    '#default_value' => 'default value',
1557
    '#access' => FALSE,
1558
  );
1559

    
1560
  $form['field_to_validate'] = array(
1561
    '#type' => 'radios',
1562
    '#title' => 'Field to validate (in the case of limited validation)',
1563
    '#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.',
1564
    '#options' => array(
1565
      'all' => 'Validate all fields',
1566
      'textfield' => 'Validate the "Textfield" field',
1567
      'field_to_validate' => 'Validate the "Field to validate" field',
1568
    ),
1569
    '#default_value' => 'all',
1570
  );
1571

    
1572
  // The main submit button for the form.
1573
  $form['submit'] = array(
1574
    '#type' => 'submit',
1575
    '#value' => 'Submit',
1576
  );
1577
  // A secondary submit button that allows validation to be limited based on
1578
  // the value of the above radio selector.
1579
  $form['submit_limit_validation'] = array(
1580
    '#type' => 'submit',
1581
    '#value' => 'Submit with limited validation',
1582
    // Use the same submit handler for this button as for the form itself.
1583
    // (This must be set explicitly or otherwise the form API will ignore the
1584
    // #limit_validation_errors property.)
1585
    '#submit' => array('form_test_programmatic_form_submit'),
1586
  );
1587
  if (!empty($form_state['input']['field_to_validate']) && $form_state['input']['field_to_validate'] != 'all') {
1588
    $form['submit_limit_validation']['#limit_validation_errors'] = array(
1589
      array($form_state['input']['field_to_validate']),
1590
    );
1591
  }
1592

    
1593
  return $form;
1594
}
1595

    
1596
/**
1597
 * Form validation handler for programmatic form submissions.
1598
 *
1599
 * To test that the validation handler is correctly executed, the field value is
1600
 * explicitly required here.
1601
 */
1602
function form_test_programmatic_form_validate($form, &$form_state) {
1603
  if (empty($form_state['values']['textfield'])) {
1604
    form_set_error('textfield', t('Textfield is required.'));
1605
  }
1606
}
1607

    
1608
/**
1609
 * Form submit handler for programmatic form submissions.
1610
 *
1611
 * To test that the submission handler is correctly executed, we store the
1612
 * submitted values in a place we can access from the caller context.
1613
 */
1614
function form_test_programmatic_form_submit($form, &$form_state) {
1615
  $form_state['storage']['programmatic_form_submit'] = $form_state['values'];
1616
}
1617

    
1618
/**
1619
 * Form builder to test button click detection.
1620
 */
1621
function form_test_clicked_button($form, &$form_state) {
1622
  // A single text field. In IE, when a form has only one non-button input field
1623
  // and the ENTER key is pressed while that field has focus, the form is
1624
  // submitted without any information identifying the button responsible for
1625
  // the submission. In other browsers, the form is submitted as though the
1626
  // first button were clicked.
1627
  $form['text'] = array(
1628
    '#title' => 'Text',
1629
    '#type' => 'textfield',
1630
  );
1631

    
1632
  // Loop through each path argument, addding buttons based on the information
1633
  // in the argument. For example, if the path is
1634
  // form-test/clicked-button/s/i/rb, then 3 buttons are added: a 'submit', an
1635
  // 'image_button', and a 'button' with #access=FALSE. This enables form.test
1636
  // to test a variety of combinations.
1637
  $i=0;
1638
  $args = array_slice(arg(), 2);
1639
  foreach ($args as $arg) {
1640
    $name = 'button' . ++$i;
1641
    // 's', 'b', or 'i' in the argument define the button type wanted.
1642
    if (strpos($arg, 's') !== FALSE) {
1643
      $type = 'submit';
1644
    }
1645
    elseif (strpos($arg, 'b') !== FALSE) {
1646
      $type = 'button';
1647
    }
1648
    elseif (strpos($arg, 'i') !== FALSE) {
1649
      $type = 'image_button';
1650
    }
1651
    else {
1652
      $type = NULL;
1653
    }
1654
    if (isset($type)) {
1655
      $form[$name] = array(
1656
        '#type' => $type,
1657
        '#name' => $name,
1658
      );
1659
      // Image buttons need a #src; the others need a #value.
1660
      if ($type == 'image_button') {
1661
        $form[$name]['#src'] = 'misc/druplicon.png';
1662
      }
1663
      else {
1664
        $form[$name]['#value'] = $name;
1665
      }
1666
      // 'r' for restricted, so we can test that button click detection code
1667
      // correctly takes #access security into account.
1668
      if (strpos($arg, 'r') !== FALSE) {
1669
        $form[$name]['#access'] = FALSE;
1670
      }
1671
    }
1672
  }
1673

    
1674
  return $form;
1675
}
1676

    
1677
/**
1678
 * Form validation handler for the form_test_clicked_button() form.
1679
 */
1680
function form_test_clicked_button_validate($form, &$form_state) {
1681
  if (isset($form_state['triggering_element'])) {
1682
    drupal_set_message(t('The clicked button is %name.', array('%name' => $form_state['triggering_element']['#name'])));
1683
  }
1684
  else {
1685
    drupal_set_message('There is no clicked button.');
1686
  }
1687
}
1688

    
1689
/**
1690
 * Form submit handler for the form_test_clicked_button() form.
1691
 */
1692
function form_test_clicked_button_submit($form, &$form_state) {
1693
  drupal_set_message('Submit handler for form_test_clicked_button executed.');
1694
}
1695

    
1696
/**
1697
 * Form builder to detect form redirect.
1698
 */
1699
function form_test_redirect($form, &$form_state) {
1700
  $form['redirection'] = array(
1701
    '#type' => 'checkbox',
1702
    '#title' => t('Use redirection'),
1703
  );
1704
  $form['destination'] = array(
1705
    '#type' => 'textfield',
1706
    '#title' => t('Redirect destination'),
1707
    '#states' => array(
1708
      'visible' => array(
1709
        ':input[name="redirection"]' => array('checked' => TRUE),
1710
      ),
1711
    ),
1712
  );
1713
  $form['submit'] = array(
1714
    '#type' => 'submit',
1715
    '#value' => t('Submit'),
1716
  );
1717

    
1718
  return $form;
1719
}
1720

    
1721
/**
1722
 * Form submit handler to test different redirect behaviours.
1723
 */
1724
function form_test_redirect_submit(&$form, &$form_state) {
1725
  if (!empty($form_state['values']['redirection'])) {
1726
    $form_state['redirect'] = !empty($form_state['values']['destination']) ? $form_state['values']['destination'] : NULL;
1727
  }
1728
  else {
1729
    $form_state['redirect'] = FALSE;
1730
  }
1731
}
1732

    
1733
/**
1734
 * Implements hook_form_FORM_ID_alter() for the registration form.
1735
 */
1736
function form_test_form_user_register_form_alter(&$form, &$form_state) {
1737
  $form['test_rebuild'] = array(
1738
    '#type' => 'submit',
1739
    '#value' => t('Rebuild'),
1740
    '#submit' => array('form_test_user_register_form_rebuild'),
1741
  );
1742
  // If requested, add the test field by attaching the node page form.
1743
  if (!empty($_REQUEST['field'])) {
1744
    $node = (object)array('type' => 'page');
1745
    field_attach_form('node', $node, $form, $form_state);
1746
  }
1747
}
1748

    
1749
/**
1750
 * Submit callback that just lets the form rebuild.
1751
 */
1752
function form_test_user_register_form_rebuild($form, &$form_state) {
1753
  drupal_set_message('Form rebuilt.');
1754
  $form_state['rebuild'] = TRUE;
1755
}
1756

    
1757
/**
1758
 * Menu callback that returns two instances of the node form.
1759
 */
1760
function form_test_two_instances() {
1761
  global $user;
1762
  $node1 = (object) array(
1763
    'uid' => $user->uid,
1764
    'name' => (isset($user->name) ? $user->name : ''),
1765
    'type' => 'page',
1766
    'language' => LANGUAGE_NONE,
1767
  );
1768
  $node2 = clone($node1);
1769
  $return['node_form_1'] = drupal_get_form('page_node_form', $node1);
1770
  $return['node_form_2'] = drupal_get_form('page_node_form', $node2);
1771
  return $return;
1772
}
1773

    
1774
/**
1775
 * Menu callback for testing custom form includes.
1776
 */
1777
function form_test_load_include_custom($form, &$form_state) {
1778
  $form['button'] = array(
1779
    '#type' => 'submit',
1780
    '#value' => t('Save'),
1781
    '#submit' => array('form_test_load_include_submit'),
1782
  );
1783
  // Specify the include file and enable form caching. That way the form is
1784
  // cached when it is submitted, but needs to find the specified submit handler
1785
  // in the include.
1786
  // Filename is a bit weird here: modules/simpletest/tests/form_test.file.inc
1787
  form_load_include($form_state, 'inc', 'form_test', 'form_test.file');
1788
  $form_state['cache'] = TRUE;
1789
  return $form;
1790
}
1791

    
1792
function form_test_checkbox_type_juggling($form, $form_state, $default_value, $return_value) {
1793
  $form['checkbox'] = array(
1794
    '#type' => 'checkbox',
1795
    '#return_value' => $return_value,
1796
    '#default_value' => $default_value,
1797
  );
1798
  return $form;
1799
}
1800

    
1801
function form_test_checkboxes_zero($form, &$form_state, $json = TRUE) {
1802
  $form['checkbox_off'] = array(
1803
    '#type' => 'checkboxes',
1804
    '#options' => array('foo', 'bar', 'baz'),
1805
  );
1806
  $form['checkbox_zero_default'] = array(
1807
    '#type' => 'checkboxes',
1808
    '#options' => array('foo', 'bar', 'baz'),
1809
    '#default_value' => array(0),
1810
  );
1811
  $form['checkbox_string_zero_default'] = array(
1812
    '#type' => 'checkboxes',
1813
    '#options' => array('foo', 'bar', 'baz'),
1814
    '#default_value' => array('0'),
1815
  );
1816
  $form['submit'] = array(
1817
    '#type' => 'submit',
1818
    '#value' => 'Save',
1819
  );
1820
  if ($json) {
1821
    $form['#submit'][] = '_form_test_checkbox_submit';
1822
  }
1823
  else {
1824
    $form['#submit'][] = '_form_test_checkboxes_zero_no_redirect';
1825
  }
1826
  return $form;
1827
}
1828

    
1829
function _form_test_checkboxes_zero_no_redirect($form, &$form_state) {
1830
  $form_state['redirect'] = FALSE;
1831
}
1832

    
1833
/**
1834
 * Menu callback returns two instances of the same form.
1835
 */
1836
function form_test_double_form() {
1837
  return array(
1838
    'form1' => drupal_get_form('form_test_html_id'),
1839
    'form2' => drupal_get_form('form_test_html_id'),
1840
  );
1841
}
1842

    
1843
/**
1844
 * Builds a simple form to test duplicate HTML IDs.
1845
 */
1846
function form_test_html_id($form, &$form_state) {
1847
  $form['name'] = array(
1848
    '#type' => 'textfield',
1849
    '#title' => 'name',
1850
    '#required' => TRUE,
1851
  );
1852
  $form['submit'] = array(
1853
    '#type' => 'submit',
1854
    '#value' => 'Save',
1855
  );
1856
  return $form;
1857
}