form_test.module 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848
  1. <?php
  2. /**
  3. * @file
  4. * Helper module for the form API tests.
  5. */
  6. /**
  7. * Implements hook_menu().
  8. */
  9. function form_test_menu() {
  10. $items['form-test/alter'] = array(
  11. 'title' => 'Form altering test',
  12. 'page callback' => 'drupal_get_form',
  13. 'page arguments' => array('form_test_alter_form'),
  14. 'access arguments' => array('access content'),
  15. 'type' => MENU_CALLBACK,
  16. );
  17. $items['form-test/validate'] = array(
  18. 'title' => 'Form validation handlers test',
  19. 'page callback' => 'drupal_get_form',
  20. 'page arguments' => array('form_test_validate_form'),
  21. 'access arguments' => array('access content'),
  22. 'type' => MENU_CALLBACK,
  23. );
  24. $items['form-test/validate-required'] = array(
  25. 'title' => 'Form #required validation',
  26. 'page callback' => 'drupal_get_form',
  27. 'page arguments' => array('form_test_validate_required_form'),
  28. 'access callback' => TRUE,
  29. 'type' => MENU_CALLBACK,
  30. );
  31. $items['form-test/validate-required-no-title'] = array(
  32. 'title' => 'Form #required validation without #title',
  33. 'page callback' => 'drupal_get_form',
  34. 'page arguments' => array('form_test_validate_required_form_no_title'),
  35. 'access callback' => TRUE,
  36. 'type' => MENU_CALLBACK,
  37. );
  38. $items['form-test/limit-validation-errors'] = array(
  39. 'title' => 'Form validation with some error suppression',
  40. 'page callback' => 'drupal_get_form',
  41. 'page arguments' => array('form_test_limit_validation_errors_form'),
  42. 'access arguments' => array('access content'),
  43. 'type' => MENU_CALLBACK,
  44. );
  45. $items['form_test/tableselect/multiple-true'] = array(
  46. 'title' => 'Tableselect checkboxes test',
  47. 'page callback' => 'drupal_get_form',
  48. 'page arguments' => array('_form_test_tableselect_multiple_true_form'),
  49. 'access arguments' => array('access content'),
  50. 'type' => MENU_CALLBACK,
  51. );
  52. $items['form_test/tableselect/multiple-false'] = array(
  53. 'title' => 'Tableselect radio button test',
  54. 'page callback' => 'drupal_get_form',
  55. 'page arguments' => array('_form_test_tableselect_multiple_false_form'),
  56. 'access arguments' => array('access content'),
  57. 'type' => MENU_CALLBACK,
  58. );
  59. $items['form_test/tableselect/empty-text'] = array(
  60. 'title' => 'Tableselect empty text test',
  61. 'page callback' => 'drupal_get_form',
  62. 'page arguments' => array('_form_test_tableselect_empty_form'),
  63. 'access arguments' => array('access content'),
  64. 'type' => MENU_CALLBACK,
  65. );
  66. $items['form_test/tableselect/advanced-select'] = array(
  67. 'title' => 'Tableselect js_select tests',
  68. 'page callback' => 'drupal_get_form',
  69. 'page arguments' => array('_form_test_tableselect_js_select_form'),
  70. 'access arguments' => array('access content'),
  71. 'type' => MENU_CALLBACK,
  72. );
  73. $items['form_test/vertical-tabs'] = array(
  74. 'title' => 'Vertical tabs tests',
  75. 'page callback' => 'drupal_get_form',
  76. 'page arguments' => array('_form_test_vertical_tabs_form'),
  77. 'access arguments' => array('access content'),
  78. 'type' => MENU_CALLBACK,
  79. );
  80. $items['form_test/form-storage'] = array(
  81. 'title' => 'Form storage test',
  82. 'page callback' => 'drupal_get_form',
  83. 'page arguments' => array('form_test_storage_form'),
  84. 'access arguments' => array('access content'),
  85. 'type' => MENU_CALLBACK,
  86. );
  87. $items['form_test/wrapper-callback'] = array(
  88. 'title' => 'Form wrapper callback test',
  89. 'page callback' => 'form_test_wrapper_callback',
  90. 'page arguments' => array('form_test_wrapper_callback_form'),
  91. 'access arguments' => array('access content'),
  92. 'type' => MENU_CALLBACK,
  93. );
  94. $items['form_test/form-state-values-clean'] = array(
  95. 'title' => 'Form state values clearance test',
  96. 'page callback' => 'drupal_get_form',
  97. 'page arguments' => array('form_test_form_state_values_clean_form'),
  98. 'access arguments' => array('access content'),
  99. 'type' => MENU_CALLBACK,
  100. );
  101. $items['form_test/form-state-values-clean-advanced'] = array(
  102. 'title' => 'Form state values clearance advanced test',
  103. 'page callback' => 'drupal_get_form',
  104. 'page arguments' => array('form_test_form_state_values_clean_advanced_form'),
  105. 'access arguments' => array('access content'),
  106. 'type' => MENU_CALLBACK,
  107. );
  108. $items['form-test/checkbox'] = array(
  109. 'title' => t('Form test'),
  110. 'page callback' => 'drupal_get_form',
  111. 'page arguments' => array('_form_test_checkbox'),
  112. 'access callback' => TRUE,
  113. 'type' => MENU_CALLBACK,
  114. );
  115. $items['form-test/select'] = array(
  116. 'title' => t('Select'),
  117. 'page callback' => 'drupal_get_form',
  118. 'page arguments' => array('form_test_select'),
  119. 'access callback' => TRUE,
  120. );
  121. $items['form-test/checkboxes-radios'] = array(
  122. 'title' => t('Checkboxes, Radios'),
  123. 'page callback' => 'drupal_get_form',
  124. 'page arguments' => array('form_test_checkboxes_radios'),
  125. 'access callback' => TRUE,
  126. );
  127. $items['form-test/disabled-elements'] = array(
  128. 'title' => t('Form test'),
  129. 'page callback' => 'drupal_get_form',
  130. 'page arguments' => array('_form_test_disabled_elements'),
  131. 'access callback' => TRUE,
  132. 'type' => MENU_CALLBACK,
  133. );
  134. $items['form-test/input-forgery'] = array(
  135. 'title' => t('Form test'),
  136. 'page callback' => 'drupal_get_form',
  137. 'page arguments' => array('_form_test_input_forgery'),
  138. 'access callback' => TRUE,
  139. 'type' => MENU_CALLBACK,
  140. );
  141. $items['form-test/form-rebuild-preserve-values'] = array(
  142. 'title' => 'Form values preservation during rebuild test',
  143. 'page callback' => 'drupal_get_form',
  144. 'page arguments' => array('form_test_form_rebuild_preserve_values_form'),
  145. 'access arguments' => array('access content'),
  146. 'type' => MENU_CALLBACK,
  147. );
  148. $items['form-test/redirect'] = array(
  149. 'title' => 'Redirect test',
  150. 'page callback' => 'drupal_get_form',
  151. 'page arguments' => array('form_test_redirect'),
  152. 'access callback' => TRUE,
  153. 'type' => MENU_CALLBACK,
  154. );
  155. $items['form_test/form-labels'] = array(
  156. 'title' => 'Form label test',
  157. 'page callback' => 'drupal_get_form',
  158. 'page arguments' => array('form_label_test_form'),
  159. 'access arguments' => array('access content'),
  160. 'type' => MENU_CALLBACK,
  161. );
  162. $items['form-test/state-persist'] = array(
  163. 'title' => 'Form state persistence without storage',
  164. 'page callback' => 'drupal_get_form',
  165. 'page arguments' => array('form_test_state_persist'),
  166. 'access callback' => TRUE,
  167. 'type' => MENU_CALLBACK,
  168. );
  169. $items['form-test/clicked-button'] = array(
  170. 'title' => 'Clicked button test',
  171. 'page callback' => 'drupal_get_form',
  172. 'page arguments' => array('form_test_clicked_button'),
  173. 'access callback' => TRUE,
  174. 'type' => MENU_CALLBACK,
  175. );
  176. if (module_exists('node')) {
  177. $items['form-test/two-instances-of-same-form'] = array(
  178. 'title' => 'AJAX test with two form instances',
  179. 'page callback' => 'form_test_two_instances',
  180. 'access callback' => 'node_access',
  181. 'access arguments' => array('create', 'page'),
  182. 'file path' => drupal_get_path('module', 'node'),
  183. 'file' => 'node.pages.inc',
  184. 'type' => MENU_CALLBACK,
  185. );
  186. }
  187. $items['form-test/double-form'] = array(
  188. 'title' => 'Double form test',
  189. 'page callback' => 'form_test_double_form',
  190. 'access callback' => TRUE,
  191. 'type' => MENU_CALLBACK,
  192. );
  193. $items['form-test/load-include-menu'] = array(
  194. 'title' => 'FAPI test loading includes',
  195. 'page callback' => 'drupal_get_form',
  196. 'page arguments' => array('form_test_load_include_menu'),
  197. 'access callback' => TRUE,
  198. 'file' => 'form_test.file.inc',
  199. 'type' => MENU_CALLBACK,
  200. );
  201. $items['form-test/load-include-custom'] = array(
  202. 'title' => 'FAPI test loading includes',
  203. 'page callback' => 'drupal_get_form',
  204. 'page arguments' => array('form_test_load_include_custom'),
  205. 'access callback' => TRUE,
  206. 'type' => MENU_CALLBACK,
  207. );
  208. $items['form-test/checkboxes-zero'] = array(
  209. 'title' => 'FAPI test involving checkboxes and zero',
  210. 'page callback' => 'drupal_get_form',
  211. 'page arguments' => array('form_test_checkboxes_zero'),
  212. 'access callback' => TRUE,
  213. 'type' => MENU_CALLBACK,
  214. );
  215. return $items;
  216. }
  217. /**
  218. * Form submit handler to return form values as JSON.
  219. */
  220. function _form_test_submit_values_json($form, &$form_state) {
  221. drupal_json_output($form_state['values']);
  222. drupal_exit();
  223. }
  224. /**
  225. * Form builder for testing hook_form_alter() and hook_form_FORM_ID_alter().
  226. */
  227. function form_test_alter_form($form, &$form_state) {
  228. // Elements can be added as needed for future testing needs, but for now,
  229. // we're only testing alter hooks that do not require any elements added by
  230. // this function.
  231. return $form;
  232. }
  233. /**
  234. * Implements hook_form_FORM_ID_alter() on behalf of block.module.
  235. */
  236. function block_form_form_test_alter_form_alter(&$form, &$form_state) {
  237. drupal_set_message('block_form_form_test_alter_form_alter() executed.');
  238. }
  239. /**
  240. * Implements hook_form_alter().
  241. */
  242. function form_test_form_alter(&$form, &$form_state, $form_id) {
  243. if ($form_id == 'form_test_alter_form') {
  244. drupal_set_message('form_test_form_alter() executed.');
  245. }
  246. }
  247. /**
  248. * Implements hook_form_FORM_ID_alter().
  249. */
  250. function form_test_form_form_test_alter_form_alter(&$form, &$form_state) {
  251. drupal_set_message('form_test_form_form_test_alter_form_alter() executed.');
  252. }
  253. /**
  254. * Implements hook_form_FORM_ID_alter() on behalf of system.module.
  255. */
  256. function system_form_form_test_alter_form_alter(&$form, &$form_state) {
  257. drupal_set_message('system_form_form_test_alter_form_alter() executed.');
  258. }
  259. /**
  260. * Form builder for testing drupal_validate_form().
  261. *
  262. * Serves for testing form processing and alterations by form validation
  263. * handlers, especially for the case of a validation error:
  264. * - form_set_value() should be able to alter submitted values in
  265. * $form_state['values'] without affecting the form element.
  266. * - #element_validate handlers should be able to alter the $element in the form
  267. * structure and the alterations should be contained in the rebuilt form.
  268. * - #validate handlers should be able to alter the $form and the alterations
  269. * should be contained in the rebuilt form.
  270. */
  271. function form_test_validate_form($form, &$form_state) {
  272. $form['name'] = array(
  273. '#type' => 'textfield',
  274. '#title' => 'Name',
  275. '#default_value' => '',
  276. '#element_validate' => array('form_test_element_validate_name'),
  277. );
  278. $form['submit'] = array(
  279. '#type' => 'submit',
  280. '#value' => 'Save',
  281. );
  282. // To simplify this test, enable form caching and use form storage to
  283. // remember our alteration.
  284. $form_state['cache'] = TRUE;
  285. return $form;
  286. }
  287. /**
  288. * Form element validation handler for 'name' in form_test_validate_form().
  289. */
  290. function form_test_element_validate_name(&$element, &$form_state) {
  291. $triggered = FALSE;
  292. if ($form_state['values']['name'] == 'element_validate') {
  293. // Alter the form element.
  294. $element['#value'] = '#value changed by #element_validate';
  295. // Alter the submitted value in $form_state.
  296. form_set_value($element, 'value changed by form_set_value() in #element_validate', $form_state);
  297. $triggered = TRUE;
  298. }
  299. if ($form_state['values']['name'] == 'element_validate_access') {
  300. $form_state['storage']['form_test_name'] = $form_state['values']['name'];
  301. // Alter the form element.
  302. $element['#access'] = FALSE;
  303. $triggered = TRUE;
  304. }
  305. elseif (!empty($form_state['storage']['form_test_name'])) {
  306. // To simplify this test, just take over the element's value into $form_state.
  307. form_set_value($element, $form_state['storage']['form_test_name'], $form_state);
  308. $triggered = TRUE;
  309. }
  310. if ($triggered) {
  311. // Output the element's value from $form_state.
  312. drupal_set_message(t('@label value: @value', array('@label' => $element['#title'], '@value' => $form_state['values']['name'])));
  313. // Trigger a form validation error to see our changes.
  314. form_set_error('');
  315. }
  316. }
  317. /**
  318. * Form validation handler for form_test_validate_form().
  319. */
  320. function form_test_validate_form_validate(&$form, &$form_state) {
  321. if ($form_state['values']['name'] == 'validate') {
  322. // Alter the form element.
  323. $form['name']['#value'] = '#value changed by #validate';
  324. // Alter the submitted value in $form_state.
  325. form_set_value($form['name'], 'value changed by form_set_value() in #validate', $form_state);
  326. // Output the element's value from $form_state.
  327. drupal_set_message(t('@label value: @value', array('@label' => $form['name']['#title'], '@value' => $form_state['values']['name'])));
  328. // Trigger a form validation error to see our changes.
  329. form_set_error('');
  330. }
  331. }
  332. /**
  333. * Form constructor to test the #required property.
  334. */
  335. function form_test_validate_required_form($form, &$form_state) {
  336. $options = drupal_map_assoc(array('foo', 'bar'));
  337. $form['textfield'] = array(
  338. '#type' => 'textfield',
  339. '#title' => 'Textfield',
  340. '#required' => TRUE,
  341. );
  342. $form['checkboxes'] = array(
  343. '#type' => 'checkboxes',
  344. '#title' => 'Checkboxes',
  345. '#options' => $options,
  346. '#required' => TRUE,
  347. );
  348. $form['select'] = array(
  349. '#type' => 'select',
  350. '#title' => 'Select',
  351. '#options' => $options,
  352. '#required' => TRUE,
  353. );
  354. $form['radios'] = array(
  355. '#type' => 'radios',
  356. '#title' => 'Radios',
  357. '#options' => $options,
  358. '#required' => TRUE,
  359. );
  360. $form['radios_optional'] = array(
  361. '#type' => 'radios',
  362. '#title' => 'Radios (optional)',
  363. '#options' => $options,
  364. );
  365. $form['radios_optional_default_value_false'] = array(
  366. '#type' => 'radios',
  367. '#title' => 'Radios (optional, with a default value of FALSE)',
  368. '#options' => $options,
  369. '#default_value' => FALSE,
  370. );
  371. $form['actions'] = array('#type' => 'actions');
  372. $form['actions']['submit'] = array('#type' => 'submit', '#value' => 'Submit');
  373. return $form;
  374. }
  375. /**
  376. * Form submission handler for form_test_validate_required_form().
  377. */
  378. function form_test_validate_required_form_submit($form, &$form_state) {
  379. drupal_set_message('The form_test_validate_required_form form was submitted successfully.');
  380. }
  381. /**
  382. * Form constructor to test the #required property without #title.
  383. */
  384. function form_test_validate_required_form_no_title($form, &$form_state) {
  385. $form['textfield'] = array(
  386. '#type' => 'textfield',
  387. '#required' => TRUE,
  388. );
  389. $form['actions'] = array('#type' => 'actions');
  390. $form['actions']['submit'] = array('#type' => 'submit', '#value' => 'Submit');
  391. return $form;
  392. }
  393. /**
  394. * Form submission handler for form_test_validate_required_form_no_title().
  395. */
  396. function form_test_validate_required_form_no_title_submit($form, &$form_state) {
  397. drupal_set_message('The form_test_validate_required_form_no_title form was submitted successfully.');
  398. }
  399. /**
  400. * Builds a simple form with a button triggering partial validation.
  401. */
  402. function form_test_limit_validation_errors_form($form, &$form_state) {
  403. $form['title'] = array(
  404. '#type' => 'textfield',
  405. '#title' => 'Title',
  406. '#required' => TRUE,
  407. );
  408. $form['test'] = array(
  409. '#title' => 'Test',
  410. '#type' => 'textfield',
  411. '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
  412. );
  413. $form['test_numeric_index'] = array(
  414. '#tree' => TRUE,
  415. );
  416. $form['test_numeric_index'][0] = array(
  417. '#title' => 'Test (numeric index)',
  418. '#type' => 'textfield',
  419. '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
  420. );
  421. $form['test_substring'] = array(
  422. '#tree' => TRUE,
  423. );
  424. $form['test_substring']['foo'] = array(
  425. '#title' => 'Test (substring) foo',
  426. '#type' => 'textfield',
  427. '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
  428. );
  429. $form['test_substring']['foobar'] = array(
  430. '#title' => 'Test (substring) foobar',
  431. '#type' => 'textfield',
  432. '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'),
  433. );
  434. $form['actions']['partial'] = array(
  435. '#type' => 'submit',
  436. '#limit_validation_errors' => array(array('test')),
  437. '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
  438. '#value' => t('Partial validate'),
  439. );
  440. $form['actions']['partial_numeric_index'] = array(
  441. '#type' => 'submit',
  442. '#limit_validation_errors' => array(array('test_numeric_index', 0)),
  443. '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
  444. '#value' => t('Partial validate (numeric index)'),
  445. );
  446. $form['actions']['substring'] = array(
  447. '#type' => 'submit',
  448. '#limit_validation_errors' => array(array('test_substring', 'foo')),
  449. '#submit' => array('form_test_limit_validation_errors_form_partial_submit'),
  450. '#value' => t('Partial validate (substring)'),
  451. );
  452. $form['actions']['full'] = array(
  453. '#type' => 'submit',
  454. '#value' => t('Full validate'),
  455. );
  456. return $form;
  457. }
  458. /**
  459. * Form element validation handler for the 'test' element.
  460. */
  461. function form_test_limit_validation_errors_element_validate_test(&$element, &$form_state) {
  462. if ($element['#value'] == 'invalid') {
  463. form_error($element, t('@label element is invalid', array('@label' => $element['#title'])));
  464. }
  465. }
  466. /**
  467. * Form submit handler for the partial validation submit button.
  468. */
  469. function form_test_limit_validation_errors_form_partial_submit($form, $form_state) {
  470. // The title has not been validated, thus its value - in case of the test case
  471. // an empty string - may not be set.
  472. if (!isset($form_state['values']['title']) && isset($form_state['values']['test'])) {
  473. drupal_set_message('Only validated values appear in the form values.');
  474. }
  475. }
  476. /**
  477. * Create a header and options array. Helper function for callbacks.
  478. */
  479. function _form_test_tableselect_get_data() {
  480. $header = array(
  481. 'one' => t('One'),
  482. 'two' => t('Two'),
  483. 'three' => t('Three'),
  484. 'four' => t('Four'),
  485. );
  486. $options['row1'] = array(
  487. 'one' => 'row1col1',
  488. 'two' => t('row1col2'),
  489. 'three' => t('row1col3'),
  490. 'four' => t('row1col4'),
  491. );
  492. $options['row2'] = array(
  493. 'one' => 'row2col1',
  494. 'two' => t('row2col2'),
  495. 'three' => t('row2col3'),
  496. 'four' => t('row2col4'),
  497. );
  498. $options['row3'] = array(
  499. 'one' => 'row3col1',
  500. 'two' => t('row3col2'),
  501. 'three' => t('row3col3'),
  502. 'four' => t('row3col4'),
  503. );
  504. return array($header, $options);
  505. }
  506. /**
  507. * Build a form to test the tableselect element.
  508. *
  509. * @param $form_state
  510. * The form_state
  511. * @param $element_properties
  512. * An array of element properties for the tableselect element.
  513. *
  514. * @return
  515. * A form with a tableselect element and a submit button.
  516. */
  517. function _form_test_tableselect_form_builder($form, $form_state, $element_properties) {
  518. list($header, $options) = _form_test_tableselect_get_data();
  519. $form['tableselect'] = $element_properties;
  520. $form['tableselect'] += array(
  521. '#type' => 'tableselect',
  522. '#header' => $header,
  523. '#options' => $options,
  524. '#multiple' => FALSE,
  525. '#empty' => t('Empty text.'),
  526. );
  527. $form['submit'] = array(
  528. '#type' => 'submit',
  529. '#value' => t('Submit'),
  530. );
  531. return $form;
  532. }
  533. /**
  534. * Test the tableselect #multiple = TRUE functionality.
  535. */
  536. function _form_test_tableselect_multiple_true_form($form, $form_state) {
  537. return _form_test_tableselect_form_builder($form, $form_state, array('#multiple' => TRUE));
  538. }
  539. /**
  540. * Process the tableselect #multiple = TRUE submitted values.
  541. */
  542. function _form_test_tableselect_multiple_true_form_submit($form, &$form_state) {
  543. $selected = $form_state['values']['tableselect'];
  544. foreach ($selected as $key => $value) {
  545. drupal_set_message(t('Submitted: @key = @value', array('@key' => $key, '@value' => $value)));
  546. }
  547. }
  548. /**
  549. * Test the tableselect #multiple = FALSE functionality.
  550. */
  551. function _form_test_tableselect_multiple_false_form($form, $form_state) {
  552. return _form_test_tableselect_form_builder($form, $form_state, array('#multiple' => FALSE));
  553. }
  554. /**
  555. * Process the tableselect #multiple = FALSE submitted values.
  556. */
  557. function _form_test_tableselect_multiple_false_form_submit($form, &$form_state) {
  558. drupal_set_message(t('Submitted: @value', array('@value' => $form_state['values']['tableselect'])));
  559. }
  560. /**
  561. * Test functionality of the tableselect #empty property.
  562. */
  563. function _form_test_tableselect_empty_form($form, $form_state) {
  564. return _form_test_tableselect_form_builder($form, $form_state, array('#options' => array()));
  565. }
  566. /**
  567. * Test functionality of the tableselect #js_select property.
  568. */
  569. function _form_test_tableselect_js_select_form($form, $form_state, $action) {
  570. switch ($action) {
  571. case 'multiple-true-default':
  572. $options = array('#multiple' => TRUE);
  573. break;
  574. case 'multiple-false-default':
  575. $options = array('#multiple' => FALSE);
  576. break;
  577. case 'multiple-true-no-advanced-select':
  578. $options = array('#multiple' => TRUE, '#js_select' => FALSE);
  579. break;
  580. case 'multiple-false-advanced-select':
  581. $options = array('#multiple' => FALSE, '#js_select' => TRUE);
  582. break;
  583. }
  584. return _form_test_tableselect_form_builder($form, $form_state, $options);
  585. }
  586. /**
  587. * Tests functionality of vertical tabs.
  588. */
  589. function _form_test_vertical_tabs_form($form, &$form_state) {
  590. $form['vertical_tabs'] = array(
  591. '#type' => 'vertical_tabs',
  592. );
  593. $form['tab1'] = array(
  594. '#type' => 'fieldset',
  595. '#title' => t('Tab 1'),
  596. '#collapsible' => TRUE,
  597. '#group' => 'vertical_tabs',
  598. );
  599. $form['tab1']['field1'] = array(
  600. '#title' => t('Field 1'),
  601. '#type' => 'textfield',
  602. );
  603. $form['tab2'] = array(
  604. '#type' => 'fieldset',
  605. '#title' => t('Tab 2'),
  606. '#collapsible' => TRUE,
  607. '#group' => 'vertical_tabs',
  608. );
  609. $form['tab2']['field2'] = array(
  610. '#title' => t('Field 2'),
  611. '#type' => 'textfield',
  612. );
  613. return $form;
  614. }
  615. /**
  616. * A multistep form for testing the form storage.
  617. *
  618. * It uses two steps for editing a virtual "thing". Any changes to it are saved
  619. * in the form storage and have to be present during any step. By setting the
  620. * request parameter "cache" the form can be tested with caching enabled, as
  621. * it would be the case, if the form would contain some #ajax callbacks.
  622. *
  623. * @see form_test_storage_form_submit()
  624. */
  625. function form_test_storage_form($form, &$form_state) {
  626. if ($form_state['rebuild']) {
  627. $form_state['input'] = array();
  628. }
  629. // Initialize
  630. if (empty($form_state['storage'])) {
  631. if (empty($form_state['input'])) {
  632. $_SESSION['constructions'] = 0;
  633. }
  634. // Put the initial thing into the storage
  635. $form_state['storage'] = array(
  636. 'thing' => array(
  637. 'title' => 'none',
  638. 'value' => '',
  639. ),
  640. );
  641. }
  642. // Count how often the form is constructed.
  643. $_SESSION['constructions']++;
  644. drupal_set_message("Form constructions: " . $_SESSION['constructions']);
  645. $form['title'] = array(
  646. '#type' => 'textfield',
  647. '#title' => 'Title',
  648. '#default_value' => $form_state['storage']['thing']['title'],
  649. '#required' => TRUE,
  650. );
  651. $form['value'] = array(
  652. '#type' => 'textfield',
  653. '#title' => 'Value',
  654. '#default_value' => $form_state['storage']['thing']['value'],
  655. '#element_validate' => array('form_test_storage_element_validate_value_cached'),
  656. );
  657. $form['continue_button'] = array(
  658. '#type' => 'button',
  659. '#value' => 'Reset',
  660. // Rebuilds the form without keeping the values.
  661. );
  662. $form['continue_submit'] = array(
  663. '#type' => 'submit',
  664. '#value' => 'Continue submit',
  665. '#submit' => array('form_storage_test_form_continue_submit'),
  666. );
  667. $form['submit'] = array(
  668. '#type' => 'submit',
  669. '#value' => 'Save',
  670. );
  671. if (isset($_REQUEST['cache'])) {
  672. // Manually activate caching, so we can test that the storage keeps working
  673. // when it's enabled.
  674. $form_state['cache'] = TRUE;
  675. }
  676. return $form;
  677. }
  678. /**
  679. * Form element validation handler for 'value' element in form_test_storage_form().
  680. *
  681. * Tests updating of cached form storage during validation.
  682. */
  683. function form_test_storage_element_validate_value_cached($element, &$form_state) {
  684. // If caching is enabled and we receive a certain value, change the storage.
  685. // This presumes that another submitted form value triggers a validation error
  686. // elsewhere in the form. Form API should still update the cached form storage
  687. // though.
  688. if (isset($_REQUEST['cache']) && $form_state['values']['value'] == 'change_title') {
  689. $form_state['storage']['thing']['changed'] = TRUE;
  690. }
  691. }
  692. /**
  693. * Form submit handler to continue multi-step form.
  694. */
  695. function form_storage_test_form_continue_submit($form, &$form_state) {
  696. $form_state['storage']['thing']['title'] = $form_state['values']['title'];
  697. $form_state['storage']['thing']['value'] = $form_state['values']['value'];
  698. $form_state['rebuild'] = TRUE;
  699. }
  700. /**
  701. * Form submit handler to finish multi-step form.
  702. */
  703. function form_test_storage_form_submit($form, &$form_state) {
  704. drupal_set_message("Title: " . check_plain($form_state['values']['title']));
  705. drupal_set_message("Form constructions: " . $_SESSION['constructions']);
  706. if (isset($form_state['storage']['thing']['changed'])) {
  707. drupal_set_message("The thing has been changed.");
  708. }
  709. $form_state['redirect'] = 'node';
  710. }
  711. /**
  712. * A form for testing form labels and required marks.
  713. */
  714. function form_label_test_form() {
  715. $form['form_checkboxes_test'] = array(
  716. '#type' => 'checkboxes',
  717. '#title' => t('Checkboxes test'),
  718. '#options' => array(
  719. 'first-checkbox' => t('First checkbox'),
  720. 'second-checkbox' => t('Second checkbox'),
  721. 'third-checkbox' => t('Third checkbox'),
  722. '0' => t('0'),
  723. ),
  724. );
  725. $form['form_radios_test'] = array(
  726. '#type' => 'radios',
  727. '#title' => t('Radios test'),
  728. '#options' => array(
  729. 'first-radio' => t('First radio'),
  730. 'second-radio' => t('Second radio'),
  731. 'third-radio' => t('Third radio'),
  732. '0' => t('0'),
  733. ),
  734. // Test #field_prefix and #field_suffix placement.
  735. '#field_prefix' => '<span id="form-test-radios-field-prefix">' . t('Radios #field_prefix element') . '</span>',
  736. '#field_suffix' => '<span id="form-test-radios-field-suffix">' . t('Radios #field_suffix element') . '</span>',
  737. );
  738. $form['form_checkbox_test'] = array(
  739. '#type' => 'checkbox',
  740. '#title' => t('Checkbox test'),
  741. );
  742. $form['form_textfield_test_title_and_required'] = array(
  743. '#type' => 'textfield',
  744. '#title' => t('Textfield test for required with title'),
  745. '#required' => TRUE,
  746. );
  747. $form['form_textfield_test_no_title_required'] = array(
  748. '#type' => 'textfield',
  749. // We use an empty title, since not setting #title suppresses the label
  750. // and required marker.
  751. '#title' => '',
  752. '#required' => TRUE,
  753. );
  754. $form['form_textfield_test_title'] = array(
  755. '#type' => 'textfield',
  756. '#title' => t('Textfield test for title only'),
  757. // Not required.
  758. // Test #prefix and #suffix placement.
  759. '#prefix' => '<div id="form-test-textfield-title-prefix">' . t('Textfield #prefix element') . '</div>',
  760. '#suffix' => '<div id="form-test-textfield-title-suffix">' . t('Textfield #suffix element') . '</div>',
  761. );
  762. $form['form_textfield_test_title_after'] = array(
  763. '#type' => 'textfield',
  764. '#title' => t('Textfield test for title after element'),
  765. '#title_display' => 'after',
  766. );
  767. $form['form_textfield_test_title_invisible'] = array(
  768. '#type' => 'textfield',
  769. '#title' => t('Textfield test for invisible title'),
  770. '#title_display' => 'invisible',
  771. );
  772. // Textfield test for title set not to display.
  773. $form['form_textfield_test_title_no_show'] = array(
  774. '#type' => 'textfield',
  775. );
  776. // Checkboxes & radios with title as attribute.
  777. $form['form_checkboxes_title_attribute'] = array(
  778. '#type' => 'checkboxes',
  779. '#title' => 'Checkboxes test',
  780. '#options' => array(
  781. 'first-checkbox' => 'First checkbox',
  782. 'second-checkbox' => 'Second checkbox',
  783. ),
  784. '#title_display' => 'attribute',
  785. '#required' => TRUE,
  786. );
  787. $form['form_radios_title_attribute'] = array(
  788. '#type' => 'radios',
  789. '#title' => 'Radios test',
  790. '#options' => array(
  791. 'first-radio' => 'First radio',
  792. 'second-radio' => 'Second radio',
  793. ),
  794. '#title_display' => 'attribute',
  795. '#required' => TRUE,
  796. );
  797. return $form;
  798. }
  799. /**
  800. * Menu callback; Invokes a form builder function with a wrapper callback.
  801. */
  802. function form_test_wrapper_callback($form_id) {
  803. $form_state = array(
  804. 'build_info' => array('args' => array()),
  805. 'wrapper_callback' => 'form_test_wrapper_callback_wrapper',
  806. );
  807. return drupal_build_form($form_id, $form_state);
  808. }
  809. /**
  810. * Form wrapper for form_test_wrapper_callback_form().
  811. */
  812. function form_test_wrapper_callback_wrapper($form, &$form_state) {
  813. $form['wrapper'] = array('#markup' => 'Form wrapper callback element output.');
  814. return $form;
  815. }
  816. /**
  817. * Form builder for form wrapper callback test.
  818. */
  819. function form_test_wrapper_callback_form($form, &$form_state) {
  820. $form['builder'] = array('#markup' => 'Form builder element output.');
  821. return $form;
  822. }
  823. /**
  824. * Form builder for form_state_values_clean() test.
  825. */
  826. function form_test_form_state_values_clean_form($form, &$form_state) {
  827. // Build an example form containing multiple submit and button elements; not
  828. // only on the top-level.
  829. $form = array('#tree' => TRUE);
  830. $form['foo'] = array('#type' => 'submit', '#value' => t('Submit'));
  831. $form['bar'] = array('#type' => 'submit', '#value' => t('Submit'));
  832. $form['beer'] = array('#type' => 'value', '#value' => 1000);
  833. $form['baz']['foo'] = array('#type' => 'button', '#value' => t('Submit'));
  834. $form['baz']['baz'] = array('#type' => 'submit', '#value' => t('Submit'));
  835. $form['baz']['beer'] = array('#type' => 'value', '#value' => 2000);
  836. return $form;
  837. }
  838. /**
  839. * Form submit handler for form_state_values_clean() test form.
  840. */
  841. function form_test_form_state_values_clean_form_submit($form, &$form_state) {
  842. form_state_values_clean($form_state);
  843. drupal_json_output($form_state['values']);
  844. exit;
  845. }
  846. /**
  847. * Form constructor for the form_state_values_clean() test.
  848. */
  849. function form_test_form_state_values_clean_advanced_form($form, &$form_state) {
  850. // Build an example form containing a managed file and a submit form element.
  851. $form['image'] = array(
  852. '#type' => 'managed_file',
  853. '#title' => t('Image'),
  854. '#upload_location' => 'public://',
  855. '#default_value' => 0,
  856. );
  857. $form['submit'] = array(
  858. '#type' => 'submit',
  859. '#value' => t('Submit'),
  860. );
  861. return $form;
  862. }
  863. /**
  864. * Form submission handler for form_test_form_state_values_clean_advanced_form().
  865. */
  866. function form_test_form_state_values_clean_advanced_form_submit($form, &$form_state) {
  867. form_state_values_clean($form_state);
  868. print t('You WIN!');
  869. exit;
  870. }
  871. /**
  872. * Build a form to test a checkbox.
  873. */
  874. function _form_test_checkbox($form, &$form_state) {
  875. // A required checkbox.
  876. $form['required_checkbox'] = array(
  877. '#type' => 'checkbox',
  878. '#required' => TRUE,
  879. '#title' => 'required_checkbox',
  880. );
  881. // A disabled checkbox should get its default value back.
  882. $form['disabled_checkbox_on'] = array(
  883. '#type' => 'checkbox',
  884. '#disabled' => TRUE,
  885. '#return_value' => 'disabled_checkbox_on',
  886. '#default_value' => 'disabled_checkbox_on',
  887. '#title' => 'disabled_checkbox_on',
  888. );
  889. $form['disabled_checkbox_off'] = array(
  890. '#type' => 'checkbox',
  891. '#disabled' => TRUE,
  892. '#return_value' => 'disabled_checkbox_off',
  893. '#default_value' => NULL,
  894. '#title' => 'disabled_checkbox_off',
  895. );
  896. // A checkbox is active when #default_value == #return_value.
  897. $form['checkbox_on'] = array(
  898. '#type' => 'checkbox',
  899. '#return_value' => 'checkbox_on',
  900. '#default_value' => 'checkbox_on',
  901. '#title' => 'checkbox_on',
  902. );
  903. // But inactive in any other case.
  904. $form['checkbox_off'] = array(
  905. '#type' => 'checkbox',
  906. '#return_value' => 'checkbox_off',
  907. '#default_value' => 'checkbox_on',
  908. '#title' => 'checkbox_off',
  909. );
  910. // Checkboxes with a #return_value of '0' are supported.
  911. $form['zero_checkbox_on'] = array(
  912. '#type' => 'checkbox',
  913. '#return_value' => '0',
  914. '#default_value' => '0',
  915. '#title' => 'zero_checkbox_on',
  916. );
  917. // In that case, passing a #default_value != '0' means that the checkbox is off.
  918. $form['zero_checkbox_off'] = array(
  919. '#type' => 'checkbox',
  920. '#return_value' => '0',
  921. '#default_value' => '1',
  922. '#title' => 'zero_checkbox_off',
  923. );
  924. $form['submit'] = array(
  925. '#type' => 'submit',
  926. '#value' => t('Submit')
  927. );
  928. return $form;
  929. }
  930. /**
  931. * Return the form values via JSON.
  932. */
  933. function _form_test_checkbox_submit($form, &$form_state) {
  934. drupal_json_output($form_state['values']);
  935. exit();
  936. }
  937. /**
  938. * Builds a form to test #type 'select' validation.
  939. */
  940. function form_test_select($form, &$form_state) {
  941. $base = array(
  942. '#type' => 'select',
  943. '#options' => drupal_map_assoc(array('one', 'two', 'three')),
  944. );
  945. $form['select'] = $base + array(
  946. '#title' => '#default_value one',
  947. '#default_value' => 'one',
  948. );
  949. $form['select_required'] = $base + array(
  950. '#title' => '#default_value one, #required',
  951. '#required' => TRUE,
  952. '#default_value' => 'one',
  953. );
  954. $form['select_optional'] = $base + array(
  955. '#title' => '#default_value one, not #required',
  956. '#required' => FALSE,
  957. '#default_value' => 'one',
  958. );
  959. $form['empty_value'] = $base + array(
  960. '#title' => '#default_value one, #required, #empty_value 0',
  961. '#required' => TRUE,
  962. '#default_value' => 'one',
  963. '#empty_value' => 0,
  964. );
  965. $form['empty_value_one'] = $base + array(
  966. '#title' => '#default_value = #empty_value, #required',
  967. '#required' => TRUE,
  968. '#default_value' => 'one',
  969. '#empty_value' => 'one',
  970. );
  971. $form['no_default'] = $base + array(
  972. '#title' => 'No #default_value, #required',
  973. '#required' => TRUE,
  974. );
  975. $form['no_default_optional'] = $base + array(
  976. '#title' => 'No #default_value, not #required',
  977. '#required' => FALSE,
  978. '#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.',
  979. );
  980. $form['no_default_optional_empty_value'] = $base + array(
  981. '#title' => 'No #default_value, not #required, #empty_value empty string',
  982. '#empty_value' => '',
  983. '#required' => FALSE,
  984. '#description' => 'Should result in an empty string (due to #empty_value), because it is optional.',
  985. );
  986. $form['no_default_empty_option'] = $base + array(
  987. '#title' => 'No #default_value, #required, #empty_option',
  988. '#required' => TRUE,
  989. '#empty_option' => '- Choose -',
  990. );
  991. $form['no_default_empty_option_optional'] = $base + array(
  992. '#title' => 'No #default_value, not #required, #empty_option',
  993. '#empty_option' => '- Dismiss -',
  994. '#description' => 'Should result in an empty string (default of #empty_value), because it is optional.',
  995. );
  996. $form['no_default_empty_value'] = $base + array(
  997. '#title' => 'No #default_value, #required, #empty_value 0',
  998. '#required' => TRUE,
  999. '#empty_value' => 0,
  1000. '#description' => 'Should never result in 0.',
  1001. );
  1002. $form['no_default_empty_value_one'] = $base + array(
  1003. '#title' => 'No #default_value, #required, #empty_value one',
  1004. '#required' => TRUE,
  1005. '#empty_value' => 'one',
  1006. '#description' => 'A mistakenly assigned #empty_value contained in #options should not be valid.',
  1007. );
  1008. $form['no_default_empty_value_optional'] = $base + array(
  1009. '#title' => 'No #default_value, not #required, #empty_value 0',
  1010. '#required' => FALSE,
  1011. '#empty_value' => 0,
  1012. '#description' => 'Should result in 0, because it is optional.',
  1013. );
  1014. $form['multiple'] = $base + array(
  1015. '#title' => '#multiple, #default_value two',
  1016. '#default_value' => array('two'),
  1017. '#multiple' => TRUE,
  1018. );
  1019. $form['multiple_no_default'] = $base + array(
  1020. '#title' => '#multiple, no #default_value',
  1021. '#multiple' => TRUE,
  1022. );
  1023. $form['multiple_no_default_required'] = $base + array(
  1024. '#title' => '#multiple, #required, no #default_value',
  1025. '#required' => TRUE,
  1026. '#multiple' => TRUE,
  1027. );
  1028. $form['submit'] = array('#type' => 'submit', '#value' => 'Submit');
  1029. return $form;
  1030. }
  1031. /**
  1032. * Form submit handler for form_test_select().
  1033. */
  1034. function form_test_select_submit($form, &$form_state) {
  1035. drupal_json_output($form_state['values']);
  1036. exit();
  1037. }
  1038. /**
  1039. * Form constructor to test expansion of #type checkboxes and radios.
  1040. */
  1041. function form_test_checkboxes_radios($form, &$form_state, $customize = FALSE) {
  1042. $form['#submit'] = array('_form_test_submit_values_json');
  1043. // Expand #type checkboxes, setting custom element properties for some but not
  1044. // all options.
  1045. $form['checkboxes'] = array(
  1046. '#type' => 'checkboxes',
  1047. '#title' => 'Checkboxes',
  1048. '#options' => array(
  1049. 0 => 'Zero',
  1050. 'foo' => 'Foo',
  1051. 1 => 'One',
  1052. 'bar' => 'Bar',
  1053. '>' => 'Special Char',
  1054. ),
  1055. );
  1056. if ($customize) {
  1057. $form['checkboxes'] += array(
  1058. 'foo' => array(
  1059. '#description' => 'Enable to foo.',
  1060. ),
  1061. 1 => array(
  1062. '#weight' => 10,
  1063. ),
  1064. );
  1065. }
  1066. // Expand #type radios, setting custom element properties for some but not
  1067. // all options.
  1068. $form['radios'] = array(
  1069. '#type' => 'radios',
  1070. '#title' => 'Radios',
  1071. '#options' => array(
  1072. 0 => 'Zero',
  1073. 'foo' => 'Foo',
  1074. 1 => 'One',
  1075. 'bar' => 'Bar',
  1076. '>' => 'Special Char',
  1077. ),
  1078. );
  1079. if ($customize) {
  1080. $form['radios'] += array(
  1081. 'foo' => array(
  1082. '#description' => 'Enable to foo.',
  1083. ),
  1084. 1 => array(
  1085. '#weight' => 10,
  1086. ),
  1087. );
  1088. }
  1089. $form['submit'] = array('#type' => 'submit', '#value' => 'Submit');
  1090. return $form;
  1091. }
  1092. /**
  1093. * Build a form to test disabled elements.
  1094. */
  1095. function _form_test_disabled_elements($form, &$form_state) {
  1096. // Elements that take a simple default value.
  1097. foreach (array('textfield', 'textarea', 'hidden') as $type) {
  1098. $form[$type] = array(
  1099. '#type' => $type,
  1100. '#title' => $type,
  1101. '#default_value' => $type,
  1102. '#test_hijack_value' => 'HIJACK',
  1103. '#disabled' => TRUE,
  1104. );
  1105. }
  1106. // Multiple values option elements.
  1107. foreach (array('checkboxes', 'select') as $type) {
  1108. $form[$type . '_multiple'] = array(
  1109. '#type' => $type,
  1110. '#title' => $type . ' (multiple)',
  1111. '#options' => array(
  1112. 'test_1' => 'Test 1',
  1113. 'test_2' => 'Test 2',
  1114. ),
  1115. '#multiple' => TRUE,
  1116. '#default_value' => array('test_2' => 'test_2'),
  1117. // The keys of #test_hijack_value need to match the #name of the control.
  1118. // @see FormsTestCase::testDisabledElements()
  1119. '#test_hijack_value' => $type == 'select' ? array('' => 'test_1') : array('test_1' => 'test_1'),
  1120. '#disabled' => TRUE,
  1121. );
  1122. }
  1123. // Single values option elements.
  1124. foreach (array('radios', 'select') as $type) {
  1125. $form[$type . '_single'] = array(
  1126. '#type' => $type,
  1127. '#title' => $type . ' (single)',
  1128. '#options' => array(
  1129. 'test_1' => 'Test 1',
  1130. 'test_2' => 'Test 2',
  1131. ),
  1132. '#multiple' => FALSE,
  1133. '#default_value' => 'test_2',
  1134. '#test_hijack_value' => 'test_1',
  1135. '#disabled' => TRUE,
  1136. );
  1137. }
  1138. // Checkbox and radio.
  1139. foreach (array('checkbox', 'radio') as $type) {
  1140. $form[$type . '_unchecked'] = array(
  1141. '#type' => $type,
  1142. '#title' => $type . ' (unchecked)',
  1143. '#return_value' => 1,
  1144. '#default_value' => 0,
  1145. '#test_hijack_value' => 1,
  1146. '#disabled' => TRUE,
  1147. );
  1148. $form[$type . '_checked'] = array(
  1149. '#type' => $type,
  1150. '#title' => $type . ' (checked)',
  1151. '#return_value' => 1,
  1152. '#default_value' => 1,
  1153. '#test_hijack_value' => NULL,
  1154. '#disabled' => TRUE,
  1155. );
  1156. }
  1157. // Weight.
  1158. $form['weight'] = array(
  1159. '#type' => 'weight',
  1160. '#title' => 'weight',
  1161. '#default_value' => 10,
  1162. '#test_hijack_value' => 5,
  1163. '#disabled' => TRUE,
  1164. );
  1165. // Date.
  1166. $form['date'] = array(
  1167. '#type' => 'date',
  1168. '#title' => 'date',
  1169. '#disabled' => TRUE,
  1170. '#default_value' => array(
  1171. 'day' => 19,
  1172. 'month' => 11,
  1173. 'year' => 1978,
  1174. ),
  1175. '#test_hijack_value' => array(
  1176. 'day' => 20,
  1177. 'month' => 12,
  1178. 'year' => 1979,
  1179. ),
  1180. );
  1181. // The #disabled state should propagate to children.
  1182. $form['disabled_container'] = array(
  1183. '#disabled' => TRUE,
  1184. );
  1185. foreach (array('textfield', 'textarea', 'hidden') as $type) {
  1186. $form['disabled_container']['disabled_container_' . $type] = array(
  1187. '#type' => $type,
  1188. '#title' => $type,
  1189. '#default_value' => $type,
  1190. '#test_hijack_value' => 'HIJACK',
  1191. );
  1192. }
  1193. // Text format.
  1194. $form['text_format'] = array(
  1195. '#type' => 'text_format',
  1196. '#title' => 'Text format',
  1197. '#disabled' => TRUE,
  1198. '#default_value' => 'Text value',
  1199. '#format' => 'plain_text',
  1200. '#expected_value' => array(
  1201. 'value' => 'Text value',
  1202. 'format' => 'plain_text',
  1203. ),
  1204. '#test_hijack_value' => array(
  1205. 'value' => 'HIJACK',
  1206. 'format' => 'filtered_html',
  1207. ),
  1208. );
  1209. // Password fields.
  1210. $form['password'] = array(
  1211. '#type' => 'password',
  1212. '#title' => 'Password',
  1213. '#disabled' => TRUE,
  1214. );
  1215. $form['password_confirm'] = array(
  1216. '#type' => 'password_confirm',
  1217. '#title' => 'Password confirm',
  1218. '#disabled' => TRUE,
  1219. );
  1220. // Files.
  1221. $form['file'] = array(
  1222. '#type' => 'file',
  1223. '#title' => 'File',
  1224. '#disabled' => TRUE,
  1225. );
  1226. $form['managed_file'] = array(
  1227. '#type' => 'managed_file',
  1228. '#title' => 'Managed file',
  1229. '#disabled' => TRUE,
  1230. );
  1231. // Buttons.
  1232. $form['image_button'] = array(
  1233. '#type' => 'image_button',
  1234. '#value' => 'Image button',
  1235. '#disabled' => TRUE,
  1236. );
  1237. $form['button'] = array(
  1238. '#type' => 'button',
  1239. '#value' => 'Button',
  1240. '#disabled' => TRUE,
  1241. );
  1242. $form['submit_disabled'] = array(
  1243. '#type' => 'submit',
  1244. '#value' => 'Submit',
  1245. '#disabled' => TRUE,
  1246. );
  1247. $form['submit'] = array(
  1248. '#type' => 'submit',
  1249. '#value' => t('Submit'),
  1250. );
  1251. return $form;
  1252. }
  1253. /**
  1254. * Return the form values via JSON.
  1255. */
  1256. function _form_test_disabled_elements_submit($form, &$form_state) {
  1257. drupal_json_output($form_state['values']);
  1258. exit();
  1259. }
  1260. /**
  1261. * Build a form to test input forgery of enabled elements.
  1262. */
  1263. function _form_test_input_forgery($form, &$form_state) {
  1264. // For testing that a user can't submit a value not matching one of the
  1265. // allowed options.
  1266. $form['checkboxes'] = array(
  1267. '#type' => 'checkboxes',
  1268. '#options' => array(
  1269. 'one' => 'One',
  1270. 'two' => 'Two',
  1271. ),
  1272. );
  1273. $form['submit'] = array(
  1274. '#type' => 'submit',
  1275. '#value' => t('Submit'),
  1276. );
  1277. return $form;
  1278. }
  1279. /**
  1280. * Return the form values via JSON.
  1281. */
  1282. function _form_test_input_forgery_submit($form, &$form_state) {
  1283. drupal_json_output($form_state['values']);
  1284. exit();
  1285. }
  1286. /**
  1287. * Form builder for testing preservation of values during a rebuild.
  1288. */
  1289. function form_test_form_rebuild_preserve_values_form($form, &$form_state) {
  1290. // Start the form with two checkboxes, to test different defaults, and a
  1291. // textfield, to test more than one element type.
  1292. $form = array(
  1293. 'checkbox_1_default_off' => array(
  1294. '#type' => 'checkbox',
  1295. '#title' => t('This checkbox defaults to unchecked.'),
  1296. '#default_value' => FALSE,
  1297. ),
  1298. 'checkbox_1_default_on' => array(
  1299. '#type' => 'checkbox',
  1300. '#title' => t('This checkbox defaults to checked.'),
  1301. '#default_value' => TRUE,
  1302. ),
  1303. 'text_1' => array(
  1304. '#type' => 'textfield',
  1305. '#title' => t('This textfield has a non-empty default value.'),
  1306. '#default_value' => 'DEFAULT 1',
  1307. ),
  1308. );
  1309. // Provide an 'add more' button that rebuilds the form with an additional two
  1310. // checkboxes and a textfield. The test is to make sure that the rebuild
  1311. // triggered by this button preserves the user input values for the initial
  1312. // elements and initializes the new elements with the correct default values.
  1313. if (empty($form_state['storage']['add_more'])) {
  1314. $form['add_more'] = array(
  1315. '#type' => 'submit',
  1316. '#value' => 'Add more',
  1317. '#submit' => array('form_test_form_rebuild_preserve_values_form_add_more'),
  1318. );
  1319. }
  1320. else {
  1321. $form += array(
  1322. 'checkbox_2_default_off' => array(
  1323. '#type' => 'checkbox',
  1324. '#title' => t('This checkbox defaults to unchecked.'),
  1325. '#default_value' => FALSE,
  1326. ),
  1327. 'checkbox_2_default_on' => array(
  1328. '#type' => 'checkbox',
  1329. '#title' => t('This checkbox defaults to checked.'),
  1330. '#default_value' => TRUE,
  1331. ),
  1332. 'text_2' => array(
  1333. '#type' => 'textfield',
  1334. '#title' => t('This textfield has a non-empty default value.'),
  1335. '#default_value' => 'DEFAULT 2',
  1336. ),
  1337. );
  1338. }
  1339. // A submit button that finishes the form workflow (does not rebuild).
  1340. $form['submit'] = array(
  1341. '#type' => 'submit',
  1342. '#value' => 'Submit',
  1343. );
  1344. return $form;
  1345. }
  1346. /**
  1347. * Button submit handler for form_test_form_rebuild_preserve_values_form().
  1348. */
  1349. function form_test_form_rebuild_preserve_values_form_add_more($form, &$form_state) {
  1350. // Rebuild, to test preservation of input values.
  1351. $form_state['storage']['add_more'] = TRUE;
  1352. $form_state['rebuild'] = TRUE;
  1353. }
  1354. /**
  1355. * Form submit handler for form_test_form_rebuild_preserve_values_form().
  1356. */
  1357. function form_test_form_rebuild_preserve_values_form_submit($form, &$form_state) {
  1358. // Finish the workflow. Do not rebuild.
  1359. drupal_set_message(t('Form values: %values', array('%values' => var_export($form_state['values'], TRUE))));
  1360. }
  1361. /**
  1362. * Form constructor for testing form state persistence.
  1363. */
  1364. function form_test_state_persist($form, &$form_state) {
  1365. $form['title'] = array(
  1366. '#type' => 'textfield',
  1367. '#title' => 'title',
  1368. '#default_value' => 'DEFAULT',
  1369. '#required' => TRUE,
  1370. );
  1371. $form_state['value'] = 'State persisted.';
  1372. $form['submit'] = array(
  1373. '#type' => 'submit',
  1374. '#value' => t('Submit'),
  1375. );
  1376. return $form;
  1377. }
  1378. /**
  1379. * Submit handler.
  1380. *
  1381. * @see form_test_state_persist()
  1382. */
  1383. function form_test_state_persist_submit($form, &$form_state) {
  1384. drupal_set_message($form_state['value']);
  1385. $form_state['rebuild'] = TRUE;
  1386. }
  1387. /**
  1388. * Implements hook_form_FORM_ID_alter().
  1389. *
  1390. * @see form_test_state_persist()
  1391. */
  1392. function form_test_form_form_test_state_persist_alter(&$form, &$form_state) {
  1393. // Simulate a form alter implementation inserting form elements that enable
  1394. // caching of the form, e.g. elements having #ajax.
  1395. if (!empty($_REQUEST['cache'])) {
  1396. $form_state['cache'] = TRUE;
  1397. }
  1398. }
  1399. /**
  1400. * Form builder to test programmatic form submissions.
  1401. */
  1402. function form_test_programmatic_form($form, &$form_state) {
  1403. $form['textfield'] = array(
  1404. '#title' => 'Textfield',
  1405. '#type' => 'textfield',
  1406. );
  1407. $form['checkboxes'] = array(
  1408. '#type' => 'checkboxes',
  1409. '#options' => array(
  1410. 1 => 'First checkbox',
  1411. 2 => 'Second checkbox',
  1412. ),
  1413. // Both checkboxes are selected by default so that we can test the ability
  1414. // of programmatic form submissions to uncheck them.
  1415. '#default_value' => array(1, 2),
  1416. );
  1417. $form['field_to_validate'] = array(
  1418. '#type' => 'radios',
  1419. '#title' => 'Field to validate (in the case of limited validation)',
  1420. '#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.',
  1421. '#options' => array(
  1422. 'all' => 'Validate all fields',
  1423. 'textfield' => 'Validate the "Textfield" field',
  1424. 'field_to_validate' => 'Validate the "Field to validate" field',
  1425. ),
  1426. '#default_value' => 'all',
  1427. );
  1428. // The main submit button for the form.
  1429. $form['submit'] = array(
  1430. '#type' => 'submit',
  1431. '#value' => 'Submit',
  1432. );
  1433. // A secondary submit button that allows validation to be limited based on
  1434. // the value of the above radio selector.
  1435. $form['submit_limit_validation'] = array(
  1436. '#type' => 'submit',
  1437. '#value' => 'Submit with limited validation',
  1438. // Use the same submit handler for this button as for the form itself.
  1439. // (This must be set explicitly or otherwise the form API will ignore the
  1440. // #limit_validation_errors property.)
  1441. '#submit' => array('form_test_programmatic_form_submit'),
  1442. );
  1443. if (!empty($form_state['input']['field_to_validate']) && $form_state['input']['field_to_validate'] != 'all') {
  1444. $form['submit_limit_validation']['#limit_validation_errors'] = array(
  1445. array($form_state['input']['field_to_validate']),
  1446. );
  1447. }
  1448. return $form;
  1449. }
  1450. /**
  1451. * Form validation handler for programmatic form submissions.
  1452. *
  1453. * To test that the validation handler is correctly executed, the field value is
  1454. * explicitly required here.
  1455. */
  1456. function form_test_programmatic_form_validate($form, &$form_state) {
  1457. if (empty($form_state['values']['textfield'])) {
  1458. form_set_error('textfield', t('Textfield is required.'));
  1459. }
  1460. }
  1461. /**
  1462. * Form submit handler for programmatic form submissions.
  1463. *
  1464. * To test that the submission handler is correctly executed, we store the
  1465. * submitted values in a place we can access from the caller context.
  1466. */
  1467. function form_test_programmatic_form_submit($form, &$form_state) {
  1468. $form_state['storage']['programmatic_form_submit'] = $form_state['values'];
  1469. }
  1470. /**
  1471. * Form builder to test button click detection.
  1472. */
  1473. function form_test_clicked_button($form, &$form_state) {
  1474. // A single text field. In IE, when a form has only one non-button input field
  1475. // and the ENTER key is pressed while that field has focus, the form is
  1476. // submitted without any information identifying the button responsible for
  1477. // the submission. In other browsers, the form is submitted as though the
  1478. // first button were clicked.
  1479. $form['text'] = array(
  1480. '#title' => 'Text',
  1481. '#type' => 'textfield',
  1482. );
  1483. // Loop through each path argument, addding buttons based on the information
  1484. // in the argument. For example, if the path is
  1485. // form-test/clicked-button/s/i/rb, then 3 buttons are added: a 'submit', an
  1486. // 'image_button', and a 'button' with #access=FALSE. This enables form.test
  1487. // to test a variety of combinations.
  1488. $i=0;
  1489. $args = array_slice(arg(), 2);
  1490. foreach ($args as $arg) {
  1491. $name = 'button' . ++$i;
  1492. // 's', 'b', or 'i' in the argument define the button type wanted.
  1493. if (strpos($arg, 's') !== FALSE) {
  1494. $type = 'submit';
  1495. }
  1496. elseif (strpos($arg, 'b') !== FALSE) {
  1497. $type = 'button';
  1498. }
  1499. elseif (strpos($arg, 'i') !== FALSE) {
  1500. $type = 'image_button';
  1501. }
  1502. else {
  1503. $type = NULL;
  1504. }
  1505. if (isset($type)) {
  1506. $form[$name] = array(
  1507. '#type' => $type,
  1508. '#name' => $name,
  1509. );
  1510. // Image buttons need a #src; the others need a #value.
  1511. if ($type == 'image_button') {
  1512. $form[$name]['#src'] = 'misc/druplicon.png';
  1513. }
  1514. else {
  1515. $form[$name]['#value'] = $name;
  1516. }
  1517. // 'r' for restricted, so we can test that button click detection code
  1518. // correctly takes #access security into account.
  1519. if (strpos($arg, 'r') !== FALSE) {
  1520. $form[$name]['#access'] = FALSE;
  1521. }
  1522. }
  1523. }
  1524. return $form;
  1525. }
  1526. /**
  1527. * Form validation handler for the form_test_clicked_button() form.
  1528. */
  1529. function form_test_clicked_button_validate($form, &$form_state) {
  1530. if (isset($form_state['triggering_element'])) {
  1531. drupal_set_message(t('The clicked button is %name.', array('%name' => $form_state['triggering_element']['#name'])));
  1532. }
  1533. else {
  1534. drupal_set_message('There is no clicked button.');
  1535. }
  1536. }
  1537. /**
  1538. * Form submit handler for the form_test_clicked_button() form.
  1539. */
  1540. function form_test_clicked_button_submit($form, &$form_state) {
  1541. drupal_set_message('Submit handler for form_test_clicked_button executed.');
  1542. }
  1543. /**
  1544. * Form builder to detect form redirect.
  1545. */
  1546. function form_test_redirect($form, &$form_state) {
  1547. $form['redirection'] = array(
  1548. '#type' => 'checkbox',
  1549. '#title' => t('Use redirection'),
  1550. );
  1551. $form['destination'] = array(
  1552. '#type' => 'textfield',
  1553. '#title' => t('Redirect destination'),
  1554. '#states' => array(
  1555. 'visible' => array(
  1556. ':input[name="redirection"]' => array('checked' => TRUE),
  1557. ),
  1558. ),
  1559. );
  1560. $form['submit'] = array(
  1561. '#type' => 'submit',
  1562. '#value' => t('Submit'),
  1563. );
  1564. return $form;
  1565. }
  1566. /**
  1567. * Form submit handler to test different redirect behaviours.
  1568. */
  1569. function form_test_redirect_submit(&$form, &$form_state) {
  1570. if (!empty($form_state['values']['redirection'])) {
  1571. $form_state['redirect'] = !empty($form_state['values']['destination']) ? $form_state['values']['destination'] : NULL;
  1572. }
  1573. else {
  1574. $form_state['redirect'] = FALSE;
  1575. }
  1576. }
  1577. /**
  1578. * Implements hook_form_FORM_ID_alter() for the registration form.
  1579. */
  1580. function form_test_form_user_register_form_alter(&$form, &$form_state) {
  1581. $form['test_rebuild'] = array(
  1582. '#type' => 'submit',
  1583. '#value' => t('Rebuild'),
  1584. '#submit' => array('form_test_user_register_form_rebuild'),
  1585. );
  1586. // If requested, add the test field by attaching the node page form.
  1587. if (!empty($_REQUEST['field'])) {
  1588. $node = (object)array('type' => 'page');
  1589. field_attach_form('node', $node, $form, $form_state);
  1590. }
  1591. }
  1592. /**
  1593. * Submit callback that just lets the form rebuild.
  1594. */
  1595. function form_test_user_register_form_rebuild($form, &$form_state) {
  1596. drupal_set_message('Form rebuilt.');
  1597. $form_state['rebuild'] = TRUE;
  1598. }
  1599. /**
  1600. * Menu callback that returns two instances of the node form.
  1601. */
  1602. function form_test_two_instances() {
  1603. global $user;
  1604. $node1 = (object) array(
  1605. 'uid' => $user->uid,
  1606. 'name' => (isset($user->name) ? $user->name : ''),
  1607. 'type' => 'page',
  1608. 'language' => LANGUAGE_NONE,
  1609. );
  1610. $node2 = clone($node1);
  1611. $return['node_form_1'] = drupal_get_form('page_node_form', $node1);
  1612. $return['node_form_2'] = drupal_get_form('page_node_form', $node2);
  1613. return $return;
  1614. }
  1615. /**
  1616. * Menu callback for testing custom form includes.
  1617. */
  1618. function form_test_load_include_custom($form, &$form_state) {
  1619. $form['button'] = array(
  1620. '#type' => 'submit',
  1621. '#value' => t('Save'),
  1622. '#submit' => array('form_test_load_include_submit'),
  1623. );
  1624. // Specify the include file and enable form caching. That way the form is
  1625. // cached when it is submitted, but needs to find the specified submit handler
  1626. // in the include.
  1627. // Filename is a bit weird here: modules/simpletest/tests/form_test.file.inc
  1628. form_load_include($form_state, 'inc', 'form_test', 'form_test.file');
  1629. $form_state['cache'] = TRUE;
  1630. return $form;
  1631. }
  1632. function form_test_checkbox_type_juggling($form, $form_state, $default_value, $return_value) {
  1633. $form['checkbox'] = array(
  1634. '#type' => 'checkbox',
  1635. '#return_value' => $return_value,
  1636. '#default_value' => $default_value,
  1637. );
  1638. return $form;
  1639. }
  1640. function form_test_checkboxes_zero($form, &$form_state, $json = TRUE) {
  1641. $form['checkbox_off'] = array(
  1642. '#type' => 'checkboxes',
  1643. '#options' => array('foo', 'bar', 'baz'),
  1644. );
  1645. $form['checkbox_zero_default'] = array(
  1646. '#type' => 'checkboxes',
  1647. '#options' => array('foo', 'bar', 'baz'),
  1648. '#default_value' => array(0),
  1649. );
  1650. $form['checkbox_string_zero_default'] = array(
  1651. '#type' => 'checkboxes',
  1652. '#options' => array('foo', 'bar', 'baz'),
  1653. '#default_value' => array('0'),
  1654. );
  1655. $form['submit'] = array(
  1656. '#type' => 'submit',
  1657. '#value' => 'Save',
  1658. );
  1659. if ($json) {
  1660. $form['#submit'][] = '_form_test_checkbox_submit';
  1661. }
  1662. else {
  1663. $form['#submit'][] = '_form_test_checkboxes_zero_no_redirect';
  1664. }
  1665. return $form;
  1666. }
  1667. function _form_test_checkboxes_zero_no_redirect($form, &$form_state) {
  1668. $form_state['redirect'] = FALSE;
  1669. }
  1670. /**
  1671. * Menu callback returns two instances of the same form.
  1672. */
  1673. function form_test_double_form() {
  1674. return array(
  1675. 'form1' => drupal_get_form('form_test_html_id'),
  1676. 'form2' => drupal_get_form('form_test_html_id'),
  1677. );
  1678. }
  1679. /**
  1680. * Builds a simple form to test duplicate HTML IDs.
  1681. */
  1682. function form_test_html_id($form, &$form_state) {
  1683. $form['name'] = array(
  1684. '#type' => 'textfield',
  1685. '#title' => 'name',
  1686. '#required' => TRUE,
  1687. );
  1688. $form['submit'] = array(
  1689. '#type' => 'submit',
  1690. '#value' => 'Save',
  1691. );
  1692. return $form;
  1693. }