diff --git a/includes/form.inc b/includes/form.inc index ee5d194c65e7923ab1c72ce65e6f14df91e72d9e..44954953d48ade4a425d643f90002a9359a168e6 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -39,7 +39,9 @@ /** * Retrieves a form from a builder function, passes it on for * processing, and renders the form or redirects to its destination - * as appropriate. + * as appropriate. In multi-step form scenerios, it handles properly + * processing the values using the previous step's form definition, + * then rendering the requested step for display. * * @param $form_id * The unique string identifying the desired form. If a function @@ -54,12 +56,61 @@ * The rendered form. */ function drupal_get_form($form_id) { - $args = func_get_args(); - $form = call_user_func_array('drupal_retrieve_form', $args); - drupal_process_form($form_id, $form); - return drupal_render_form($form_id, $form); + // In multi-step form scenerios, the incoming $_POST values are not + // necessarily intended for the current form. We need to build + // a copy of the previously built form for validation and processing, + // then go on to the one that was requested if everything works. + + $form_build_id = md5(mt_rand()); + if (isset($_POST['form_build_id']) && isset($_SESSION['form'][$_POST['form_build_id']])) { + // There's a previously stored multi-step form. We should handle + // IT first. + $stored = TRUE; + $args = $_SESSION['form'][$_POST['form_build_id']]; + $form = call_user_func_array('drupal_retrieve_form', $args); + } + else { + // We're coming in fresh; build things as they would be. If the + // form's #multistep flag is set, store the build parameters so + // the same form can be reconstituted for validation. + $args = func_get_args(); + $form = call_user_func_array('drupal_retrieve_form', $args); + if (isset($form['#multistep']) && $form['#multistep']) { + $_SESSION['form'][$form_build_id] = $args; + $form['#build_id'] = $form_build_id; + } + $stored = FALSE; + } + + // Process the form, submit it, and store any errors if necessary. + drupal_process_form($args[0], $form); + + if ($stored && !form_get_errors()) { + // If it's a stored form and there were no errors, we processed the + // stored form successfully. Now we need to build the form that was + // actually requested. We always pass in the current $_POST values + // to the builder function, as values from one stage of a multistep + // form can determine how subsequent steps are displayed. + $args = func_get_args(); + $args[] = $_POST['edit']; + $form = call_user_func_array('drupal_retrieve_form', $args); + unset($_SESSION['form'][$_POST['form_build_id']]); + if (isset($form['#multistep']) && $form['#multistep']) { + $_SESSION['form'][$form_build_id] = $args; + $form['#build_id'] = $form_build_id; + } + // If we're in this part of the code, $_POST['edit'] always contains + // values from the previously submitted form. Unset it to avoid + // any accidental submission of doubled data, then process the form + // to prep it for rendering. + unset($_POST['edit']); + drupal_process_form($args[0], $form); + } + + return drupal_render_form($args[0], $form); } + /** * Retrieves the structured array that defines a given form. * @@ -158,6 +209,17 @@ function drupal_prepare_form($form_id, &$form) { $form['#programmed'] = TRUE; } + // In multi-step form scenerios, this id is used to identify + // a unique instance of a particular form for retrieval. + if (isset($form['#build_id'])) { + $form['form_build_id'] = array( + '#type' => 'hidden', + '#value' => $form['#build_id'], + '#id' => $form['#build_id'], + '#name' => 'form_build_id', + ); + } + // 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.