diff options
Diffstat (limited to 'includes/form.inc')
-rw-r--r-- | includes/form.inc | 192 |
1 files changed, 141 insertions, 51 deletions
diff --git a/includes/form.inc b/includes/form.inc index 291933d1f..c99f423c0 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -4,59 +4,138 @@ /** * @defgroup form Form generation * @{ - * Functions to enable output of HTML forms and form elements. + * Functions to enable the processing and display of HTML forms. * - * Drupal uses these functions to achieve consistency in its form presentation, - * while at the same time simplifying code and reducing the amount of HTML that - * must be explicitly generated by modules. See the reference at + * Drupal uses these functions to achieve consistency in its form processing and + * presentation, while simplifying code and reducing the amount of HTML that + * must be explicitly generated by modules. + * + * The drupal_get_form() function handles retrieving, processing, and + * displaying a rendered HTML form for modules automatically. For example: + * + * // display the user registration form + * $output = drupal_get_form('user_register'); + * + * Forms can also be built and submitted programmatically without any user input + * by populating $form['#post']['edit'] with values to be submitted. For example: + * + * // register a new user + * $form = drupal_retrieve_form('user_register'); + * $form['#post']['edit']['name'] = 'robo-user'; + * $form['#post']['edit']['mail'] = 'robouser@example.com'; + * $form['#post']['edit']['pass'] = 'password'; + * drupal_process_form('user_register', $form); + * + * Calling form_get_errors() will list any validation errors that prevented the + * form from being submitted. + * + * For information on the format of the structured arrays used to define forms, + * and more detailed explanations of the Form API workflow, see the reference at * http://api.drupal.org/api/HEAD/file/developer/topics/forms_api_reference.html * and the quickstart guide at * http://api.drupal.org/api/HEAD/file/developer/topics/forms_api.html */ /** - * Processes 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(). + * Retrieves a form from a builder function, passes it on for + * processing, and renders the form or redirects to its destination + * as appropriate. + * + * @param $form_id + * The unique string identifying the desired form. If a function + * with that name exists, it is called to build the form array. + * Modules that need to generate the same form (or very similar forms) + * using different $form_ids can implement hook_forms(), which maps + * different $form_id values to the proper form building function. Examples + * may be found in node_forms(), search_forms(), and user_forms(). + * @param ... + * Any additional arguments needed by the form building function. + * @return + * The rendered form. + */ +function drupal_get_form($form_id) { + $args = func_get_args(); + $form = call_user_func_array('drupal_retrieve_form', $args); + + $redirect = drupal_process_form($form_id, $form); + + if (isset($redirect)) { + drupal_redirect_form($form, $redirect); + } + return drupal_render_form($form_id, $form); +} + +/** + * Retrieves the structured array that defines a given form. + * + * @param $form_id + * The unique string identifying the desired form. If a function + * with that name exists, it is called to build the form array. + * Modules that need to generate the same form (or very similar forms) + * using different $form_ids can implement hook_forms(), which maps + * different $form_id values to the proper form building function. + * @param ... + * Any additional arguments needed by the form building function. + */ +function drupal_retrieve_form($form_id) { + static $forms; + + $args = func_get_args(); + array_shift($args); + if (!function_exists($form_id)) { + if (!isset($forms)) { + $forms = module_invoke_all('forms'); + } + $form_definition = $forms[$form_id]; + if (isset($form_definition['callback arguments'])) { + $args = array_merge($form_definition['callback arguments'], $args); + } + if (isset($form_definition['callback'])) { + $callback = $form_definition['callback']; + } + } + // $callback comes from a hook_forms() implementation + return call_user_func_array(isset($callback) ? $callback : $form_id, $args); +} + +/** + * This function is the heart of form API. The form gets built, validated and in + * appropriate cases, submitted. * * @param $form_id - * A unique string identifying the form. Allows each form to be - * themed. Pass NULL to suppress the form_id parameter (produces - * a shorter URL with method=get) + * The unique string identifying the current form. * @param $form * An associative array containing the structure of the form. - * @param $callback - * An optional callback that will be used in addition to the form_id. - * + * @return + * The path to redirect the user to upon completion. */ -function drupal_get_form($form_id, &$form, $callback = NULL) { +function drupal_process_form($form_id, &$form) { global $form_values, $form_submitted, $user, $form_button_counter; static $saved_globals = array(); - - // Save globals in case of indirect recursive call + // In some scenerios, this function can be called recursively. Pushing any pre-existing + // $form_values and form submission data lets us start fresh without clobbering work done + // in earlier recursive calls. array_push($saved_globals, array($form_values, $form_submitted, $form_button_counter)); $form_values = array(); $form_submitted = FALSE; $form_button_counter = array(0, 0); - $form = drupal_build_form($form_id, $form, $callback); - - if (!empty($_POST['edit']) && (($_POST['edit']['form_id'] == $form_id) || ($_POST['edit']['form_id'] == $callback))) { - drupal_validate_form($form_id, $form, $callback); + drupal_prepare_form($form_id, $form); + if (($form['#programmed']) || (!empty($_POST['edit']) && (($_POST['edit']['form_id'] == $form_id) || ($_POST['edit']['form_id'] == $form['#base'])))) { + drupal_validate_form($form_id, $form); // IE does not send a button value when there is only one submit button (and no non-submit buttons) // and you submit by pressing enter. // In that case we accept a submission without button values. - if (($form_submitted || (!$form_button_counter[0] && $form_button_counter[1])) && !form_get_errors()) { - $redirect = drupal_submit_form($form_id, $form, $callback); - drupal_redirect_form($form, $redirect); + if ((($form['#programmed']) || $form_submitted || (!$form_button_counter[0] && $form_button_counter[1])) && !form_get_errors()) { + $redirect = drupal_submit_form($form_id, $form); } } - $output = drupal_render_form($form_id, $form, $callback); + // We've finished calling functions that alter the global values, so we can + // restore the ones that were there before this function was called. list($form_values, $form_submitted, $form_button_counter) = array_pop($saved_globals); - return $output; + return $redirect; } /** @@ -69,12 +148,25 @@ function drupal_get_form($form_id, &$form, $callback = NULL) { * theming, and hook_form_alter functions. * @param $form * An associative array containing the structure of the form. - * @param $callback - * An optional callback that will be used in addition to the form_id. - * */ -function drupal_build_form($form_id, &$form, $callback = NULL) { +function drupal_prepare_form($form_id, &$form) { $form['#type'] = 'form'; + + if (!isset($form['#post'])) { + $form['#post'] = $_POST; + $form['#programmed'] = FALSE; + } + else { + $form['#programmed'] = TRUE; + } + + // If $base is set, it is used in place of $form_id when constructing validation, + // submission, and theming functions. Useful for mapping many similar or duplicate + // forms with different $form_ids to the same processing functions. + if (isset($form['#base'])) { + $base = $form['#base']; + } + if (isset($form['#token'])) { // If the page cache is on and an anonymous user issues a GET request, // unset the token because the token in the cached page would not match, @@ -105,8 +197,8 @@ function drupal_build_form($form_id, &$form, $callback = NULL) { if (function_exists($form_id .'_validate')) { $form['#validate'] = array($form_id .'_validate' => array()); } - elseif (function_exists($callback .'_validate')) { - $form['#validate'] = array($callback .'_validate' => array()); + elseif (function_exists($base .'_validate')) { + $form['#validate'] = array($base .'_validate' => array()); } } @@ -116,8 +208,8 @@ function drupal_build_form($form_id, &$form, $callback = NULL) { // $form_values because it will change later $form['#submit'] = array($form_id .'_submit' => array()); } - elseif (function_exists($callback .'_submit')) { - $form['#submit'] = array($callback .'_submit' => array()); + elseif (function_exists($base .'_submit')) { + $form['#submit'] = array($base .'_submit' => array()); } } @@ -127,8 +219,6 @@ function drupal_build_form($form_id, &$form, $callback = NULL) { } $form = form_builder($form_id, $form); - - return $form; } @@ -141,11 +231,9 @@ function drupal_build_form($form_id, &$form, $callback = NULL) { * theming, and hook_form_alter functions. * @param $form * An associative array containing the structure of the form. - * @param $callback - * An optional callback that will be used in addition to the form_id. * */ -function drupal_validate_form($form_id, $form, $callback = NULL) { +function drupal_validate_form($form_id, $form) { global $form_values; static $validated_forms = array(); @@ -153,7 +241,7 @@ function drupal_validate_form($form_id, $form, $callback = NULL) { return; } - // If the session token was set by drupal_build_form(), ensure that it + // If the session token was set by drupal_prepare_form(), ensure that it // matches the current user's session if (isset($form['#token'])) { if ($form_values['form_token'] != md5(session_id() . $form['#token'] . variable_get('drupal_private_key', ''))) { @@ -175,14 +263,12 @@ function drupal_validate_form($form_id, $form, $callback = NULL) { * theming, and hook_form_alter functions. * @param $form * An associative array containing the structure of the form. - * @param $callback - * An optional callback that will be used in addition to the form_id. * @return * A string containing the path of the page to display when processing * is complete. * */ -function drupal_submit_form($form_id, $form, $callback = NULL) { +function drupal_submit_form($form_id, $form) { global $form_values; $default_args = array($form_id, &$form_values); @@ -209,21 +295,23 @@ function drupal_submit_form($form_id, $form, $callback = NULL) { * theming, and hook_form_alter functions. * @param $form * An associative array containing the structure of the form. - * @param $callback - * An optional callback that will be used in addition to the form_id. * @return * A string containing the path of the page to display when processing * is complete. * */ -function drupal_render_form($form_id, &$form, $callback = NULL) { +function drupal_render_form($form_id, &$form) { // Don't override #theme if someone already set it. + if (isset($form['#base'])) { + $base = $form['#base']; + } + if (!isset($form['#theme'])) { if (theme_get_function($form_id)) { $form['#theme'] = $form_id; } - elseif (theme_get_function($callback)) { - $form['#theme'] = $callback; + elseif (theme_get_function($base)) { + $form['#theme'] = $base; } } @@ -408,8 +496,8 @@ function form_builder($form_id, $form) { $form['#id'] = 'edit-' . implode('-', $form['#parents']); } - $posted = (isset($_POST['edit']) && ($_POST['edit']['form_id'] == $form_id)); - $edit = $posted ? $_POST['edit'] : array(); + $posted = (($form['#programmed']) || (isset($_POST['edit']) && ($_POST['edit']['form_id'] == $form_id))); + $edit = $posted ? $form['#post']['edit'] : array(); foreach ($form['#parents'] as $parent) { $edit = isset($edit[$parent]) ? $edit[$parent] : NULL; } @@ -490,6 +578,8 @@ function form_builder($form_id, $form) { // Recurse through all child elements. $count = 0; foreach (element_children($form) as $key) { + $form[$key]['#post'] = $form['#post']; + $form[$key]['#programmed'] = $form['#programmed']; // don't squash an existing tree value if (!isset($form[$key]['#tree'])) { $form[$key]['#tree'] = $form['#tree']; @@ -769,7 +859,7 @@ function password_confirm_validate($form) { form_error($form, t('The specified passwords do not match.')); } } - elseif ($form['#required'] && !empty($_POST['edit'])) { + elseif ($form['#required'] && !empty($form['#post']['edit'])) { form_error($form, t('Password field is required.')); } |