form.inc

  1. Views
    1. 6 includes/form.inc
  2. drupal
    1. 4.7 includes/form.inc
    2. 5 includes/form.inc
    3. 6 includes/form.inc
    4. 7 includes/form.inc
    5. 8 core/includes/form.inc

Functions & methods

NameDescription
checkboxes_valueHelper function to load value from default value for checkboxes
date_validateValidates the FAPI date type to stop dates like 30/Feb/2006
drupal_get_formProcesses a form array and produces the HTML output of a form. If there is input in the $_POST['edit'] variable, this function will attempt to validate it, using drupal_validate_form(), and then submit the form using drupal_submit_form().
drupal_submit_form
drupal_validate_form
element_childCheck if the key is a child.
element_childrenGet keys of a form tree element that are not properties (i.e., do not begin with '#').
element_propertiesGet properties of a form tree element. Properties begin with '#'.
element_propertyCheck if the key is a property.
expand_checkboxes
expand_dateRoll out a single date element.
expand_password_confirm
expand_radiosRoll out a single radios element to a list of radios, using the options array as index.
form_builderAdds some required properties to each form element, which are used internally in the form api. This function also automatically assigns the value property from the $edit array, provided the element doesn't already have an assigned value.
form_clean_idRemove invalid characters from an HTML ID attribute string.
form_errorFlag an element as having an error.
form_get_errorReturn the error message filed against the form with the specified name.
form_get_errorsReturn an associative array of all errors.
form_options_flatten
form_renderRenders a HTML form given a form tree. Recursively iterates over each of the form elements, generating HTML code. This function is usually called from within a theme. To render a form from within a module, use drupal_get_form().
form_select_options
form_set_errorFile an error against a form element. If the name of the element is edit[foo][bar] then you may pass either foo or foo][bar as $name foo will set an error for all its children.
form_set_valueUse this function to make changes to form values in the form validate phase, so they will be available in the submit phase in $form_values.
map_monthHelper function for usage with drupal_map_assoc to display month names.
password_confirm_validateValidate password_confirm element.
theme_button
theme_checkboxFormat a checkbox.
theme_checkboxesFormat a set of checkboxes.
theme_dateFormat a date selection element.
theme_fieldsetFormat a group of form items.
theme_fileFormat a file upload field.
theme_formFormat a form.
theme_hiddenFormat a hidden form field.
theme_itemFormat a form item.
theme_markup
theme_passwordFormat a password field. *
theme_password_confirmFormat a password_confirm item.
theme_radioFormat a radio button.
theme_radiosFormat a set of radio buttons.
theme_selectFormat a dropdown menu or scrolling selection box.
theme_submit
theme_textareaFormat a textarea.
theme_textfieldFormat a textfield.
theme_token
theme_weightFormat a weight selection menu.
weight_valueIf no default value is set for weight select boxes, use 0.
_element_infoRetrieve the default properties for the defined element type.
_form_set_classSets a form element's class attribute.
_form_set_valueHelper function for form_set_value().
_form_sortFunction used by uasort in form_render() to sort form by weight.
_form_validate

File

includes/form.inc
View source
  1. <?php
  2. /**
  3. * @defgroup form Form generation
  4. * @{
  5. * Functions to enable output of HTML forms and form elements.
  6. *
  7. * Drupal uses these functions to achieve consistency in its form presentation,
  8. * while at the same time simplifying code and reducing the amount of HTML that
  9. * must be explicitly generated by modules. See the reference at
  10. * http://api.drupal.org/api/HEAD/file/developer/topics/forms_api_reference.html
  11. * and the quickstart guide at
  12. * http://api.drupal.org/api/HEAD/file/developer/topics/forms_api.html
  13. */
  14. /**
  15. * Check if the key is a property.
  16. */
  17. function element_property($key) {
  18. return $key[0] == '#';
  19. }
  20. /**
  21. * Get properties of a form tree element. Properties begin with '#'.
  22. */
  23. function element_properties($element) {
  24. return array_filter(array_keys((array) $element), 'element_property');
  25. }
  26. /**
  27. * Check if the key is a child.
  28. */
  29. function element_child($key) {
  30. return $key[0] != '#';
  31. }
  32. /**
  33. * Get keys of a form tree element that are not properties (i.e., do not begin with '#').
  34. */
  35. function element_children($element) {
  36. return array_filter(array_keys((array) $element), 'element_child');
  37. }
  38. /**
  39. * Processes a form array and produces the HTML output of a form.
  40. * If there is input in the $_POST['edit'] variable, this function
  41. * will attempt to validate it, using drupal_validate_form(),
  42. * and then submit the form using drupal_submit_form().
  43. *
  44. * @param $form_id
  45. * A unique string identifying the form. Allows each form to be
  46. * themed. Pass NULL to suppress the form_id parameter (produces
  47. * a shorter URL with method=get)
  48. * @param $form
  49. * An associative array containing the structure of the form.
  50. * @param $callback
  51. * An optional callback that will be used in addition to the form_id.
  52. *
  53. */
  54. function drupal_get_form($form_id, &$form, $callback = NULL) {
  55. global $form_values, $form_submitted, $user, $form_button_counter;
  56. static $saved_globals = array();
  57. // Save globals in case of indirect recursive call
  58. array_push($saved_globals, array($form_values, $form_submitted, $form_button_counter));
  59. $form_values = array();
  60. $form_submitted = FALSE;
  61. $form_button_counter = array(0, 0);
  62. $form['#type'] = 'form';
  63. // Add a token to any form displayed to authenticated users.
  64. // This ensures that any submitted form was actually requested previously by the user to protect against
  65. // cross site request forgeries. The default token can be bypassed by setting $form['#token'] to FALSE.
  66. if (isset($form['#token'])) {
  67. if ($form['#token'] === FALSE || $user->uid == 0) {
  68. unset($form['#token']);
  69. }
  70. else {
  71. $form['form_token'] = array('#type' => 'token', '#default_value' => drupal_get_token($form['#token']));
  72. }
  73. }
  74. else if ($user->uid) {
  75. $form['#token'] = $form_id;
  76. $form['form_token'] = array(
  77. '#id' => 'edit-'. str_replace('_', '-', $form_id) .'-form-token',
  78. '#type' => 'token',
  79. '#default_value' => drupal_get_token($form['#token']),
  80. );
  81. }
  82. if (isset($form_id)) {
  83. $form['form_id'] = array('#type' => 'hidden', '#value' => $form_id, '#id' => str_replace('_', '-', "edit-$form_id"));
  84. }
  85. if (!isset($form['#id'])) {
  86. $form['#id'] = $form_id;
  87. }
  88. $form += _element_info('form');
  89. if (!isset($form['#validate'])) {
  90. if (function_exists($form_id .'_validate')) {
  91. $form['#validate'] = array($form_id .'_validate' => array());
  92. }
  93. elseif (function_exists($callback .'_validate')) {
  94. $form['#validate'] = array($callback .'_validate' => array());
  95. }
  96. }
  97. if (!isset($form['#submit'])) {
  98. if (function_exists($form_id .'_submit')) {
  99. // we set submit here so that it can be altered but use reference for
  100. // $form_values because it will change later
  101. $form['#submit'] = array($form_id .'_submit' => array());
  102. }
  103. elseif (function_exists($callback .'_submit')) {
  104. $form['#submit'] = array($callback .'_submit' => array());
  105. }
  106. }
  107. foreach (module_implements('form_alter') as $module) {
  108. $function = $module .'_form_alter';
  109. $function($form_id, $form);
  110. }
  111. $form = form_builder($form_id, $form);
  112. if (!empty($_POST['edit']) && (($_POST['edit']['form_id'] == $form_id) || ($_POST['edit']['form_id'] == $callback))) {
  113. drupal_validate_form($form_id, $form, $callback);
  114. // IE does not send a button value when there is only one submit button (and no non-submit buttons)
  115. // and you submit by pressing enter.
  116. // In that case we accept a submission without button values.
  117. if (($form_submitted || (!$form_button_counter[0] && $form_button_counter[1])) && !form_get_errors()) {
  118. $redirect = drupal_submit_form($form_id, $form, $callback);
  119. if (isset($redirect)) {
  120. $goto = $redirect;
  121. }
  122. if (isset($form['#redirect'])) {
  123. $goto = $form['#redirect'];
  124. }
  125. if ($goto !== FALSE) {
  126. if (is_array($goto)) {
  127. call_user_func_array('drupal_goto', $goto);
  128. }
  129. elseif (!isset($goto)) {
  130. drupal_goto($_GET['q']);
  131. }
  132. else {
  133. drupal_goto($goto);
  134. }
  135. }
  136. }
  137. }
  138. // Don't override #theme if someone already set it.
  139. if (!isset($form['#theme'])) {
  140. if (theme_get_function($form_id)) {
  141. $form['#theme'] = $form_id;
  142. }
  143. elseif (theme_get_function($callback)) {
  144. $form['#theme'] = $callback;
  145. }
  146. }
  147. if (isset($form['#pre_render'])) {
  148. foreach ($form['#pre_render'] as $function) {
  149. if (function_exists($function)) {
  150. $function($form_id, $form);
  151. }
  152. }
  153. }
  154. $output = form_render($form);
  155. // Restore globals
  156. list($form_values, $form_submitted, $form_button_counter) = array_pop($saved_globals);
  157. return $output;
  158. }
  159. function drupal_validate_form($form_id, $form, $callback = NULL) {
  160. global $form_values;
  161. static $validated_forms = array();
  162. if (isset($validated_forms[$form_id])) {
  163. return;
  164. }
  165. // Check whether the form token is valid.
  166. if (isset($form['#token'])) {
  167. if (!drupal_valid_token($form_values['form_token'], $form['#token'])) {
  168. // setting this error will cause the form to fail validation
  169. form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));
  170. }
  171. }
  172. _form_validate($form, $form_id);
  173. $validated_forms[$form_id] = TRUE;
  174. }
  175. function drupal_submit_form($form_id, $form, $callback = NULL) {
  176. global $form_values;
  177. $default_args = array($form_id, &$form_values);
  178. if (isset($form['#submit'])) {
  179. foreach ($form['#submit'] as $function => $args) {
  180. if (function_exists($function)) {
  181. $args = array_merge($default_args, (array) $args);
  182. // Since we can only redirect to one page, only the last redirect will work
  183. $redirect = call_user_func_array($function, $args);
  184. if (isset($redirect)) {
  185. $goto = $redirect;
  186. }
  187. }
  188. }
  189. }
  190. return $goto;
  191. }
  192. function _form_validate($elements, $form_id = NULL) {
  193. // Recurse through all children.
  194. foreach (element_children($elements) as $key) {
  195. if (isset($elements[$key]) && $elements[$key]) {
  196. _form_validate($elements[$key]);
  197. }
  198. }
  199. /* Validate the current input */
  200. if (!$elements['#validated']) {
  201. if (isset($elements['#needs_validation'])) {
  202. // An empty textfield returns '' so we use empty(). An empty checkbox
  203. // and a textfield could return '0' and empty('0') returns TRUE so we
  204. // need a special check for the '0' string.
  205. if ($elements['#required'] && empty($elements['#value']) && $elements['#value'] !== '0') {
  206. form_error($elements, t('%name field is required.', array('%name' => $elements['#title'])));
  207. }
  208. // Add legal choice check if element has #options. Can be skipped, but then you must validate your own element.
  209. if (isset($elements['#options']) && isset($elements['#value']) && !isset($elements['#DANGEROUS_SKIP_CHECK'])) {
  210. if ($elements['#type'] == 'select') {
  211. $options = form_options_flatten($elements['#options']);
  212. }
  213. else {
  214. $options = $elements['#options'];
  215. }
  216. if (is_array($elements['#value'])) {
  217. $value = $elements['#type'] == 'checkboxes' ? array_keys(array_filter($elements['#value'])) : $elements['#value'];
  218. foreach ($value as $v) {
  219. if (!isset($options[$v])) {
  220. form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
  221. watchdog('form', t('Illegal choice %choice in %name element.', array('%choice' => theme('placeholder', check_plain($v)), '%name' => theme('placeholder', empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']))), WATCHDOG_ERROR);
  222. }
  223. }
  224. }
  225. elseif (!isset($options[$elements['#value']])) {
  226. form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
  227. watchdog('form', t('Illegal choice %choice in %name element.', array('%choice' => theme('placeholder', $elements['#value']), '%name' => theme('placeholder', empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']))), WATCHDOG_ERROR);
  228. }
  229. }
  230. }
  231. // User-applied checks.
  232. if (isset($elements['#validate'])) {
  233. foreach ($elements['#validate'] as $function => $args) {
  234. $args = array_merge(array($elements), $args);
  235. // for the full form we hand over a copy of $form_values
  236. if (isset($form_id)) {
  237. $args = array_merge(array($form_id, $GLOBALS['form_values']), $args);
  238. }
  239. if (function_exists($function)) {
  240. call_user_func_array($function, $args);
  241. }
  242. }
  243. }
  244. $elements['#validated'] = TRUE;
  245. }
  246. }
  247. /**
  248. * File an error against a form element. If the name of the element is
  249. * edit[foo][bar] then you may pass either foo or foo][bar as $name
  250. * foo will set an error for all its children.
  251. */
  252. function form_set_error($name = NULL, $message = '') {
  253. static $form = array();
  254. if (isset($name) && !isset($form[$name])) {
  255. $form[$name] = $message;
  256. if ($message) {
  257. drupal_set_message($message, 'error');
  258. }
  259. }
  260. return $form;
  261. }
  262. /**
  263. * Return an associative array of all errors.
  264. */
  265. function form_get_errors() {
  266. $form = form_set_error();
  267. if (!empty($form)) {
  268. return $form;
  269. }
  270. }
  271. /**
  272. * Return the error message filed against the form with the specified name.
  273. */
  274. function form_get_error($element) {
  275. $form = form_set_error();
  276. $key = $element['#parents'][0];
  277. if (isset($form[$key])) {
  278. return $form[$key];
  279. }
  280. $key = implode('][', $element['#parents']);
  281. if (isset($form[$key])) {
  282. return $form[$key];
  283. }
  284. }
  285. /**
  286. * Flag an element as having an error.
  287. */
  288. function form_error(&$element, $message = '') {
  289. $element['#error'] = TRUE;
  290. form_set_error(implode('][', $element['#parents']), $message);
  291. }
  292. /**
  293. * Adds some required properties to each form element, which are used
  294. * internally in the form api. This function also automatically assigns
  295. * the value property from the $edit array, provided the element doesn't
  296. * already have an assigned value.
  297. *
  298. * @param $form_id
  299. * A unique string identifying the form. Allows each form to be themed.
  300. * @param $form
  301. * An associative array containing the structure of the form.
  302. */
  303. function form_builder($form_id, $form) {
  304. global $form_values, $form_submitted, $form_button_counter;
  305. // Initialize as unprocessed.
  306. $form['#processed'] = FALSE;
  307. /* Use element defaults */
  308. if ((!empty($form['#type'])) && ($info = _element_info($form['#type']))) {
  309. // overlay $info onto $form, retaining preexisting keys in $form
  310. $form += $info;
  311. }
  312. if (isset($form['#input']) && $form['#input']) {
  313. if (!isset($form['#name'])) {
  314. $form['#name'] = 'edit[' . implode('][', $form['#parents']) . ']';
  315. }
  316. if (!isset($form['#id'])) {
  317. $form['#id'] = 'edit-' . implode('-', $form['#parents']);
  318. }
  319. $posted = (isset($_POST['edit']) && ($_POST['edit']['form_id'] == $form_id));
  320. $edit = $posted ? $_POST['edit'] : array();
  321. foreach ($form['#parents'] as $parent) {
  322. $edit = isset($edit[$parent]) ? $edit[$parent] : NULL;
  323. }
  324. if (!isset($form['#value']) && !array_key_exists('#value', $form)) {
  325. if ($posted) {
  326. switch ($form['#type']) {
  327. case 'checkbox':
  328. $form['#value'] = !empty($edit) ? $form['#return_value'] : 0;
  329. break;
  330. case 'select':
  331. if (isset($form['#multiple']) && $form['#multiple']) {
  332. if (isset($edit) && is_array($edit)) {
  333. $form['#value'] = drupal_map_assoc($edit);
  334. }
  335. else {
  336. $form['#value'] = array();
  337. }
  338. }
  339. elseif (isset($edit)) {
  340. $form['#value'] = $edit;
  341. }
  342. break;
  343. case 'textfield':
  344. if (isset($edit)) {
  345. // Equate $edit to the form value to ensure it's marked for validation
  346. $edit = str_replace(array("\r", "\n"), '', $edit);
  347. $form['#value'] = $edit;
  348. }
  349. break;
  350. case 'token':
  351. $form['#value'] = (string)$edit;
  352. break;
  353. default:
  354. if (isset($edit)) {
  355. $form['#value'] = $edit;
  356. }
  357. }
  358. // Mark all posted values for validation
  359. if ((isset($form['#value']) && $form['#value'] === $edit) || (isset($form['#required']) && $form['#required'])) {
  360. $form['#needs_validation'] = TRUE;
  361. }
  362. }
  363. if (!isset($form['#value'])) {
  364. $function = $form['#type'] . '_value';
  365. if (function_exists($function)) {
  366. $function($form);
  367. }
  368. else {
  369. $form['#value'] = $form['#default_value'];
  370. }
  371. }
  372. }
  373. if (isset($form['#executes_submit_callback'])) {
  374. // Count submit and non-submit buttons
  375. $form_button_counter[$form['#executes_submit_callback']]++;
  376. // See if a submit button was pressed
  377. if (isset($_POST[$form['#name']]) && $_POST[$form['#name']] == $form['#value']) {
  378. $form_submitted = $form_submitted || $form['#executes_submit_callback'];
  379. }
  380. }
  381. }
  382. // Allow for elements to expand to multiple elements, e.g. radios, checkboxes and files.
  383. if (isset($form['#process']) && !$form['#processed']) {
  384. foreach ($form['#process'] as $process => $args) {
  385. if (function_exists($process)) {
  386. $args = array_merge(array($form), $args);
  387. $form = call_user_func_array($process, $args);
  388. }
  389. }
  390. $form['#processed'] = TRUE;
  391. }
  392. // Set the $form_values key that gets passed to validate and submit.
  393. // We call this after #process gets called so that #process has a
  394. // chance to update #value if desired.
  395. if (isset($form['#input']) && $form['#input']) {
  396. form_set_value($form, $form['#value']);
  397. }
  398. // Recurse through all child elements.
  399. $count = 0;
  400. foreach (element_children($form) as $key) {
  401. // don't squash an existing tree value
  402. if (!isset($form[$key]['#tree'])) {
  403. $form[$key]['#tree'] = $form['#tree'];
  404. }
  405. // don't squash existing parents value
  406. if (!isset($form[$key]['#parents'])) {
  407. // Check to see if a tree of child elements is present. If so, continue down the tree if required.
  408. $form[$key]['#parents'] = $form[$key]['#tree'] && $form['#tree'] ? array_merge($form['#parents'], array($key)) : array($key);
  409. }
  410. # Assign a decimal placeholder weight to preserve original array order
  411. if (!isset($form[$key]['#weight'])) {
  412. $form[$key]['#weight'] = $count/1000;
  413. }
  414. $form[$key] = form_builder($form_id, $form[$key]);
  415. $count++;
  416. }
  417. if (isset($form['#after_build']) && !isset($form['#after_build_done'])) {
  418. foreach ($form['#after_build'] as $function) {
  419. if (function_exists($function)) {
  420. $form = $function($form, $form_values);
  421. }
  422. }
  423. $form['#after_build_done'] = TRUE;
  424. }
  425. return $form;
  426. }
  427. /**
  428. * Use this function to make changes to form values in the form validate
  429. * phase, so they will be available in the submit phase in $form_values.
  430. *
  431. * Specifically, if $form['#parents'] is array('foo', 'bar')
  432. * and $value is 'baz' then this function will make
  433. * $form_values['foo']['bar'] to be 'baz'.
  434. *
  435. * @param $form
  436. * The form item. Keys used: #parents, #value
  437. * @param $value
  438. * The value for the form item.
  439. */
  440. function form_set_value($form, $value) {
  441. global $form_values;
  442. _form_set_value($form_values, $form, $form['#parents'], $value);
  443. }
  444. /**
  445. * Helper function for form_set_value().
  446. *
  447. * We iterate of $parents and create nested arrays for them
  448. * in $form_values if needed. Then we insert the value in
  449. * the right array.
  450. */
  451. function _form_set_value(&$form_values, $form, $parents, $value) {
  452. $parent = array_shift($parents);
  453. if (empty($parents)) {
  454. $form_values[$parent] = $value;
  455. }
  456. else {
  457. if (!isset($form_values[$parent])) {
  458. $form_values[$parent] = array();
  459. }
  460. _form_set_value($form_values[$parent], $form, $parents, $value);
  461. }
  462. return $form;
  463. }
  464. /**
  465. * Renders a HTML form given a form tree. Recursively iterates over each of
  466. * the form elements, generating HTML code. This function is usually
  467. * called from within a theme. To render a form from within a module, use
  468. * drupal_get_form().
  469. *
  470. * @param $elements
  471. * The form tree describing the form.
  472. * @return
  473. * The rendered HTML form.
  474. */
  475. function form_render(&$elements) {
  476. if (!isset($elements)) {
  477. return NULL;
  478. }
  479. $content = '';
  480. uasort($elements, "_form_sort");
  481. if (!isset($elements['#children'])) {
  482. $children = element_children($elements);
  483. /* Render all the children that use a theme function */
  484. if (isset($elements['#theme']) && !$elements['#theme_used']) {
  485. $elements['#theme_used'] = TRUE;
  486. $previous = array();
  487. foreach (array('#value', '#type', '#prefix', '#suffix') as $key) {
  488. $previous[$key] = isset($elements[$key]) ? $elements[$key] : NULL;
  489. }
  490. // If we rendered a single element, then we will skip the renderer.
  491. if (empty($children)) {
  492. $elements['#printed'] = TRUE;
  493. }
  494. else {
  495. $elements['#value'] = '';
  496. }
  497. $elements['#type'] = 'markup';
  498. unset($elements['#prefix'], $elements['#suffix']);
  499. $content = theme($elements['#theme'], $elements);
  500. foreach (array('#value', '#type', '#prefix', '#suffix') as $key) {
  501. $elements[$key] = isset($previous[$key]) ? $previous[$key] : NULL;
  502. }
  503. }
  504. /* render each of the children using form_render and concatenate them */
  505. if (!isset($content) || $content === '') {
  506. foreach ($children as $key) {
  507. $content .= form_render($elements[$key]);
  508. }
  509. }
  510. }
  511. if (isset($content) && $content !== '') {
  512. $elements['#children'] = $content;
  513. }
  514. // Until now, we rendered the children, here we render the element itself
  515. if (!isset($elements['#printed'])) {
  516. $content = theme(($elements['#type']) ? $elements['#type']: 'markup', $elements);
  517. $elements['#printed'] = TRUE;
  518. }
  519. if (isset($content) && $content !== '') {
  520. $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : '';
  521. $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
  522. return $prefix . $content . $suffix;
  523. }
  524. }
  525. /**
  526. * Function used by uasort in form_render() to sort form by weight.
  527. */
  528. function _form_sort($a, $b) {
  529. $a_weight = (is_array($a) && isset($a['#weight'])) ? $a['#weight'] : 0;
  530. $b_weight = (is_array($b) && isset($b['#weight'])) ? $b['#weight'] : 0;
  531. if ($a_weight == $b_weight) {
  532. return 0;
  533. }
  534. return ($a_weight < $b_weight) ? -1 : 1;
  535. }
  536. /**
  537. * Retrieve the default properties for the defined element type.
  538. */
  539. function _element_info($type, $refresh = null) {
  540. static $cache;
  541. $basic_defaults = array(
  542. '#description' => NULL,
  543. '#attributes' => array(),
  544. '#required' => FALSE,
  545. '#tree' => FALSE,
  546. '#parents' => array()
  547. );
  548. if (!isset($cache) || $refresh) {
  549. $cache = array();
  550. foreach (module_implements('elements') as $module) {
  551. $elements = module_invoke($module, 'elements');
  552. if (isset($elements) && is_array($elements)) {
  553. $cache = array_merge_recursive($cache, $elements);
  554. }
  555. }
  556. if (sizeof($cache)) {
  557. foreach ($cache as $element_type => $info) {
  558. $cache[$element_type] = array_merge_recursive($basic_defaults, $info);
  559. }
  560. }
  561. }
  562. return $cache[$type];
  563. }
  564. function form_options_flatten($array, $reset = TRUE) {
  565. static $return;
  566. if ($reset) {
  567. $return = array();
  568. }
  569. foreach ($array as $key => $value) {
  570. if (is_array($value)) {
  571. form_options_flatten($value, FALSE);
  572. }
  573. else {
  574. $return[$key] = 1;
  575. }
  576. }
  577. return $return;
  578. }
  579. /**
  580. * Format a dropdown menu or scrolling selection box.
  581. *
  582. * @param $element
  583. * An associative array containing the properties of the element.
  584. * Properties used: title, value, options, description, extra, multiple, required
  585. * @return
  586. * A themed HTML string representing the form element.
  587. *
  588. * It is possible to group options together; to do this, change the format of
  589. * $options to an associative array in which the keys are group labels, and the
  590. * values are associative arrays in the normal $options format.
  591. */
  592. function theme_select($element) {
  593. $select = '';
  594. $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';
  595. _form_set_class($element, array('form-select'));
  596. return theme('form_element', $element['#title'], '<select name="'. $element['#name'] .''. ($element['#multiple'] ? '[]' : '') .'"'. ($element['#multiple'] ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) .' id="' . $element['#id'] .'" '. $size .'>'. form_select_options($element) .'</select>', $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
  597. }
  598. function form_select_options($element, $choices = NULL) {
  599. if (!isset($choices)) {
  600. $choices = $element['#options'];
  601. }
  602. // array_key_exists() accommodates the rare event where $element['#value'] is NULL.
  603. // isset() fails in this situation.
  604. $value_valid = isset($element['#value']) || array_key_exists('#value', $element);
  605. $value_is_array = is_array($element['#value']);
  606. $options = '';
  607. foreach ($choices as $key => $choice) {
  608. if (is_array($choice)) {
  609. $options .= '<optgroup label="'. $key .'">';
  610. $options .= form_select_options($element, $choice);
  611. $options .= '</optgroup>';
  612. }
  613. else {
  614. $key = (string)$key;
  615. if ($value_valid && ($element['#value'] == $key || ($value_is_array && in_array($key, $element['#value'])))) {
  616. $selected = ' selected="selected"';
  617. }
  618. else {
  619. $selected = '';
  620. }
  621. $options .= '<option value="'. check_plain($key) .'"'. $selected .'>'. check_plain($choice) .'</option>';
  622. }
  623. }
  624. return $options;
  625. }
  626. /**
  627. * Format a group of form items.
  628. *
  629. * @param $element
  630. * An associative array containing the properties of the element.
  631. * Properties used: attributes, title, value, description, children, collapsible, collapsed
  632. * @return
  633. * A themed HTML string representing the form item group.
  634. */
  635. function theme_fieldset($element) {
  636. if ($element['#collapsible']) {
  637. drupal_add_js('misc/collapse.js');
  638. $element['#attributes']['class'] .= ' collapsible';
  639. if ($element['#collapsed']) {
  640. $element['#attributes']['class'] .= ' collapsed';
  641. }
  642. }
  643. return '<fieldset' . drupal_attributes($element['#attributes']) .'>' . ($element['#title'] ? '<legend>'. $element['#title'] .'</legend>' : '') . ($element['#description'] ? '<div class="description">'. $element['#description'] .'</div>' : '') . $element['#children'] . $element['#value'] . "</fieldset>\n";
  644. }
  645. /**
  646. * Format a radio button.
  647. *
  648. * @param $element
  649. * An associative array containing the properties of the element.
  650. * Properties used: required, return_value, value, attributes, title, description
  651. * @return
  652. * A themed HTML string representing the form item group.
  653. */
  654. function theme_radio($element) {
  655. _form_set_class($element, array('form-radio'));
  656. $output = '<input type="radio" ';
  657. $output .= 'name="' . $element['#name'] .'" ';
  658. $output .= 'value="'. $element['#return_value'] .'" ';
  659. $output .= (check_plain($element['#value']) == $element['#return_value']) ? ' checked="checked" ' : ' ';
  660. $output .= drupal_attributes($element['#attributes']) .' />';
  661. if (!is_null($element['#title'])) {
  662. $output = '<label class="option">'. $output .' '. $element['#title'] .'</label>';
  663. }
  664. return theme('form_element', NULL, $output, $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
  665. }
  666. /**
  667. * Format a set of radio buttons.
  668. *
  669. * @param $element
  670. * An associative array containing the properties of the element.
  671. * Properties used: title, value, options, description, required and attributes.
  672. * @return
  673. * A themed HTML string representing the radio button set.
  674. */
  675. function theme_radios($element) {
  676. if ($element['#title'] || $element['#description']) {
  677. return theme('form_element', $element['#title'], $element['#children'], $element['#description'], NULL, $element['#required'], form_get_error($element));
  678. }
  679. else {
  680. return $element['#children'];
  681. }
  682. }
  683. /**
  684. * Format a password_confirm item.
  685. *
  686. * @param $element
  687. * An associative array containing the properties of the element.
  688. * Properties used: title, value, id, required, error.
  689. * @return
  690. * A themed HTML string representing the form item.
  691. */
  692. function theme_password_confirm($element) {
  693. return theme('form_element', $element['#title'], '<div class="container-inline">'. $element['#children']. '</div>', $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
  694. }
  695. /*
  696. * Expand a password_confirm field into two text boxes.
  697. */
  698. function expand_password_confirm($element) {
  699. $element['pass1'] = array('#type' => 'password', '#size' => 12, '#value' => $element['#value']['pass1']);
  700. $element['pass2'] = array('#type' => 'password', '#size' => 12, '#value' => $element['#value']['pass2']);
  701. $element['#validate'] = array('password_confirm_validate' => array());
  702. $element['#tree'] = TRUE;
  703. return $element;
  704. }
  705. /**
  706. * Validate password_confirm element.
  707. */
  708. function password_confirm_validate($form) {
  709. $pass1 = trim($form['pass1']['#value']);
  710. if (!empty($pass1)) {
  711. $pass2 = trim($form['pass2']['#value']);
  712. if ($pass1 != $pass2) {
  713. form_error($form, t('The specified passwords do not match.'));
  714. }
  715. }
  716. elseif ($form['#required'] && !empty($_POST['edit'])) {
  717. form_error($form, t('Password field is required.'));
  718. }
  719. // Password field must be converted from a two-element array into a single
  720. // string regardless of validation results.
  721. form_set_value($form['pass1'], NULL);
  722. form_set_value($form['pass2'], NULL);
  723. form_set_value($form, $pass1);
  724. return $form;
  725. }
  726. /**
  727. * Format a date selection element.
  728. *
  729. * @param $element
  730. * An associative array containing the properties of the element.
  731. * Properties used: title, value, options, description, required and attributes.
  732. * @return
  733. * A themed HTML string representing the date selection boxes.
  734. */
  735. function theme_date($element) {
  736. $output = '<div class="container-inline">' . $element['#children'] . '</div>';
  737. return theme('form_element', $element['#title'], $output, $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
  738. }
  739. /**
  740. * Roll out a single date element.
  741. */
  742. function expand_date($element) {
  743. // Default to current date
  744. if (!isset($element['#value'])) {
  745. $element['#value'] = array('day' => format_date(time(), 'custom', 'j'),
  746. 'month' => format_date(time(), 'custom', 'n'),
  747. 'year' => format_date(time(), 'custom', 'Y'));
  748. }
  749. $element['#tree'] = TRUE;
  750. // Determine the order of day, month, year in the site's chosen date format.
  751. $format = variable_get('date_format_short', 'm/d/Y');
  752. $sort = array();
  753. $sort['day'] = max(strpos($format, 'd'), strpos($format, 'j'));
  754. $sort['month'] = max(strpos($format, 'm'), strpos($format, 'M'));
  755. $sort['year'] = strpos($format, 'Y');
  756. asort($sort);
  757. $order = array_keys($sort);
  758. // Output multi-selector for date
  759. foreach ($order as $type) {
  760. switch ($type) {
  761. case 'day':
  762. $options = drupal_map_assoc(range(1, 31));
  763. break;
  764. case 'month':
  765. $options = drupal_map_assoc(range(1, 12), 'map_month');
  766. break;
  767. case 'year':
  768. $options = drupal_map_assoc(range(1900, 2050));
  769. break;
  770. }
  771. $parents = $element['#parents'];
  772. $parents[] = $type;
  773. $element[$type] = array(
  774. '#type' => 'select',
  775. '#value' => $element['#value'][$type],
  776. '#attributes' => $element['#attributes'],
  777. '#options' => $options,
  778. );
  779. }
  780. return $element;
  781. }
  782. /**
  783. * Validates the FAPI date type to stop dates like 30/Feb/2006
  784. */
  785. function date_validate($form) {
  786. if (!checkdate($form['#value']['month'], $form['#value']['day'], $form['#value']['year'])) {
  787. form_error($form, t('The specified date is invalid.'));
  788. }
  789. }
  790. /**
  791. * Helper function for usage with drupal_map_assoc to display month names.
  792. */
  793. function map_month($month) {
  794. return format_date(gmmktime(0, 0, 0, $month, 2, 1970), 'custom', 'M', 0);
  795. }
  796. /**
  797. * Helper function to load value from default value for checkboxes
  798. */
  799. function checkboxes_value(&$form) {
  800. $value = array();
  801. foreach ((array)$form['#default_value'] as $key) {
  802. $value[$key] = 1;
  803. }
  804. $form['#value'] = $value;
  805. }
  806. /**
  807. * If no default value is set for weight select boxes, use 0.
  808. */
  809. function weight_value(&$form) {
  810. if (isset($form['#default_value'])) {
  811. $form['#value'] = $form['#default_value'];
  812. }
  813. else {
  814. $form['#value'] = 0;
  815. }
  816. }
  817. /**
  818. * Roll out a single radios element to a list of radios,
  819. * using the options array as index.
  820. */
  821. function expand_radios($element) {
  822. if (count($element['#options']) > 0) {
  823. foreach ($element['#options'] as $key => $choice) {
  824. if (!isset($element[$key])) {
  825. $element[$key] = array('#type' => 'radio', '#title' => $choice, '#return_value' => check_plain($key), '#default_value' => $element['#default_value'], '#attributes' => $element['#attributes'], '#parents' => $element['#parents'], '#spawned' => TRUE);
  826. }
  827. }
  828. }
  829. return $element;
  830. }
  831. /**
  832. * Format a form item.
  833. *
  834. * @param $element
  835. * An associative array containing the properties of the element.
  836. * Properties used: title, value, description, required, error
  837. * @return
  838. * A themed HTML string representing the form item.
  839. */
  840. function theme_item($element) {
  841. return theme('form_element', $element['#title'], $element['#value'] . $element['#children'], $element['#description'], $element['#id'], $element['#required'], $element['#error']);
  842. }
  843. /**
  844. * Format a checkbox.
  845. *
  846. * @param $element
  847. * An associative array containing the properties of the element.
  848. * Properties used: title, value, return_value, description, required
  849. * @return
  850. * A themed HTML string representing the checkbox.
  851. */
  852. function theme_checkbox($element) {
  853. _form_set_class($element, array('form-checkbox'));
  854. $checkbox = '<input ';
  855. $checkbox .= 'type="checkbox" ';
  856. $checkbox .= 'name="'. $element['#name'] .'" ';
  857. $checkbox .= 'id="'. $element['#id'].'" ' ;
  858. $checkbox .= 'value="'. $element['#return_value'] .'" ';
  859. $checkbox .= $element['#value'] ? ' checked="checked" ' : ' ';
  860. $checkbox .= drupal_attributes($element['#attributes']) . ' />';
  861. if (!is_null($element['#title'])) {
  862. $checkbox = '<label class="option">'. $checkbox .' '. $element['#title'] .'</label>';
  863. }
  864. return theme('form_element', NULL, $checkbox, $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
  865. }
  866. /**
  867. * Format a set of checkboxes.
  868. *
  869. * @param $element
  870. * An associative array containing the properties of the element.
  871. * @return
  872. * A themed HTML string representing the checkbox set.
  873. */
  874. function theme_checkboxes($element) {
  875. if ($element['#title'] || $element['#description']) {
  876. return theme('form_element', $element['#title'], $element['#children'], $element['#description'], NULL, $element['#required'], form_get_error($element));
  877. }
  878. else {
  879. return $element['#children'];
  880. }
  881. }
  882. function expand_checkboxes($element) {
  883. $value = is_array($element['#value']) ? $element['#value'] : array();
  884. $element['#tree'] = TRUE;
  885. if (count($element['#options']) > 0) {
  886. if (!isset($element['#default_value']) || $element['#default_value'] == 0) {
  887. $element['#default_value'] = array();
  888. }
  889. foreach ($element['#options'] as $key => $choice) {
  890. if (!isset($element[$key])) {
  891. $element[$key] = array('#type' => 'checkbox', '#processed' => TRUE, '#title' => $choice, '#return_value' => $key, '#default_value' => isset($value[$key]), '#attributes' => $element['#attributes']);
  892. }
  893. }
  894. }
  895. return $element;
  896. }
  897. function theme_submit($element) {
  898. return theme('button', $element);
  899. }
  900. function theme_button($element) {
  901. //Make sure not to overwrite classes
  902. if (isset($element['#attributes']['class'])) {
  903. $element['#attributes']['class'] = 'form-'. $element['#button_type'] .' '. $element['#attributes']['class'];
  904. }
  905. else {
  906. $element['#attributes']['class'] = 'form-'. $element['#button_type'];
  907. }
  908. return '<input type="submit" '. (empty($element['#name']) ? '' : 'name="'. $element['#name'] .'" ') .'value="'. check_plain($element['#value']) .'" '. drupal_attributes($element['#attributes']) ." />\n";
  909. }
  910. /**
  911. * Format a hidden form field.
  912. *
  913. * @param $element
  914. * An associative array containing the properties of the element.
  915. * Properties used: value, edit
  916. * @return
  917. * A themed HTML string representing the hidden form field.
  918. */
  919. function theme_hidden($element) {
  920. return '<input type="hidden" name="'. $element['#name'] . '" id="'. $element['#id'] . '" value="'. check_plain($element['#value']) ."\" " . drupal_attributes($element['#attributes']) ." />\n";
  921. }
  922. function theme_token($element) {
  923. return theme('hidden', $element);
  924. }
  925. /**
  926. * Format a textfield.
  927. *
  928. * @param $element
  929. * An associative array containing the properties of the element.
  930. * Properties used: title, value, description, size, maxlength, required, attributes autocomplete_path
  931. * @return
  932. * A themed HTML string representing the textfield.
  933. */
  934. function theme_textfield($element) {
  935. $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';
  936. $class = array('form-text');
  937. $extra = '';
  938. if ($element['#autocomplete_path']) {
  939. drupal_add_js('misc/autocomplete.js');
  940. $class[] = 'form-autocomplete';
  941. $extra = '<input class="autocomplete" type="hidden" id="'. $element['#id'] .'-autocomplete" value="'. check_url(url($element['#autocomplete_path'], NULL, NULL, TRUE)) .'" disabled="disabled" />';
  942. }
  943. _form_set_class($element, $class);
  944. $output = '<input type="text" maxlength="'. $element['#maxlength'] .'" name="'. $element['#name'] .'" id="'. $element['#id'] .'" '. $size .' value="'. check_plain($element['#value']) .'"'. drupal_attributes($element['#attributes']) .' />';
  945. return theme('form_element', $element['#title'], $output, $element['#description'], $element['#id'], $element['#required'], form_get_error($element)). $extra;
  946. }
  947. /**
  948. * Format a form.
  949. *
  950. * @param $element
  951. * An associative array containing the properties of the element.
  952. * Properties used: action, method, attributes, children
  953. * @return
  954. * A themed HTML string representing the form.
  955. */
  956. function theme_form($element) {
  957. // Anonymous div to satisfy XHTML compliance.
  958. $action = $element['#action'] ? 'action="' . check_url($element['#action']) . '" ' : '';
  959. return '<form '. $action . ' method="'. $element['#method'] .'" '. 'id="'. $element['#id'] .'"'. drupal_attributes($element['#attributes']) .">\n<div>". $element['#children'] ."\n</div></form>\n";
  960. }
  961. /**
  962. * Format a textarea.
  963. *
  964. * @param $element
  965. * An associative array containing the properties of the element.
  966. * Properties used: title, value, description, rows, cols, required, attributes
  967. * @return
  968. * A themed HTML string representing the textarea.
  969. */
  970. function theme_textarea($element) {
  971. $class = array('form-textarea');
  972. if ($element['#resizable'] !== false) {
  973. drupal_add_js('misc/textarea.js');
  974. $class[] = 'resizable';
  975. }
  976. $cols = $element['#cols'] ? ' cols="'. $element['#cols'] .'"' : '';
  977. _form_set_class($element, $class);
  978. return theme('form_element', $element['#title'], '<textarea'. $cols .' rows="'. $element['#rows'] .'" name="'. $element['#name'] .'" id="' . $element['#id'] .'" '. drupal_attributes($element['#attributes']) .'>'. check_plain($element['#value']) .'</textarea>', $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
  979. }
  980. /**
  981. * Format HTML markup for use in forms.
  982. *
  983. * This is used in more advanced forms, such as theme selection and filter format.
  984. *
  985. * @param $element
  986. * An associative array containing the properties of the element.
  987. * Properties used: prefix, value, children and suffix.
  988. * @return
  989. * A themed HTML string representing the HTML markup.
  990. */
  991. function theme_markup($element) {
  992. return $element['#value'] . $element['#children'];
  993. }
  994. /**
  995. * Format a password field.
  996. *
  997. * @param $element
  998. * An associative array containing the properties of the element.
  999. * Properties used: title, value, description, size, maxlength, required, attributes
  1000. * @return
  1001. * A themed HTML string representing the form.
  1002. */
  1003. function theme_password($element) {
  1004. $size = $element['#size'] ? ' size="'. $element['#size'] .'" ' : '';
  1005. $maxlength = $element['#maxlength'] ? ' maxlength="'. $element['#maxlength'] .'" ' : '';
  1006. _form_set_class($element, array('form-text'));
  1007. $output = '<input type="password" name="'. $element['#name'] .'" id="'. $element['#id'] .'" '. $maxlength . $size . drupal_attributes($element['#attributes']) .' />';
  1008. return theme('form_element', $element['#title'], $output, $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
  1009. }
  1010. /**
  1011. * Format a weight selection menu.
  1012. *
  1013. * @param $element
  1014. * An associative array containing the properties of the element.
  1015. * Properties used: title, delta, description
  1016. * @return
  1017. * A themed HTML string representing the form.
  1018. */
  1019. function theme_weight($element) {
  1020. for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) {
  1021. $weights[$n] = $n;
  1022. }
  1023. $element['#options'] = $weights;
  1024. $element['#type'] = 'select';
  1025. return form_render($element);
  1026. }
  1027. /**
  1028. * Format a file upload field.
  1029. *
  1030. * @param $title
  1031. * The label for the file upload field.
  1032. * @param $name
  1033. * The internal name used to refer to the field.
  1034. * @param $size
  1035. * A measure of the visible size of the field (passed directly to HTML).
  1036. * @param $description
  1037. * Explanatory text to display after the form item.
  1038. * @param $required
  1039. * Whether the user must upload a file to the field.
  1040. * @return
  1041. * A themed HTML string representing the field.
  1042. *
  1043. * For assistance with handling the uploaded file correctly, see the API
  1044. * provided by file.inc.
  1045. */
  1046. function theme_file($element) {
  1047. _form_set_class($element, array('form-file'));
  1048. return theme('form_element', $element['#title'], '<input type="file" name="'. $element['#name'] .'"'. ($element['#attributes'] ? ' '. drupal_attributes($element['#attributes']) : '') .' id="'. form_clean_id($element['#id']) .'" size="'. $element['#size'] ."\" />\n", $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
  1049. }
  1050. /**
  1051. * Sets a form element's class attribute.
  1052. *
  1053. * Adds 'required' and 'error' classes as needed.
  1054. *
  1055. * @param &$element
  1056. * The form element
  1057. * @param $name
  1058. * Array of new class names to be added
  1059. */
  1060. function _form_set_class(&$element, $class = array()) {
  1061. if ($element['#required']) {
  1062. $class[] = 'required';
  1063. }
  1064. if (form_get_error($element)){
  1065. $class[] = 'error';
  1066. }
  1067. if (isset($element['#attributes']['class'])) {
  1068. $class[] = $element['#attributes']['class'];
  1069. }
  1070. $element['#attributes']['class'] = implode(' ', $class);
  1071. }
  1072. /**
  1073. * Remove invalid characters from an HTML ID attribute string.
  1074. *
  1075. * @param $id
  1076. * The ID to clean
  1077. * @return
  1078. * The cleaned ID
  1079. */
  1080. function form_clean_id($id = NULL) {
  1081. $id = str_replace('][', '-', $id);
  1082. return $id;
  1083. }
  1084. /**
  1085. * @} End of "defgroup form".
  1086. */
Login or register to post comments