? sites/test.angrylittletree.com Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.138 diff -u -F^f -r1.138 form.inc --- includes/form.inc 22 Aug 2006 19:12:05 -0000 1.138 +++ includes/form.inc 22 Aug 2006 19:35:39 -0000 @@ -17,17 +17,16 @@ * $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: + * using the drupal_execute() function. 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); + * $values['name'] = 'robo-user'; + * $values['mail'] = 'robouser@example.com'; + * $values['pass'] = 'password'; + * $errors = drupal_execute('user_register', $values); * - * Calling form_get_errors() will list any validation errors that prevented the - * form from being submitted. + * drupal_execute() returns an array containing any validation errors + * encountered during processing. * * 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 @@ -39,7 +38,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 @@ -53,11 +54,95 @@ * @return * The rendered form. */ -function drupal_get_form($form_id) { +function drupal_get_form($form_id) { + // 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 a form using a form_id, populates it with $form_values, + * processes it, and returns any validation errors encountered. This + * function is the programmatic counterpart to drupal_get_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. Examples + * may be found in node_forms(), search_forms(), and user_forms(). + * @param $form_values + * An array of values mirroring the values returned by a given form + * when it is submitted by a user. + * @param ... + * Any additional arguments needed by the form building function. + * @return + * Any form validation errors encountered. + */ + function drupal_execute($form_id, $form_values) { $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); + + $form_id = array_shift($args); + $form_values = array_shift($args); + array_unshift($args, $form_id); + + if (isset($form_values)) { + $form = call_user_func_array('drupal_retrieve_form', $args); + $form['#post']['edit'] = $form_values; + drupal_process_form($form_id, $form); + + return form_get_errors(); + } } /** @@ -90,7 +175,9 @@ function drupal_retrieve_form($form_id) } } // $callback comes from a hook_forms() implementation - return call_user_func_array(isset($callback) ? $callback : $form_id, $args); + $form = call_user_func_array(isset($callback) ? $callback : $form_id, $args); + $form['#parameters'] = func_get_args(); + return $form; } /** @@ -158,6 +245,17 @@ function drupal_prepare_form($form_id, & $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. @@ -500,7 +598,7 @@ function form_builder($form_id, $form) { $edit = isset($edit[$parent]) ? $edit[$parent] : NULL; } if (!isset($form['#value']) && !array_key_exists('#value', $form)) { - if ($posted) { + if ($posted && (!$form['#programmed'] || isset($edit))) { switch ($form['#type']) { case 'checkbox': $form['#value'] = !empty($edit) ? $form['#return_value'] : 0;