=== modified file 'includes/form.inc' --- includes/form.inc 2007-04-24 13:53:10 +0000 +++ includes/form.inc 2007-04-25 15:14:50 +0000 @@ -27,92 +27,94 @@ */ /** - * Retrieves a form from a builder function, passes it on for - * processing, and renders the form or redirects to its destination - * as appropriate. In multi-step form scenarios, it handles properly - * processing the values using the previous step's form definition, - * then rendering the requested step for display. + * Retrieves a form from a constructor function, or from the cache if + * the form was built in a previous page-load. The form is then passesed + * on for processing, after and rendered for display if necessary. * * @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 + * different $form_id values to the proper form constructor function. Examples * may be found in node_forms(), search_forms(), and user_forms(). * @param ... - * Any additional arguments needed by the form building function. + * Any additional arguments needed by the form constructor function. * @return * 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']; - } - 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']) { - // 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 = 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_state = array('storage' => NULL); + $expire = max(ini_get('session.cookie_lifetime'), 86400); - return drupal_render_form($args[0], $form); -} - - -/** - * Remove form information that's at least a day old from the - * $_SESSION['form'] array. - */ -function _drupal_clean_form_sessions() { - if (isset($_SESSION['form'])) { - foreach ($_SESSION['form'] as $build_id => $data) { - if ($data['timestamp'] < (time() - 84600)) { - unset($_SESSION['form'][$build_id]); + // If the incoming $_POST contains 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 stored + // form_state data from a previous step, we'll retrieve it so it can + // be passed on to the form processing code. + if (isset($_POST['form_id']) && $_POST['form_id'] == $form_id && !empty($_POST['form_build_id'])) { + if ($cached = cache_get('form_'. $_POST['form_build_id'], 'cache_form')) { + $form = unserialize($cached->data); + if ($cached = cache_get('storage_'. $_POST['form_build_id'], 'cache_form')) { + $form_state['storage'] = unserialize($cached->data); } } } -} + // If the previous bit of code didn't result in a populated $form + // object, we're hitting the form for the first time and we need + // to build it from scratch. + $args = func_get_args(); + if (!isset($form)) { + $form = call_user_func_array('drupal_retrieve_form', $args); + $form_build_id = md5(mt_rand()); + $form['#build_id'] = $form_build_id; + drupal_prepare_form($form_id, $form, $form_state['storage']); + if (!empty($form['#cache'])) { + cache_set('form_'. $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 form_state by + // altering the $form_state variable, which is passed into them by + // reference. + drupal_process_form($form_id, $form, $form_state); + + // If $form_state['storage'] is set by a submit handler, we can + // assume that the form's workflow is NOT complete, and that it + // needs to build a second, or third, or fourth, etc. step to + // display to the user. We'll add the storage element to the end + // of the args that were passed into drupal_get_form(), and call + // the constructor function to build this next step. We'll also + // cache the form and the $form_state['storage'] data so that + // we can properly resume at this point when it's next submitted. + if (isset($form_state['storage'])) { + $args[] = $form_state['storage']; + $form = call_user_func_array('drupal_retrieve_form', $args); + + // We need a new build_id for this new version of the form. + $form_build_id = md5(mt_rand()); + $form['#build_id'] = $form_build_id; + drupal_prepare_form($form_id, $form, $form_state['storage']); + + cache_set('storage_'. $form_build_id, serialize($form_state['storage']), 'cache_form', $expire); + cache_set('form_'. $form_build_id, serialize($form), 'cache_form', $expire); + + // Clear out all post data, as we don't want the previous step's + // data to pollute this one and trigger validate/submit handling, + // then process the form for rendering. + $_POST = array(); + $form['#post'] = array(); + drupal_process_form($form_id, $form, $form_state); + } + + // If we haven't redirected to a new location by now, we want to + // render whatever form array is currently in hand. + return drupal_render_form($form_id, $form); +} /** * Retrieves a form using a form_id, populates it with $form_values, @@ -124,43 +126,52 @@ function _drupal_clean_form_sessions() { * 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 + * different $form_id values to the proper form constructor 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 $form_state + * A keyed array containing the current state of the form. This is + * used primarily by multi-step forms, and the exact contents of + * the array vary from form to form. After the form has been processed, + * $form_state will be populated with the results of the form's + * submission. * @param ... - * Any additional arguments needed by the form building function. - * @return - * Any form validation errors encountered. + * Any additional arguments needed by the form constructor function. * * For example: * * // register a new user + * $form_state = array(); * $values['name'] = 'robo-user'; * $values['mail'] = 'robouser@example.com'; * $values['pass'] = 'password'; - * drupal_execute('user_register', $values); + * drupal_execute('user_register', $values, $form_state); * * // Create a new node + * $form_state = array(); * $node = array('type' => 'story'); * $values['title'] = 'My node'; * $values['body'] = 'This is the body text!'; * $values['name'] = 'robo-user'; - * drupal_execute('story_node_form', $values, $node); + * drupal_execute('story_node_form', $values, $form_state, $node); */ -function drupal_execute($form_id, $form_values) { +function drupal_execute($form_id, $form_values, &$form_state) { $args = func_get_args(); - - $form_id = array_shift($args); - $form_values = array_shift($args); + + // We do a bit of juggling here because drupal_retrieve_form() expects + // the $form_state to be the last parameter, while drupal_execute() + // always takes it in as the third parameter. + $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($form_state['storage'])) { + $args[] = $form_state['storage']; } + $form = call_user_func_array('drupal_retrieve_form', $args); + $form['#post'] = $form_values; + drupal_prepare_form($form_id, $form, $form_state['storage']); + drupal_process_form($form_id, $form, $form_state); } /** @@ -171,17 +182,17 @@ function drupal_execute($form_id, $form_ * 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. + * different $form_id values to the proper form constructor function. * @param ... - * Any additional arguments needed by the form building function. + * Any additional arguments needed by the form constructor function. */ function drupal_retrieve_form($form_id) { static $forms; // We save two copies of the incoming arguments: one for modules to use - // when mapping form ids to builder functions, and another to pass to - // the builder function itself. We shift out the first argument -- the - // $form_id itself -- from the list to pass into the builder function, + // when mapping form ids to constructor functions, and another to pass to + // the constructor function itself. We shift out the first argument -- the + // $form_id itself -- from the list to pass into the constructor function, // since it's already known. $args = func_get_args(); $saved_args = $args; @@ -190,9 +201,9 @@ function drupal_retrieve_form($form_id) // We first check to see if there's a function named after the $form_id. // If there is, we simply pass the arguments on to it to get the form. if (!function_exists($form_id)) { - // In cases where many form_ids need to share a central builder function, + // In cases where many form_ids need to share a central constructor function, // such as the node editing form, modules can implement hook_forms(). It - // maps one or more form_ids to the correct builder functions. + // maps one or more form_ids to the correct constructor functions. // // We cache the results of that hook to save time, but that only works // for modules that know all their form_ids in advance. (A module that @@ -232,11 +243,15 @@ function drupal_retrieve_form($form_id) * The unique string identifying the current form. * @param $form * An associative array containing the structure of the form. - * @return - * The path to redirect the user to upon completion. + * @param $form_state + * A keyed array containing the current state of the form. This is + * used primarily by multi-step forms, and the exact contents of + * the array vary from form to form. After the form has been processed, + * $form_state will be populated with the results of the form's + * submission. */ -function drupal_process_form($form_id, &$form) { - global $form_values, $form_submitted, $user, $form_button_counter; +function drupal_process_form($form_id, &$form, &$form_state) { + 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 +262,41 @@ 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); - // 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. + $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, $form_state); + + // Normally, we look for the $form_submitted boolean that's set if + // the value of a submit button came in via the $_POST data. However, + // 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. + // If the form was called by drupal_execute() and the calling function + // didn't specify an 'op' value, we'll see the same symptoms. To catch + // these special cases, we first check to see if the form is #programmed, + // then check to see if the $form_submitted boolean is set, then finally + // count the number of submit buttons and non-submit buttons in the form + // to test for IE's strange behavior. if ((($form['#programmed']) || $form_submitted || (!$form_button_counter[0] && $form_button_counter[1])) && !form_get_errors()) { - $redirect = drupal_submit_form($form_id, $form); - if (!$form['#programmed']) { - drupal_redirect_form($form, $redirect); + $form_state['redirect'] = NULL; + drupal_submit_form($form, $form_state); + + // We'll clear out the cached copies of the form and its stored data + // here, as we've finished with them. The in-memory copies are still + // here, though. + 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('storage_'. $old_uid .'_'. $form_values['form_build_id'], 'cache_form'); + } + + // If no submit handlers have populated the $form_state['storage'] + // bundle, we know we're finished and should redirect to a + // new destination page if one has been set. If the form was + // called by drupal_execute(), however, we'll skip this and let + // the calling function examine the resulting $form_state bundle + // itself. + if (!$form['#programmed'] && empty($form_state['storage'])) { + drupal_redirect_form($form, $form_state['redirect']); } } } @@ -264,9 +304,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; - } } /** @@ -279,25 +316,21 @@ function drupal_process_form($form_id, & * theming, and hook_form_alter functions. * @param $form * An associative array containing the structure of the form. + * @param $storage + * The current persistent 'state' of the form, populated by the + * form's submission handlers and carried forward to subsequent + * builds. Passed in here so that hook_form_alter() calls can + * use it, as well. */ -function drupal_prepare_form($form_id, &$form) { +function drupal_prepare_form($form_id, &$form, $storage) { global $user; $form['#type'] = 'form'; if (!isset($form['#skip_duplicate_check'])) { $form['#skip_duplicate_check'] = FALSE; } + $form['#programmed'] = isset($form['#post']); - if (!isset($form['#post'])) { - $form['#post'] = $_POST; - $form['#programmed'] = FALSE; - } - else { - $form['#programmed'] = TRUE; - } - - // 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 +363,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 +377,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, $storage); } @@ -366,9 +401,18 @@ function drupal_prepare_form($form_id, & * theming, and hook_form_alter functions. * @param $form * An associative array containing the structure of the form. - * + * @param $form_state + * A keyed array containing the current state of the form. This is + * used primarily by multi-step forms, and the exact contents of + * the array vary from form to form. Validation handlers can use + * this variable to store information that will later be used by + * the form's submit handlers. For example: + * $form_state['data_for_submision'] = $data; + * This technique is useful when validation requires file parsing, + * web service requests, or other expensive requests that should + * not be repeated in the submission step. */ -function drupal_validate_form($form_id, $form) { +function drupal_validate_form($form_id, $form, &$form_state) { global $form_values; static $validated_forms = array(); @@ -385,12 +429,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, $form_state, $form_id); $validated_forms[$form_id] = TRUE; } @@ -398,45 +442,39 @@ 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. - * @return - * A string containing the path of the page to display when processing - * is complete. - * - */ -function drupal_submit_form($form_id, $form) { + * @param $form_state + * A keyed array containing the current state of the form. This can + * include information passed on from validation handlers, persistent + * storage from previous steps of a complex multi-page form, and + * the results generated by other submit handlers attached to the + * form. Standard elements of the $form_state include: + * $form_state['redirect']: the path to redirect to when the form + * has been processed. + * $form_state['storage']: the persistent data store that will be + * preserved between page reloads. If $form_state['storage'] is + * set, $form_state['redirect'] will be ignored and the form will + * be re-built using the stored data. + */ +function drupal_submit_form($form, &$form_state) { 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, $form_state); $submitted = TRUE; - if (isset($redirect)) { - $goto = $redirect; - } } } } - // Successful submit. Hash this form's POST and store the hash in the + + // Successful submit. Hash this form's POST and storage 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' => $form_state['redirect'], 'hash' => md5(serialize($form['#post']))); } } @@ -513,15 +551,21 @@ function drupal_redirect_form($form, $re * * @param $elements * An associative array containing the structure of the form. + * @param $form_state + * A keyed array containing the current state of the form. This is + * used primarily by multi-step forms, and the exact contents of + * the array vary from form to form. Validation handlers can use + * this variable to store information that will later be used by + * the form's submit handlers. * @param $form_id * 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, &$form_state, $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], $form_state); } } /* Validate the current input */ @@ -564,16 +608,22 @@ 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, $form_state); + } } + } + } + // Call any element-specific validators. These must act on the element + // #value data, rather than the general $form_values collection. + elseif (isset($elements['#element_validate'])) { + foreach ($elements['#element_validate'] as $function) { if (function_exists($function)) { - call_user_func_array($function, $args); + $function($elements, $form_state); } } } @@ -1117,7 +1167,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'])) { === modified file 'modules/aggregator/aggregator.module' --- modules/aggregator/aggregator.module 2007-04-24 13:53:10 +0000 +++ modules/aggregator/aggregator.module 2007-04-25 15:14:50 +0000 @@ -357,7 +357,7 @@ function aggregator_form_category($edit /** * Validate aggregator_form_feed form submissions. */ -function aggregator_form_category_validate($form_id, $form_values) { +function aggregator_form_category_validate($form_values, $form, &$form_state) { if ($form_values['op'] == t('Submit')) { // Check for duplicate titles if (isset($form_values['cid'])) { @@ -376,7 +376,7 @@ function aggregator_form_category_valida * Process aggregator_form_category form submissions. * @todo Add delete confirmation dialog. */ -function aggregator_form_category_submit($form_id, $form_values) { +function aggregator_form_category_submit($form_values, $form, &$form_state) { if ($form_values['op'] == t('Delete')) { $title = $form_values['title']; // Unset the title: @@ -388,20 +388,24 @@ function aggregator_form_category_submit if (isset($form_values['title'])) { drupal_set_message(t('The category %category has been updated.', array('%category' => $form_values['title']))); if (arg(0) == 'admin') { - return 'admin/content/aggregator/'; + $form_state['redirect'] = 'admin/content/aggregator/'; + return; } else { - return 'aggregator/categories/'. $form_values['cid']; + $form_state['redirect'] = 'aggregator/categories/'. $form_values['cid']; + return; } } else { watchdog('aggregator', 'Category %category deleted.', array('%category' => $title)); drupal_set_message(t('The category %category has been deleted.', array('%category' => $title))); if (arg(0) == 'admin') { - return 'admin/content/aggregator/'; + $form_state['redirect'] = 'admin/content/aggregator/'; + return; } else { - return 'aggregator/categories/'; + $form_state['redirect'] = 'aggregator/categories/'; + return; } } } @@ -488,7 +492,7 @@ function aggregator_form_feed($edit = ar /** * Validate aggregator_form_feed form submissions. */ -function aggregator_form_feed_validate($form_id, $form_values) { +function aggregator_form_feed_validate($form_values, $form, &$form_state) { if ($form_values['op'] == t('Submit')) { // Check for duplicate titles if (isset($form_values['fid'])) { @@ -512,7 +516,7 @@ function aggregator_form_feed_validate($ * Process aggregator_form_feed form submissions. * @todo Add delete confirmation dialog. */ -function aggregator_form_feed_submit($form_id, $form_values) { +function aggregator_form_feed_submit($form_values, $form, &$form_state) { if ($form_values['op'] == t('Delete')) { $title = $form_values['title']; // Unset the title: @@ -524,20 +528,24 @@ function aggregator_form_feed_submit($fo if (isset($form_values['title'])) { drupal_set_message(t('The feed %feed has been updated.', array('%feed' => $form_values['title']))); if (arg(0) == 'admin') { - return 'admin/content/aggregator/'; + $form_state['redirect'] = 'admin/content/aggregator/'; + return; } else { - return 'aggregator/sources/'. $form_values['fid']; + $form_state['redirect'] = 'aggregator/sources/'. $form_values['fid']; + return; } } else { watchdog('aggregator', 'Feed %feed deleted.', array('%feed' => $title)); drupal_set_message(t('The feed %feed has been deleted.', array('%feed' => $title))); if (arg(0) == 'admin') { - return 'admin/content/aggregator/'; + $form_state['redirect'] = 'admin/content/aggregator/'; + return; } else { - return 'aggregator/sources/'; + $form_state['redirect'] = 'aggregator/sources/'; + return; } } } @@ -1076,8 +1084,8 @@ function aggregator_page_category() { } function aggregator_page_list($sql, $header, $categorize) { - $form['#submit']['aggregator_page_list_submit'] = array(); - $form['#validate']['aggregator_page_list_validate'] = array(); + $form['#submit'][] = 'aggregator_page_list_submit'; + $form['#validate'][] = 'aggregator_page_list_validate'; $form['#theme'] = 'aggregator_page_list'; $form['header'] = array('#value' => $header); $result = pager_query($sql, 20); @@ -1162,7 +1170,7 @@ function aggregator_page_list_validate($ } } -function aggregator_page_list_submit($form_id, $form_values) { +function aggregator_page_list_submit($form_values, $form, &$form_state) { foreach ($form_values['categories'] as $iid => $selection) { db_query('DELETE FROM {aggregator_category_item} WHERE iid = %d', $iid); foreach ($selection as $cid) { === modified file 'modules/block/block.module' --- modules/block/block.module 2007-04-17 07:19:38 +0000 +++ modules/block/block.module 2007-04-25 15:14:50 +0000 @@ -291,7 +291,7 @@ function _block_compare($a, $b) { /** * Process main block administration form submission. */ -function block_admin_display_submit($form_id, $form_values) { +function block_admin_display_submit($form_values, $form, &$form_state) { foreach ($form_values as $block) { $block['status'] = $block['region'] != BLOCK_REGION_NONE; $block['region'] = $block['status'] ? $block['region'] : ''; @@ -497,7 +497,7 @@ function block_admin_configure($module = return $form; } -function block_admin_configure_validate($form_id, $form_values) { +function block_admin_configure_validate($form_values, $form, &$form_state) { if ($form_values['module'] == 'block') { if (empty($form_values['info']) || db_num_rows(db_query("SELECT bid FROM {boxes} WHERE bid != %d AND info = '%s'", $form_values['delta'], $form_values['info']))) { form_set_error('info', t('Please ensure that each block description is unique.')); @@ -505,7 +505,7 @@ function block_admin_configure_validate( } } -function block_admin_configure_submit($form_id, $form_values) { +function block_admin_configure_submit($form_values, $form, &$form_state) { if (!form_get_errors()) { db_query("UPDATE {blocks} SET visibility = %d, pages = '%s', custom = %d, title = '%s' WHERE module = '%s' AND delta = '%s'", $form_values['visibility'], trim($form_values['pages']), $form_values['custom'], $form_values['title'], $form_values['module'], $form_values['delta']); db_query("DELETE FROM {blocks_roles} WHERE module = '%s' AND delta = '%s'", $form_values['module'], $form_values['delta']); @@ -515,7 +515,8 @@ function block_admin_configure_submit($f module_invoke($form_values['module'], 'block', 'save', $form_values['delta'], $form_values); drupal_set_message(t('The block configuration has been saved.')); cache_clear_all(); - return 'admin/build/block'; + $form_state['redirect'] = 'admin/build/block'; + return; } } @@ -526,7 +527,7 @@ function block_add_block_form() { return block_admin_configure('block', NULL); } -function block_add_block_form_validate($form_id, $form_values) { +function block_add_block_form_validate($form_values, $form, &$form_state) { if (empty($form_values['info']) || db_num_rows(db_query("SELECT info FROM {boxes} WHERE info = '%s'", $form_values['info']))) { form_set_error('info', t('Please ensure that each block description is unique.')); } @@ -535,7 +536,7 @@ function block_add_block_form_validate($ /** * Save the new custom block. */ -function block_add_block_form_submit($form_id, $form_values) { +function block_add_block_form_submit($form_values, $form, &$form_state) { $delta = db_next_id('{boxes}_bid'); foreach (list_themes() as $key => $theme) { @@ -553,7 +554,8 @@ function block_add_block_form_submit($fo drupal_set_message(t('The block has been created.')); cache_clear_all(); - return 'admin/build/block'; + $form_state['redirect'] = 'admin/build/block'; + return; } /** @@ -570,12 +572,13 @@ function block_box_delete($bid = 0) { /** * Deletion of custom blocks. */ -function block_box_delete_submit($form_id, $form_values) { +function block_box_delete_submit($form_values, $form, &$form_state) { db_query('DELETE FROM {boxes} WHERE bid = %d', $form_values['bid']); db_query("DELETE FROM {blocks} WHERE module = 'block' AND delta = %d", $form_values['bid']); drupal_set_message(t('The block %name has been removed.', array('%name' => $form_values['info']))); cache_clear_all(); - return 'admin/build/block'; + $form_state['redirect'] = 'admin/build/block'; + return; }; /** === modified file 'modules/book/book.module' --- modules/book/book.module 2007-04-24 13:53:10 +0000 +++ modules/book/book.module 2007-04-25 15:14:50 +0000 @@ -298,7 +298,7 @@ function book_outline($node) { /** * Handles book outline form submissions. */ -function book_outline_submit($form_id, $form_values) { +function book_outline_submit($form_values, $form, &$form_state) { $op = $form_values['op']; $node = node_load($form_values['nid']); @@ -318,7 +318,8 @@ function book_outline_submit($form_id, $ drupal_set_message(t('The post has been removed from the book.')); break; } - return "node/$node->nid"; + $form_state['redirect'] = "node/$node->nid"; + return; } /** @@ -924,13 +925,13 @@ function book_admin_orphan() { else { $form['error'] = array('#value' => '
'. t('There are no orphan pages.') .'
'); } - $form['#submit']['book_admin_edit_submit'] = array(); - $form['#validate']['book_admin_edit_validate'] = array(); + $form['#submit'][] = 'book_admin_edit_submit'; + $form['#validate'][] = 'book_admin_edit_validate'; $form['#theme'] = 'book_admin_edit'; return $form; } -function book_admin_edit_submit($form_id, $form_values) { +function book_admin_edit_submit($form_values, $form, &$form_state) { foreach ($form_values['table'] as $row) { $node = node_load($row['nid']); === modified file 'modules/color/color.module' --- modules/color/color.module 2007-04-06 13:27:20 +0000 +++ modules/color/color.module 2007-04-25 15:14:50 +0000 @@ -26,7 +26,7 @@ function color_form_alter(&$form, $form_ '#theme' => 'color_scheme_form', ); $form['color'] += color_scheme_form(arg(4)); - $form['#submit']['color_scheme_form_submit'] = array(); + $form['#submit'][] = 'color_scheme_form_submit'; } // Use the generated screenshot in the theme list @@ -183,7 +183,7 @@ function theme_color_scheme_form($form) /** * Submit handler for color change form. */ -function color_scheme_form_submit($form_id, $values) { +function color_scheme_form_submit($values, $form, &$form_state) { // Get theme coloring info if (!isset($values['info'])) { return; === modified file 'modules/comment/comment.module' --- modules/comment/comment.module 2007-04-24 18:23:48 +0000 +++ modules/comment/comment.module 2007-04-25 15:14:50 +0000 @@ -1223,7 +1223,7 @@ function comment_admin_overview($type = /** * We can't execute any 'Update options' if no comments were selected. */ -function comment_admin_overview_validate($form_id, $form_values) { +function comment_admin_overview_validate($form_values, $form, &$form_state) { $form_values['comments'] = array_diff($form_values['comments'], array(0)); if (count($form_values['comments']) == 0) { form_set_error('', t('Please select one or more comments to perform the update on.')); @@ -1235,7 +1235,7 @@ function comment_admin_overview_validate * Execute the chosen 'Update option' on the selected comments, such as * publishing, unpublishing or deleting. */ -function comment_admin_overview_submit($form_id, $form_values) { +function comment_admin_overview_submit($form_values, $form, &$form_state) { $operations = comment_operations(); if ($operations[$form_values['operation']][1]) { // extract the appropriate database query operation @@ -1320,7 +1320,7 @@ function comment_multiple_delete_confirm /** * Perform the actual comment deletion. */ -function comment_multiple_delete_confirm_submit($form_id, $form_values) { +function comment_multiple_delete_confirm_submit($form_values, $form, &$form_state) { if ($form_values['confirm']) { foreach ($form_values['comments'] as $cid => $value) { $comment = _comment_load($cid); @@ -1675,7 +1675,7 @@ function comment_form_add_preview($form, return $form; } -function comment_form_validate($form_id, $form_values) { +function comment_form_validate($form_values, $form, &$form_state) { comment_validate($form_values); } @@ -1705,13 +1705,15 @@ function _comment_form_submit($form_valu } } - return $form_values; + $form_state['redirect'] = $form_values; + return; } -function comment_form_submit($form_id, $form_values) { +function comment_form_submit($form_values, $form, &$form_state) { $form_values = _comment_form_submit($form_values); if ($cid = comment_save($form_values)) { - return array('node/'. $form_values['nid'], NULL, "comment-$cid"); + $form_state['redirect'] = array('node/'. $form_values['nid'], NULL, "comment-$cid"); + return; } } @@ -1794,7 +1796,7 @@ function theme_comment_controls($form) { return theme('box', t('Comment viewing options'), $output); } -function comment_controls_submit($form_id, $form_values) { +function comment_controls_submit($form_values, $form, &$form_state) { global $user; $mode = $form_values['mode']; === modified file 'modules/contact/contact.module' --- modules/contact/contact.module 2007-04-24 13:53:10 +0000 +++ modules/contact/contact.module 2007-04-25 15:14:50 +0000 @@ -209,7 +209,7 @@ function contact_admin_edit($cid = NULL) /** * Validate the contact category edit page form submission. */ -function contact_admin_edit_validate($form_id, $form_values) { +function contact_admin_edit_validate($form_values, $form, &$form_state) { if (empty($form_values['category'])) { form_set_error('category', t('You must enter a category.')); } @@ -229,7 +229,7 @@ function contact_admin_edit_validate($fo /** * Process the contact category edit page form submission. */ -function contact_admin_edit_submit($form_id, $form_values) { +function contact_admin_edit_submit($form_values, $form, &$form_state) { if ($form_values['selected']) { // Unselect all other contact categories. db_query('UPDATE {contact} SET selected = 0'); @@ -252,7 +252,8 @@ function contact_admin_edit_submit($form watchdog('mail', 'Contact form: category %category updated.', array('%category' => $form_values['category']), WATCHDOG_NOTICE, l(t('view'), 'admin/build/contact')); } - return 'admin/build/contact'; + $form_state['redirect'] = 'admin/build/contact'; + return; } /** @@ -275,12 +276,13 @@ function contact_admin_delete($cid = NUL /** * Process category delete form submission. */ -function contact_admin_delete_submit($form_id, $form_values) { +function contact_admin_delete_submit($form_values, $form, &$form_state) { db_query("DELETE FROM {contact} WHERE cid = %d", arg(4)); drupal_set_message(t('Category %category has been deleted.', array('%category' => $form_values['category']))); watchdog('mail', 'Contact form: category %category deleted.', array('%category' => $form_values['category']), WATCHDOG_NOTICE); - return 'admin/build/contact'; + $form_state['redirect'] = 'admin/build/contact'; + return; } function contact_admin_settings() { @@ -357,7 +359,7 @@ function contact_mail_user($recipient) { /** * Process the personal contact page form submission. */ -function contact_mail_user_submit($form_id, $form_values) { +function contact_mail_user_submit($form_values, $form, &$form_state) { global $user; $account = user_load(array('uid' => arg(1), 'status' => 1)); @@ -399,7 +401,8 @@ function contact_mail_user_submit($form_ drupal_set_message(t('The message has been sent.')); // Jump to the user's profile page: - return "user/$account->uid"; + $form_state['redirect'] = "user/$account->uid"; + return; } /** @@ -493,7 +496,7 @@ function contact_mail_page() { /** * Validate the site-wide contact page form submission. */ -function contact_mail_page_validate($form_id, $form_values) { +function contact_mail_page_validate($form_values, $form, &$form_state) { if (!$form_values['cid']) { form_set_error('category', t('You must select a valid category.')); } @@ -505,7 +508,7 @@ function contact_mail_page_validate($for /** * Process the site-wide contact page form submission. */ -function contact_mail_page_submit($form_id, $form_values) { +function contact_mail_page_submit($form_values, $form, &$form_state) { // E-mail address of the sender: as the form field is a text field, // all instances of \r and \n have been automatically stripped from it. @@ -550,6 +553,7 @@ function contact_mail_page_submit($form_ drupal_set_message(t('Your message has been sent.')); // Jump to home page rather than back to contact page to avoid contradictory messages if flood control has been activated. - return ''; + $form_state['redirect'] = ''; + return; } === modified file 'modules/dblog/dblog.module' --- modules/dblog/dblog.module 2007-04-24 13:53:10 +0000 +++ modules/dblog/dblog.module 2007-04-25 15:14:50 +0000 @@ -232,7 +232,7 @@ function theme_dblog_form_overview($form return 'Welcome to Drupal. You are user #1, which gives you full and immediate access. All future registrants will receive their passwords via e-mail, so please make sure your website e-mail address is set properly under the general settings on the site information settings page.
Your password is %pass. You may change your password below.
', array('%pass' => $pass, '@settings' => url('admin/settings/site-information')))); user_authenticate($account->name, trim($pass)); - return 'user/1/edit'; + $form_state['redirect'] = 'user/1/edit'; + return; } else { if ($admin && !$notify) { @@ -1389,7 +1393,8 @@ function user_register_submit($form_id, $body = _user_mail_text('welcome_body', $variables); drupal_mail('user-register-welcome', $mail, $subject, $body, $from); user_authenticate($account->name, trim($pass)); - return ''; + $form_state['redirect'] = ''; + return; } else if ($account->status || $notify) { // Create new user account, no administrator approval required. @@ -1403,7 +1408,8 @@ function user_register_submit($form_id, } else { drupal_set_message(t('Your password and further instructions have been sent to your e-mail address.')); - return ''; + $form_state['redirect'] = ''; + return; } } else { @@ -1614,7 +1620,7 @@ function user_delete($edit, $uid) { module_invoke_all('user', 'delete', $edit, $account); } -function user_edit_validate($form_id, $form_values) { +function user_edit_validate($form_values, $form, &$form_state) { user_module_invoke('validate', $form_values, $form_values['_account'], $form_values['_category']); // Validate input to ensure that non-privileged users can't alter protected data. if ((!user_access('administer users') && array_intersect(array_keys($form_values), array('uid', 'init', 'session'))) || (!user_access('administer access control') && isset($form_values['roles']))) { @@ -1624,7 +1630,7 @@ function user_edit_validate($form_id, $f } } -function user_edit_submit($form_id, $form_values) { +function user_edit_submit($form_values, $form, &$form_state) { $account = $form_values['_account']; $category = $form_values['_category']; unset($form_values['_account'], $form_values['op'], $form_values['submit'], $form_values['delete'], $form_values['form_token'], $form_values['form_id'], $form_values['_category']); @@ -1635,7 +1641,8 @@ function user_edit_submit($form_id, $for cache_clear_all(); drupal_set_message(t('The changes have been saved.')); - return 'user/'. $account->uid; + $form_state['redirect'] = 'user/'. $account->uid; + return; } function user_view($account) { @@ -1697,8 +1704,8 @@ function user_admin_check_user() { $form['user']['test'] = array('#type' => 'textfield', '#title' => '', '#description' => t('Enter a username to check if it will be denied or allowed.'), '#size' => 30, '#maxlength' => USERNAME_MAX_LENGTH); $form['user']['type'] = array('#type' => 'hidden', '#value' => 'user'); $form['user']['submit'] = array('#type' => 'submit', '#value' => t('Check username')); - $form['#submit']['user_admin_access_check_submit'] = array(); - $form['#validate']['user_admin_access_check_validate'] = array(); + $form['#submit'][] = 'user_admin_access_check_submit'; + $form['#validate'][] = 'user_admin_access_check_validate'; $form['#theme'] = 'user_admin_access_check'; return $form; } @@ -1708,8 +1715,8 @@ function user_admin_check_mail() { $form['mail']['test'] = array('#type' => 'textfield', '#title' => '', '#description' => t('Enter an e-mail address to check if it will be denied or allowed.'), '#size' => 30, '#maxlength' => EMAIL_MAX_LENGTH); $form['mail']['type'] = array('#type' => 'hidden', '#value' => 'mail'); $form['mail']['submit'] = array('#type' => 'submit', '#value' => t('Check e-mail')); - $form['#submit']['user_admin_access_check_submit'] = array(); - $form['#validate']['user_admin_access_check_validate'] = array(); + $form['#submit'][] = 'user_admin_access_check_submit'; + $form['#validate'][] = 'user_admin_access_check_validate'; $form['#theme'] = 'user_admin_access_check'; return $form; } @@ -1719,8 +1726,8 @@ function user_admin_check_host() { $form['host']['test'] = array('#type' => 'textfield', '#title' => '', '#description' => t('Enter a hostname or IP address to check if it will be denied or allowed.'), '#size' => 30, '#maxlength' => 64); $form['host']['type'] = array('#type' => 'hidden', '#value' => 'host'); $form['host']['submit'] = array('#type' => 'submit', '#value' => t('Check hostname')); - $form['#submit']['user_admin_access_check_submit'] = array(); - $form['#validate']['user_admin_access_check_validate'] = array(); + $form['#submit'][] = 'user_admin_access_check_submit'; + $form['#validate'][] = 'user_admin_access_check_validate'; $form['#theme'] = 'user_admin_access_check'; return $form; } @@ -1735,13 +1742,13 @@ function user_admin_access_check() { return $output; } -function user_admin_access_check_validate($form_id, $form_values) { +function user_admin_access_check_validate($form_values, $form, &$form_state) { if (empty($form_values['test'])) { form_set_error($form_values['type'], t('No value entered. Please enter a test string and try again.')); } } -function user_admin_access_check_submit($form_id, $form_values) { +function user_admin_access_check_submit($form_values, $form, &$form_state) { switch ($form_values['type']) { case 'user': if (drupal_is_denied('user', $form_values['test'])) { @@ -1812,10 +1819,11 @@ function user_admin_access_delete_confir return $output; } -function user_admin_access_delete_confirm_submit($form_id, $form_values) { +function user_admin_access_delete_confirm_submit($form_values, $form, &$form_state) { db_query('DELETE FROM {access} WHERE aid = %d', $form_values['aid']); drupal_set_message(t('The access rule has been deleted.')); - return 'admin/user/rules'; + $form_state['redirect'] = 'admin/user/rules'; + return; } /** @@ -2004,7 +2012,7 @@ function theme_user_admin_perm($form) { return $output; } -function user_admin_perm_submit($form_id, $form_values) { +function user_admin_perm_submit($form_values, $form, &$form_state) { // Save permissions: $result = db_query('SELECT * FROM {role}'); while ($role = db_fetch_object($result)) { @@ -2069,13 +2077,13 @@ function user_admin_role() { '#type' => 'submit', '#value' => t('Add role'), ); - $form['#submit']['user_admin_role_submit'] = array(); - $form['#validate']['user_admin_role_validate'] = array(); + $form['#submit'][] = 'user_admin_role_submit'; + $form['#validate'][] = 'user_admin_role_validate'; } return $form; } -function user_admin_role_validate($form_id, $form_values) { +function user_admin_role_validate($form_values, $form, &$form_state) { if ($form_values['name']) { if ($form_values['op'] == t('Save role')) { if (db_result(db_query("SELECT COUNT(*) FROM {role} WHERE name = '%s' AND rid != %d", $form_values['name'], $form_values['rid']))) { @@ -2093,7 +2101,7 @@ function user_admin_role_validate($form_ } } -function user_admin_role_submit($form_id, $form_values) { +function user_admin_role_submit($form_values, $form, &$form_state) { if ($form_values['op'] == t('Save role')) { db_query("UPDATE {role} SET name = '%s' WHERE rid = %d", $form_values['name'], $form_values['rid']); drupal_set_message(t('The role has been renamed.')); @@ -2110,7 +2118,8 @@ function user_admin_role_submit($form_id db_query("INSERT INTO {role} (name) VALUES ('%s')", $form_values['name']); drupal_set_message(t('The role has been added.')); } - return 'admin/user/roles'; + $form_state['redirect'] = 'admin/user/roles'; + return; } function theme_user_admin_new_role($form) { @@ -2244,7 +2253,7 @@ function theme_user_admin_account($form) /** * Submit the user administration update form. */ -function user_admin_account_submit($form_id, $form_values) { +function user_admin_account_submit($form_values, $form, &$form_state) { $operations = module_invoke_all('user_operations'); $operation = $operations[$form_values['operation']]; // Filter out unchecked accounts. @@ -2263,7 +2272,7 @@ function user_admin_account_submit($form } } -function user_admin_account_validate($form_id, $form_values) { +function user_admin_account_validate($form_values, $form, &$form_state) { $form_values['accounts'] = array_filter($form_values['accounts']); if (count($form_values['accounts']) == 0) { form_set_error('', t('No users selected.')); @@ -2415,14 +2424,15 @@ function user_multiple_delete_confirm() t('Delete all'), t('Cancel')); } -function user_multiple_delete_confirm_submit($form_id, $form_values) { +function user_multiple_delete_confirm_submit($form_values, $form, &$form_state) { if ($form_values['confirm']) { foreach ($form_values['accounts'] as $uid => $value) { user_delete($form_values, $uid); } drupal_set_message(t('The users have been deleted.')); } - return 'admin/user/user'; + $form_state['redirect'] = 'admin/user/user'; + return; } function user_admin_settings() { @@ -2783,7 +2793,7 @@ function theme_user_filters($form) { /** * Process result from user administration filter form. */ -function user_filter_form_submit($form_id, $form_values) { +function user_filter_form_submit($form_values, $form, &$form_state) { $op = $form_values['op']; $filters = user_filters(); switch ($op) { @@ -2807,7 +2817,8 @@ function user_filter_form_submit($form_i return; } - return 'admin/user/user'; + $form_state['redirect'] = 'admin/user/user'; + return; }