=== modified file 'includes/form.inc' --- includes/form.inc 2007-04-23 17:00:36 +0000 +++ includes/form.inc 2007-04-24 03:42:25 +0000 @@ -46,56 +46,67 @@ * The rendered form. */ function drupal_get_form($form_id) { - // In multi-step form scenarios, 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']]['args']) && $_POST['form_id'] == $form_id) { - // There's a previously stored multi-step form. We should handle - // IT first. - $stored = TRUE; - $args = $_SESSION['form'][$_POST['form_build_id']]['args']; - $form = call_user_func_array('drupal_retrieve_form', $args); - $form['#build_id'] = $_POST['form_build_id']; + global $user; + $results = array(); + + $package['store'] = NULL; + $expire = max(ini_get('session.cookie_lifetime'), 86400); + // If the incoming $_POST values have a form_build_id, we'll check the + // cache for a copy of the form in question. If it's there, we don't + // have to rebuild the form to proceed. In addition, if there is a 'data + // store' that was cached from previous steps, we'll retrieve it and + // use it in a little bit. + if (isset($_POST['form_id']) && $_POST['form_id'] == $form_id && !empty($_POST['form_build_id'])) { + if ($cached = cache_get('form_'. $user->uid .'_'. $_POST['form_build_id'], 'cache_form', $expire)) { + $form = unserialize($cached->data); + if ($cached = cache_get('store_'. $_POST['form_build_id'], 'cache_form')) { + $package['store'] = unserialize($cached->data); + } + } } - 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(); + + // If the previous bit of code didn't result in a populated $form + // object, we need to construct the form from scratch. + $args = func_get_args(); + if (!isset($form)) { $form = call_user_func_array('drupal_retrieve_form', $args); - if (isset($form['#multistep']) && $form['#multistep']) { - // Clean up old multistep form session data. - _drupal_clean_form_sessions(); - $_SESSION['form'][$form_build_id] = array('timestamp' => time(), 'args' => $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; + $form_build_id = md5(mt_rand()); + $form['#build_id'] = $form_build_id; + drupal_prepare_form($form_id, $form, $package['store']); + if (!empty($form['#cache'])) { + cache_set('form_'. $user->uid .'_'. $form_build_id, serialize($form), 'cache_form', $expire); + } + } + $form['#post'] = $_POST; + + // Now that we know we have a form, we'll process it (validating, + // submitting, and handling the results returned by its submission + // handlers. Submit handlers accumulate data in the package by + // altering the $package variable, which is passed into them by + // reference. + drupal_process_form($form_id, $form, $package); + + if (isset($package['store'])) { + $args[] = $package['store']; $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] = array('timestamp' => time(), 'args' => $args); - $form['#build_id'] = $form_build_id; - } - drupal_prepare_form($args[0], $form); + + $form_build_id = md5(mt_rand()); + $form['#build_id'] = $form_build_id; + drupal_prepare_form($form_id, $form, $package['store']); + + cache_set('store_'. $form_build_id, serialize($package['store']), 'cache_form', $expire); + cache_set('form_'. $user->uid .'_'. $form_build_id, serialize($form), 'cache_form', $expire); + + $_POST = array(); + $form['#post'] = array(); + + // Process the form, submit it, and store any errors if necessary. + drupal_process_form($form_id, $form, $package); } - return drupal_render_form($args[0], $form); + // If we haven't redirected to a new location, we want to render + // whatever form array is currently in hand. + return drupal_render_form($form_id, $form); } @@ -131,8 +142,6 @@ function _drupal_clean_form_sessions() { * when it is submitted by a user. * @param ... * Any additional arguments needed by the form building function. - * @return - * Any form validation errors encountered. * * For example: * @@ -149,18 +158,17 @@ function _drupal_clean_form_sessions() { * $values['name'] = 'robo-user'; * drupal_execute('story_node_form', $values, $node); */ -function drupal_execute($form_id, $form_values) { +function drupal_execute($form_id, $form_values, &$package) { $args = func_get_args(); - - $form_id = array_shift($args); - $form_values = array_shift($args); + $args = array_slice($args, 3); array_unshift($args, $form_id); - - if (isset($form_values)) { - $form = call_user_func_array('drupal_retrieve_form', $args); - $form['#post'] = $form_values; - return drupal_process_form($form_id, $form); + if (isset($package['store'])) { + $args[] = $package['store']; } + $form = call_user_func_array('drupal_retrieve_form', $args); + $form['#post'] = $form_values; + drupal_prepare_form($form_id, $form, $package['store']); + drupal_process_form($form_id, $form, $package); } /** @@ -235,8 +243,8 @@ function drupal_retrieve_form($form_id) * @return * The path to redirect the user to upon completion. */ -function drupal_process_form($form_id, &$form) { - global $form_values, $form_submitted, $user, $form_button_counter; +function drupal_process_form($form_id, &$form, &$package) { + global $form_values, $form_submitted, $user, $form_button_counter, $user; static $saved_globals = array(); // In some scenarios, this function can be called recursively. Pushing any pre-existing // $form_values and form submission data lets us start fresh without clobbering work done @@ -247,16 +255,22 @@ function drupal_process_form($form_id, & $form_submitted = FALSE; $form_button_counter = array(0, 0); - drupal_prepare_form($form_id, $form); - if (($form['#programmed']) || (!empty($_POST) && (($_POST['form_id'] == $form_id)))) { - drupal_validate_form($form_id, $form); + $form = form_builder($form_id, $form); + if ((!empty($form['#programmed'])) || (!empty($form['#post']) && (($form['#post']['form_id'] == $form_id)))) { + $old_uid = $user->uid; + drupal_validate_form($form_id, $form, $package); // 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['#programmed']) || $form_submitted || (!$form_button_counter[0] && $form_button_counter[1])) && !form_get_errors()) { - $redirect = drupal_submit_form($form_id, $form); + $package['redirect'] = NULL; + drupal_submit_form($form, $package); if (!$form['#programmed']) { - drupal_redirect_form($form, $redirect); + if (variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED || $user->uid) { + cache_clear_all('form_'. $old_uid .'_'. $form_values['form_build_id'], 'cache_form'); + cache_clear_all('store_'. $old_uid .'_'. $form_values['form_build_id'], 'cache_form'); + } + drupal_redirect_form($form, $package['redirect']); } } } @@ -264,9 +278,6 @@ function drupal_process_form($form_id, & // 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); - if (isset($redirect)) { - return $redirect; - } } /** @@ -280,24 +291,15 @@ function drupal_process_form($form_id, & * @param $form * An associative array containing the structure of the form. */ -function drupal_prepare_form($form_id, &$form) { +function drupal_prepare_form($form_id, &$form, $store) { global $user; $form['#type'] = 'form'; - if (!isset($form['#skip_duplicate_check'])) { + //if (!isset($form['#skip_duplicate_check'])) { $form['#skip_duplicate_check'] = FALSE; - } - - if (!isset($form['#post'])) { - $form['#post'] = $_POST; - $form['#programmed'] = FALSE; - } - else { - $form['#programmed'] = TRUE; - } + //} + $form['#programmed'] = isset($form['#post']); - // In multi-step form scenarios, 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', @@ -330,7 +332,11 @@ function drupal_prepare_form($form_id, & if (isset($form_id)) { - $form['form_id'] = array('#type' => 'hidden', '#value' => $form_id, '#id' => form_clean_id("edit-$form_id")); + $form['form_id'] = array( + '#type' => 'hidden', + '#value' => $form_id, + '#id' => form_clean_id("edit-$form_id"), + ); } if (!isset($form['#id'])) { $form['#id'] = form_clean_id($form_id); @@ -340,20 +346,18 @@ function drupal_prepare_form($form_id, & if (!isset($form['#validate'])) { if (function_exists($form_id .'_validate')) { - $form['#validate'] = array($form_id .'_validate' => array()); + $form['#validate'] = array($form_id .'_validate'); } } if (!isset($form['#submit'])) { if (function_exists($form_id .'_submit')) { // We set submit here so that it can be altered. - $form['#submit'] = array($form_id .'_submit' => array()); + $form['#submit'] = array($form_id .'_submit'); } } - drupal_alter('form', $form, $form_id); - - $form = form_builder($form_id, $form); + drupal_alter('form', $form, $form_id, $store); } @@ -368,7 +372,7 @@ function drupal_prepare_form($form_id, & * An associative array containing the structure of the form. * */ -function drupal_validate_form($form_id, $form) { +function drupal_validate_form($form_id, $form, &$package) { global $form_values; static $validated_forms = array(); @@ -385,12 +389,12 @@ function drupal_validate_form($form_id, } } - if (!$form['#programmed'] && !$form['#skip_duplicate_check'] && isset($_SESSION['last_submitted']['hash']) && $_SESSION['last_submitted']['hash'] == md5(serialize($form['form_id']['#post']))) { + if (!$form['#programmed'] && !$form['#skip_duplicate_check'] && isset($_SESSION['last_submitted']['hash']) && $_SESSION['last_submitted']['hash'] == md5(serialize($form['#post']))) { // This is a repeat submission. drupal_redirect_form(NULL, $_SESSION['last_submitted']['destination']); } - _form_validate($form, $form_id); + _form_validate($form, $package, $form_id); $validated_forms[$form_id] = TRUE; } @@ -398,45 +402,33 @@ function drupal_validate_form($form_id, * Processes user-submitted form data from a global variable using * the submit functions defined in a structured form array. * - * @param $form_id - * A unique string identifying the form for validation, submission, - * theming, and hook_form_alter functions. * @param $form * An associative array containing the structure of the form. + * @param $package + * Eaton needs to document this. * @return * A string containing the path of the page to display when processing * is complete. * */ -function drupal_submit_form($form_id, $form) { +function drupal_submit_form($form, &$package) { global $form_values; - $default_args = array($form_id, &$form_values); $submitted = FALSE; - $goto = NULL; if (isset($form['#submit'])) { - foreach ($form['#submit'] as $function => $args) { + foreach ($form['#submit'] as $function) { if (function_exists($function)) { - $args = array_merge($default_args, (array) $args); - // Since we can only redirect to one page, only the last redirect - // will work. - $redirect = call_user_func_array($function, $args); + $function($form_values, $form, $package); $submitted = TRUE; - if (isset($redirect)) { - $goto = $redirect; - } } } } + // Successful submit. Hash this form's POST and store the hash in the // session. We'll use this hash later whenever this user submits another // form to make sure no identical forms get submitted twice. if ($submitted && !$form['#skip_duplicate_check']) { - $_SESSION['last_submitted'] = array('destination' => $goto, 'hash' => md5(serialize($form['form_id']['#post']))); - } - - if (isset($goto)) { - return $goto; + $_SESSION['last_submitted'] = array('destination' => $package['redirect'], 'hash' => md5(serialize($form['#post']))); } } @@ -517,11 +509,11 @@ function drupal_redirect_form($form, $re * A unique string identifying the form for validation, submission, * theming, and hook_form_alter functions. */ -function _form_validate($elements, $form_id = NULL) { +function _form_validate($elements, &$package, $form_id = NULL) { // Recurse through all children. foreach (element_children($elements) as $key) { if (isset($elements[$key]) && $elements[$key]) { - _form_validate($elements[$key]); + _form_validate($elements[$key], $package); } } /* Validate the current input */ @@ -564,16 +556,21 @@ function _form_validate($elements, $form } } - // Call user-defined validators. - if (isset($elements['#validate'])) { - foreach ($elements['#validate'] as $function => $args) { - $args = array_merge(array($elements), $args); - // For the full form we hand over a copy of $form_values. - if (isset($form_id)) { - $args = array_merge(array($form_id, $GLOBALS['form_values']), $args); + // Call user-defined form level validators. + if (isset($form_id)) { + if (isset($elements['#validate'])) { + foreach ($elements['#validate'] as $function) { + if (function_exists($function)) { + $function($GLOBALS['form_values'], $elements, $package); + } } + } + } + // Call user-defined element validators. + elseif (isset($elements['#element_validate'])) { + foreach ($elements['#element_validate'] as $function) { if (function_exists($function)) { - call_user_func_array($function, $args); + $function($elements, $package); } } } @@ -1117,7 +1114,7 @@ function expand_password_confirm($elemen '#title' => t('Confirm password'), '#value' => $element['#value']['pass2'], ); - $element['#validate'] = array('password_confirm_validate' => array()); + $element['#element_validate'] = array('password_confirm_validate'); $element['#tree'] = TRUE; if (isset($element['#size'])) {