Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.190
diff -u -p -r1.190 form.inc
--- includes/form.inc	24 Apr 2007 13:53:10 -0000	1.190
+++ includes/form.inc	28 Apr 2007 19:34:20 -0000
@@ -27,92 +27,104 @@
  */
 
 /**
- * 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);
-  }
-
-  return drupal_render_form($args[0], $form);
-}
-
+  $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  $expire = max(ini_get('session.cookie_lifetime'), 86400);
 
-/**
- * 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 = $cached->data;
+      if ($cached = cache_get('storage_'. $_POST['form_build_id'], 'cache_form')) {
+        $form_state['storage'] = $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);
+    if (!empty($form['#cache'])) {
+      cache_set('form_'. $form_build_id, $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);
+
+  // Most simple, single-step forms will never get to this point --
+  // drupal_process_form() usually redirects to another page, or to
+  // a 'fresh' copy of the form, once processing is complete. If one
+  // of the form's handlers set $form_state['redirect'] to FALSE,
+  // the form will simply be re-rendered with the values still in its
+  // fields.
+  // 
+  // If $form_state['storage'] or $form_state['rebuild'] have been
+  // set by any submit or validate handlers, however, we know that
+  // we're in a complex multi-part process of some sort and the form's
+  // workflow is NOT complete. We need to construct a fresh copy of
+  // the form, passing in the latest $form_state in addition to any
+  // other variables passed into drupal_get_form().
+  if (!empty($form_state['rebuild']) || !empty($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);
+
+    // Now, we cache the form structure so it can be retrieved later for
+    // validation. If $form_state['storage'] is populated, we'll also cache
+    // it so that it can be used to resume complex multi-step processes.
+    cache_set('form_'. $form_build_id, $form, 'cache_form', $expire);
+    if (!empty($form_state['storage'])) {
+      cache_set('storage_'. $form_build_id, $form_state['storage'], '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 +136,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 +192,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 +211,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,41 +253,55 @@ 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;
   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
   // in earlier recursive calls.
-  array_push($saved_globals, array($form_values, $form_submitted, $form_button_counter));
+  array_push($saved_globals, array($form_values));
 
   $form_values = array();
-  $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.
-    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 = form_builder($form_id, $form, $form_state);
+  if ((!empty($form['#programmed'])) || (!empty($form['#post']) && (($form['#post']['form_id'] == $form_id)))) {
+    drupal_validate_form($form_id, $form, $form_state);
+
+    if ((($form['#programmed']) || !empty($form_state['submitted'])) && !form_get_errors() && empty($form_state['rebuild'])) {
+      $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_'. $form_values['form_build_id'], 'cache_form');
+        cache_clear_all('storage_'. $form_values['form_build_id'], 'cache_form');
+      }
+
+      // If no submit handlers have populated the $form_state['storage']
+      // bundle, and the $form_state['rebuild'] flag has not been set,
+      // we're finished and should redirect to a new destination page
+      // if one has been set (and a fresh, unpopulated copy of the form
+      // if one hasn't). 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['rebuild']) && empty($form_state['storage'])) {
+        drupal_redirect_form($form, $form_state['redirect']);
       }
     }
   }
 
   // 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;
-  }
+  list($form_values) = array_pop($saved_globals);
 }
 
 /**
@@ -279,25 +314,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 $form_state
+ *   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, &$form_state) {
   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 +361,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 +375,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, $form_state);
 }
 
 
@@ -366,9 +399,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 +427,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 +440,29 @@ 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.
- *
+ * @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, or FALSE to prevent redirection.
+ *     $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_id, $form) {
-  global $form_values;
-  $default_args = array($form_id, &$form_values);
-  $submitted = FALSE;
-  $goto = NULL;
+function drupal_submit_form($form, &$form_state) {
+  $submitted = _form_execute_handlers('submit', $form, $form_state);
 
-  if (isset($form['#submit'])) {
-    foreach ($form['#submit'] as $function => $args) {
-      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);
-        $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'])));
   }
 }
 
@@ -487,10 +513,11 @@ function drupal_render_form($form_id, &$
  *
  */
 function drupal_redirect_form($form, $redirect = NULL) {
+  $goto = NULL;
   if (isset($redirect)) {
     $goto = $redirect;
   }
-  if (isset($form['#redirect'])) {
+  if ($goto !== FALSE && isset($form['#redirect'])) {
     $goto = $form['#redirect'];
   }
   if (!isset($goto) || ($goto !== FALSE)) {
@@ -513,15 +540,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 +597,16 @@ 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)) {
+      _form_execute_handlers('validate', $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);
         }
       }
     }
@@ -581,6 +614,27 @@ function _form_validate($elements, $form
   }
 }
 
+function _form_execute_handlers($type, &$form, &$form_state) {
+  $return = FALSE;
+  if (isset($form_state[$type .'_handlers'])) {
+    $handlers = $form_state[$type .'_handlers'];
+  }
+  elseif (isset($form['#'. $type])) {
+    $handlers = $form['#'. $type];
+  }
+  else {
+    $handlers = array();
+  }
+
+  foreach ($handlers as $function) {
+    if (function_exists($function))  {
+      $function($GLOBALS['form_values'], $form, $form_state);
+      $return = TRUE;
+    }
+  }
+  return $return;
+}
+
 /**
  * File an error against a form element. If the name of the element is
  * edit[foo][bar] then you may pass either foo or foo][bar as $name
@@ -641,9 +695,13 @@ function form_error(&$element, $message 
  *   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. In this
+ *   context, it is used to accumulate information about which button
+ *   was clicked when the form was submitted.
  */
-function form_builder($form_id, $form) {
-  global $form_values, $form_submitted, $form_button_counter;
+function form_builder($form_id, $form, &$form_state) {
+  global $form_values;
 
   // Initialize as unprocessed.
   $form['#processed'] = FALSE;
@@ -655,120 +713,7 @@ function form_builder($form_id, $form) {
   }
 
   if (isset($form['#input']) && $form['#input']) {
-    if (!isset($form['#name'])) {
-      $name = array_shift($form['#parents']);
-      $form['#name'] = $name;
-      if ($form['#type'] == 'file') {
-        // To make it easier to handle $_FILES in file.inc, we place all
-        // file fields in the 'files' array. Also, we do not support
-        // nested file names.
-        $form['#name'] = 'files['. $form['#name'] .']';
-      }
-      elseif (count($form['#parents'])) {
-        $form['#name'] .= '['. implode('][', $form['#parents']) .']';
-      }
-      array_unshift($form['#parents'], $name);
-    }
-    if (!isset($form['#id'])) {
-      $form['#id'] = form_clean_id('edit-'. implode('-', $form['#parents']));
-    }
-
-    if (isset($form['#disabled']) && $form['#disabled']) {
-      $form['#attributes']['disabled'] = 'disabled';
-    }
-
-    if (!isset($form['#value']) && !array_key_exists('#value', $form)) {
-      if (($form['#programmed']) || ((!isset($form['#access']) || $form['#access']) && isset($form['#post']) && (isset($form['#post']['form_id']) && $form['#post']['form_id'] == $form_id))) {
-        $edit = $form['#post'];
-        foreach ($form['#parents'] as $parent) {
-          $edit = isset($edit[$parent]) ? $edit[$parent] : NULL;
-        }
-        if (!$form['#programmed'] || isset($edit)) {
-          switch ($form['#type']) {
-            case 'checkbox':
-              $form['#value'] = !empty($edit) ? $form['#return_value'] : 0;
-              break;
-
-            case 'select':
-              if (isset($form['#multiple']) && $form['#multiple']) {
-                if (isset($edit) && is_array($edit)) {
-                  $form['#value'] = drupal_map_assoc($edit);
-                }
-                else {
-                  $form['#value'] = array();
-                }
-              }
-              elseif (isset($edit)) {
-                $form['#value'] = $edit;
-              }
-              break;
-
-            case 'textfield':
-              if (isset($edit)) {
-                // Equate $edit to the form value to ensure it's marked for
-                // validation.
-                $edit = str_replace(array("\r", "\n"), '', $edit);
-                $form['#value'] = $edit;
-              }
-              break;
-
-            case 'token':
-              $form['#value'] = (string)$edit;
-              break;
-
-            default:
-              if (isset($edit)) {
-                $form['#value'] = $edit;
-              }
-          }
-          // Mark all posted values for validation.
-          if ((isset($form['#value']) && $form['#value'] === $edit) || (isset($form['#required']) && $form['#required'])) {
-            $form['#needs_validation'] = TRUE;
-          }
-        }
-      }
-      if (!isset($form['#value'])) {
-        $function = $form['#type'] .'_value';
-        if (function_exists($function)) {
-          $function($form);
-        }
-        else {
-          $form['#value'] = isset($form['#default_value']) ? $form['#default_value'] : '';
-        }
-      }
-    }
-    if (isset($form['#executes_submit_callback'])) {
-      // Count submit and non-submit buttons.
-      $form_button_counter[$form['#executes_submit_callback']]++;
-      // See if a submit button was pressed.
-      if (isset($form['#post'][$form['#name']]) && $form['#post'][$form['#name']] == $form['#value']) {
-        $form_submitted = $form_submitted || $form['#executes_submit_callback'];
-
-        // In most cases, we want to use form_set_value() to manipulate the
-        // global variables. In this special case, we want to make sure that
-        // the value of this element is listed in $form_variables under 'op'.
-        $form_values[$form['#name']] = $form['#value'];
-      }
-    }
-  }
-
-  // Allow for elements to expand to multiple elements, e.g., radios,
-  // checkboxes and files.
-  if (isset($form['#process']) && !$form['#processed']) {
-    foreach ($form['#process'] as $process => $args) {
-      if (function_exists($process)) {
-        $args = array_merge(array($form), array(isset($edit) ? $edit : NULL), $args);
-        $form = call_user_func_array($process, $args);
-      }
-    }
-    $form['#processed'] = TRUE;
-  }
-
-  // Set the $form_values key that gets passed to validate and submit.
-  // We call this after #process gets called so that #process has a
-  // chance to update #value if desired.
-  if (isset($form['#input']) && $form['#input']) {
-    form_set_value($form, $form['#value']);
+    _form_builder_handle_input_element($form_id, $form, $form_state);
   }
 
   // We start off assuming all form elements are in the correct order.
@@ -805,20 +750,163 @@ function form_builder($form_id, $form) {
       // later.
       unset($form['#sorted']);
     }
-    $form[$key] = form_builder($form_id, $form[$key]);
+    $form[$key] = form_builder($form_id, $form[$key], $form_state);
     $count++;
   }
 
-  if (isset($form['#after_build']) && !isset($form['#after_build_done'])) {
-    foreach ($form['#after_build'] as $function) {
+  // Now that we've processed everything, we can go back to handle the funky
+  // Internet Explorer button-click scenerio.
+  _form_builder_ie_cleanup($form, $form_state);
+
+  return $form;
+}
+
+/**
+ * Handle the non-clicked single submit button in the world's best browser.
+ */
+function _form_builder_ie_cleanup($form, &$form_state) {
+  if (!empty($form['#type']) && $form['#type'] == 'form') {
+    // If the 'submitted' flag isn't tripped, but there is only one submit button...
+    if (empty($form_state['submitted']) && !empty($form_state['buttons']['submit']) && empty($form_state['buttons']['button'])) {
+      $button = $form_state['buttons']['submit'][0];
+      $form_state['submitted'] = TRUE;
+      $form_state['submit_handlers'] = $button['#submit'];
+      $form_state['validate_handlers'] = $button['#validate'];
+      $form_values[$button['#name']] = $button['#value'];
+    }
+    // After handling the special IE case, we no longer need the buttons collection.
+    unset($form_state['buttons']);
+  }
+}
+
+function _form_builder_handle_input_element($form_id, &$form, &$form_state) {
+  if (!isset($form['#name'])) {
+    $name = array_shift($form['#parents']);
+    $form['#name'] = $name;
+    if ($form['#type'] == 'file') {
+      // To make it easier to handle $_FILES in file.inc, we place all
+      // file fields in the 'files' array. Also, we do not support
+      // nested file names.
+      $form['#name'] = 'files['. $form['#name'] .']';
+    }
+    elseif (count($form['#parents'])) {
+      $form['#name'] .= '['. implode('][', $form['#parents']) .']';
+    }
+    array_unshift($form['#parents'], $name);
+  }
+  if (!isset($form['#id'])) {
+    $form['#id'] = form_clean_id('edit-'. implode('-', $form['#parents']));
+  }
+
+  if (!empty($form['#disabled'])) {
+    $form['#attributes']['disabled'] = 'disabled';
+  }
+
+  if (!isset($form['#value']) && !array_key_exists('#value', $form)) {
+    if (($form['#programmed']) || ((!isset($form['#access']) || $form['#access']) && isset($form['#post']) && (isset($form['#post']['form_id']) && $form['#post']['form_id'] == $form_id))) {
+      $edit = $form['#post'];
+      foreach ($form['#parents'] as $parent) {
+        $edit = isset($edit[$parent]) ? $edit[$parent] : NULL;
+      }
+      if (!$form['#programmed'] || isset($edit)) {
+        switch ($form['#type']) {
+          case 'checkbox':
+            $form['#value'] = !empty($edit) ? $form['#return_value'] : 0;
+            break;
+
+          case 'select':
+            if (isset($form['#multiple']) && $form['#multiple']) {
+              if (isset($edit) && is_array($edit)) {
+                $form['#value'] = drupal_map_assoc($edit);
+              }
+              else {
+                $form['#value'] = array();
+              }
+            }
+            elseif (isset($edit)) {
+              $form['#value'] = $edit;
+            }
+            break;
+
+          case 'textfield':
+            if (isset($edit)) {
+              // Equate $edit to the form value to ensure it's marked for
+              // validation.
+              $edit = str_replace(array("\r", "\n"), '', $edit);
+              $form['#value'] = $edit;
+            }
+            break;
+
+          case 'token':
+            $form['#value'] = (string)$edit;
+            break;
+
+          default:
+            if (isset($edit)) {
+              $form['#value'] = $edit;
+            }
+        }
+        // Mark all posted values for validation.
+        if ((isset($form['#value']) && $form['#value'] === $edit) || (isset($form['#required']) && $form['#required'])) {
+          $form['#needs_validation'] = TRUE;
+        }
+      }
+    }
+    if (!isset($form['#value'])) {
+      $function = 'form_'. $form['#type'] .'_value';
       if (function_exists($function)) {
-        $form = $function($form, $form_values);
+        $function($form);
+      }
+      else {
+        $form['#value'] = isset($form['#default_value']) ? $form['#default_value'] : '';
       }
     }
-    $form['#after_build_done'] = TRUE;
   }
 
-  return $form;
+  // Determine which button (if any) was clicked to submit the form.
+  // We compare the incoming values with the buttons defined in the form,
+  // and flag the one that matches. We have to do some funky tricks to
+  // deal with Internet Explorer's handling of single-button forms, though.
+  if (!empty($form['#post']) && isset($form['#executes_submit_callback'])) {
+    // First, accumulate a collection of buttons, divided into two bins:
+    // those that execute full submit callbacks and those that only validate.
+    $button_type = $form['#executes_submit_callback'] ? 'submit' : 'button';
+    $form_state['buttons'][$button_type][] = $form;
+
+    // See if a submit button was clicked. In Internet Explorer, if ONLY
+    // one submit button is present, AND the enter key is used to submit
+    // the form, no form value is sent for it and we'll never detect a
+    // match. In most cases, though, the following code will properly handle
+    // finding the clicked button and storing any custom validate and
+    // submit handlers it has defined.
+    if (isset($form['#post'][$form['#name']]) && $form['#post'][$form['#name']] == $form['#value']) {
+      $form_state['submitted'] = $form_state['submitted'] || $form['#executes_submit_callback'];
+
+      // In most cases, we want to use form_set_value() to manipulate
+      // the global variables. In this special case, we want to make sure that
+      // the value of this element is listed in $form_variables under 'op'.
+      $form_values[$form['#name']] = $form['#value'];
+
+      if (isset($form['#validate'])) {
+        $form_state['validate_handlers'] = $form['#submit'];
+      }
+      if (isset($form['#submit'])) {
+        $form_state['submit_handlers'] = $form['#submit'];
+      }
+    }
+  }
+  // Allow for elements to expand to multiple elements, e.g., radios,
+  // checkboxes and files.
+  if (isset($form['#process']) && !$form['#processed']) {
+    foreach ($form['#process'] as $process) {
+      if (function_exists($process)) {
+        $args = array_merge(array($form), array(isset($edit) ? $edit : NULL), array($form_state));
+        $form = call_user_func_array($process, $args);
+      }
+    }
+    $form['#processed'] = TRUE;
+  }
+  form_set_value($form, $form['#value']);
 }
 
 /**
@@ -1117,7 +1205,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'])) {
@@ -1231,7 +1319,7 @@ function map_month($month) {
 /**
  * Helper function to load value from default value for checkboxes.
  */
-function checkboxes_value(&$form) {
+function form_checkboxes_value(&$form) {
   $value = array();
   $form += array('#default_value' => array());
   foreach ($form['#default_value'] as $key) {
Index: modules/aggregator/aggregator.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.module,v
retrieving revision 1.337
diff -u -p -r1.337 aggregator.module
--- modules/aggregator/aggregator.module	24 Apr 2007 13:53:11 -0000	1.337
+++ modules/aggregator/aggregator.module	28 Apr 2007 19:34:21 -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) {
Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.257
diff -u -p -r1.257 block.module
--- modules/block/block.module	17 Apr 2007 07:19:38 -0000	1.257
+++ modules/block/block.module	28 Apr 2007 19:34:21 -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;
 };
 
 /**
Index: modules/book/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.module,v
retrieving revision 1.418
diff -u -p -r1.418 book.module
--- modules/book/book.module	24 Apr 2007 13:53:11 -0000	1.418
+++ modules/book/book.module	28 Apr 2007 19:34:21 -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' => '<p>'. t('There are no orphan pages.') .'</p>');
   }
-  $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']);
 
Index: modules/color/color.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/color/color.module,v
retrieving revision 1.18
diff -u -p -r1.18 color.module
--- modules/color/color.module	6 Apr 2007 13:27:21 -0000	1.18
+++ modules/color/color.module	28 Apr 2007 19:34:21 -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;
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.540
diff -u -p -r1.540 comment.module
--- modules/comment/comment.module	27 Apr 2007 07:42:54 -0000	1.540
+++ modules/comment/comment.module	28 Apr 2007 19:34:22 -0000
@@ -1224,7 +1224,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.'));
@@ -1236,7 +1236,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
@@ -1321,7 +1321,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);
@@ -1676,7 +1676,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);
 }
 
@@ -1706,13 +1706,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;
   }
 }
 
@@ -1795,7 +1797,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'];
Index: modules/contact/contact.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/contact/contact.module,v
retrieving revision 1.81
diff -u -p -r1.81 contact.module
--- modules/contact/contact.module	24 Apr 2007 13:53:11 -0000	1.81
+++ modules/contact/contact.module	28 Apr 2007 19:34:22 -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;
 }
 
Index: modules/dblog/dblog.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/dblog/dblog.module,v
retrieving revision 1.3
diff -u -p -r1.3 dblog.module
--- modules/dblog/dblog.module	24 Apr 2007 13:53:11 -0000	1.3
+++ modules/dblog/dblog.module	28 Apr 2007 19:34:22 -0000
@@ -232,7 +232,7 @@ function theme_dblog_form_overview($form
   return '<div class="container-inline">'. drupal_render($form) .'</div>';
 }
 
-function dblog_form_overview_submit($form_id, $form_values) {
+function dblog_form_overview_submit($form_values, $form, &$form_state) {
   $_SESSION['dblog_overview_filter'] = $form_values['filter'];
 }
 
Index: modules/filter/filter.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v
retrieving revision 1.171
diff -u -p -r1.171 filter.module
--- modules/filter/filter.module	24 Apr 2007 13:53:11 -0000	1.171
+++ modules/filter/filter.module	28 Apr 2007 19:34:22 -0000
@@ -296,7 +296,7 @@ function filter_admin_overview() {
   return $form;
 }
 
-function filter_admin_overview_submit($form_id, $form_values) {
+function filter_admin_overview_submit($form_values, $form, &$form_state) {
   // Process form submission to set the default format
   if (is_numeric($form_values['default'])) {
     drupal_set_message(t('Default format updated.'));
@@ -352,7 +352,7 @@ function filter_admin_delete() {
 /**
  * Process filter delete form submission.
  */
-function filter_admin_delete_submit($form_id, $form_values) {
+function filter_admin_delete_submit($form_values, $form, &$form_state) {
   db_query("DELETE FROM {filter_formats} WHERE format = %d", $form_values['format']);
   db_query("DELETE FROM {filters} WHERE format = %d", $form_values['format']);
 
@@ -365,7 +365,8 @@ function filter_admin_delete_submit($for
   cache_clear_all($form_values['format'] .':', 'cache_filter', TRUE);
   drupal_set_message(t('Deleted input format %format.', array('%format' => $form_values['name'])));
 
-  return 'admin/settings/filters';
+  $form_state['redirect'] = 'admin/settings/filters';
+  return;
 }
 
 /**
@@ -441,7 +442,7 @@ function filter_admin_format_form($forma
 /**
  * Validate filter format form submissions.
  */
-function filter_admin_format_form_validate($form_id, $form_values) {
+function filter_admin_format_form_validate($form_values, $form, &$form_state) {
   if (!isset($form_values['format'])) {
     $name = trim($form_values['name']);
     $result = db_fetch_object(db_query("SELECT format FROM {filter_formats} WHERE name='%s'", $name));
@@ -454,7 +455,7 @@ function filter_admin_format_form_valida
 /**
  * Process filter format form submissions.
  */
-function filter_admin_format_form_submit($form_id, $form_values) {
+function filter_admin_format_form_submit($form_values, $form, &$form_state) {
   $format = isset($form_values['format']) ? $form_values['format'] : NULL;
   $current = filter_list_format($format);
   $name = trim($form_values['name']);
@@ -506,7 +507,8 @@ function filter_admin_format_form_submit
   if (!empty($new)) {
     $return .= '/'. $format;
   }
-  return $return;
+  $form_state['redirect'] = $return;
+  return;
 }
 
 /**
@@ -549,7 +551,7 @@ function theme_filter_admin_order($form)
 /**
  * Process filter order configuration form submission.
  */
-function filter_admin_order_submit($form_id, $form_values) {
+function filter_admin_order_submit($form_values, $form, &$form_state) {
   foreach ($form_values['weights'] as $id => $weight) {
     list($module, $delta) = explode('/', $id);
     db_query("UPDATE {filters} SET weight = %d WHERE format = %d AND module = '%s' AND delta = %d", $weight, $form_values['format'], $module, $delta);
@@ -784,7 +786,7 @@ function filter_form($value = FILTER_FOR
       '#collapsible' => TRUE,
       '#collapsed' => TRUE,
       '#weight' => $weight,
-      '#validate' => array('filter_form_validate' => array()),
+      '#element_validate' => array('filter_form_validate'),
     );
     // Multiple formats available: display radio buttons with tips.
     foreach ($formats as $format) {
Index: modules/forum/forum.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v
retrieving revision 1.394
diff -u -p -r1.394 forum.module
--- modules/forum/forum.module	24 Apr 2007 13:53:12 -0000	1.394
+++ modules/forum/forum.module	28 Apr 2007 19:34:22 -0000
@@ -499,8 +499,8 @@ function forum_form_container($edit = ar
     $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
     $form['tid'] = array('#type' => 'value', '#value' => $edit['tid']);
   }
-  $form['#submit']['forum_form_submit'] = array();
-  $form['#validate']['forum_form_validate'] = array();
+  $form['#submit'][] = 'forum_form_submit';
+  $form['#validate'][] = 'forum_form_validate';
   $form['#theme'] = 'forum_form';
 
   return $form;
@@ -558,8 +558,8 @@ function forum_form_forum($edit = array(
     $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
     $form['tid'] = array('#type' => 'hidden', '#value' => $edit['tid']);
   }
-  $form['#submit']['forum_form_submit'] = array();
-  $form['#validate']['forum_form_validate'] = array();
+  $form['#submit'][] = 'forum_form_submit';
+  $form['#validate'][] = 'forum_form_validate';
   $form['#theme'] = 'forum_form';
 
   return $form;
@@ -568,7 +568,7 @@ function forum_form_forum($edit = array(
 /**
  * Process forum form and container form submissions.
  */
-function forum_form_submit($form_id, $form_values) {
+function forum_form_submit($form_values, $form, &$form_state) {
   if ($form_id == 'forum_form_container') {
     $container = TRUE;
     $type = t('forum container');
@@ -592,7 +592,8 @@ function forum_form_submit($form_id, $fo
       drupal_set_message(t('The @type %term has been updated.', array('%term' => $form_values['name'], '@type' => $type)));
       break;
   }
-  return 'admin/content/forum';
+  $form_state['redirect'] = 'admin/content/forum';
+  return;
 }
 
 /**
@@ -612,12 +613,13 @@ function forum_confirm_delete($tid) {
 /**
  * Implementation of forms api _submit call. Deletes a forum after confirmation.
  */
-function forum_confirm_delete_submit($form_id, $form_values) {
+function forum_confirm_delete_submit($form_values, $form, &$form_state) {
   taxonomy_del_term($form_values['tid']);
   drupal_set_message(t('The forum %term and all sub-forums and associated posts have been deleted.', array('%term' => $form_values['name'])));
   watchdog('content', 'forum: deleted %term and all its sub-forums and associated posts.', array('%term' => $form_values['name']));
 
-  return 'admin/content/forum';
+  $form_state['redirect'] = 'admin/content/forum';
+  return;
 }
 
 /**
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.169
diff -u -p -r1.169 locale.module
--- modules/locale/locale.module	25 Apr 2007 21:34:32 -0000	1.169
+++ modules/locale/locale.module	28 Apr 2007 19:34:22 -0000
@@ -415,7 +415,7 @@ function locale_admin_manage_delete_form
 /**
  * Process language deletion submissions.
  */
-function locale_admin_manage_delete_form_submit($form_id, $form_values) {
+function locale_admin_manage_delete_form_submit($form_values, $form, &$form_state) {
   $languages = language_list();
   if (isset($languages[$form_values['langcode']])) {
     db_query("DELETE FROM {languages} WHERE language = '%s'", $form_values['langcode']);
@@ -428,7 +428,8 @@ function locale_admin_manage_delete_form
   // Changing the locale settings impacts the interface:
   cache_clear_all('*', 'cache_page', TRUE);
 
-  return 'admin/build/locale/language/overview';
+  $form_state['redirect'] = 'admin/build/locale/language/overview';
+  return;
 }
 
 /**
@@ -495,9 +496,10 @@ function locale_admin_string_edit($lid) 
 /**
  * Process the string edit form.
  */
-function locale_admin_string_edit_submit($form_id, $form_values) {
+function locale_admin_string_edit_submit($form_values, $form, &$form_state) {
   include_once './includes/locale.inc';
-  return _locale_string_edit_submit($form_id, $form_values);
+  $form_state['redirect'] = _locale_string_edit_submit($form_id, $form_values);
+  return;
 }
 
 /**
Index: modules/menu/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v
retrieving revision 1.108
diff -u -p -r1.108 menu.module
--- modules/menu/menu.module	24 Apr 2007 13:53:12 -0000	1.108
+++ modules/menu/menu.module	28 Apr 2007 19:34:22 -0000
@@ -321,7 +321,7 @@ function menu_edit_item_form($type, $mid
   return $form;
 }
 
-function menu_edit_item_form_validate($form_id, $form_values) {
+function menu_edit_item_form_validate($form_values, $form, &$form_state) {
   if (isset($form_values['path'])) {
     $path = $form_values['path'];
     // Skip external links.
@@ -341,9 +341,10 @@ function menu_edit_item_form_validate($f
 /**
  * Process menu and menu item add/edit form submissions.
  */
-function menu_edit_item_form_submit($form_id, $form_values) {
+function menu_edit_item_form_submit($form_values, $form, &$form_state) {
   menu_edit_item_save($form_values);
-  return 'admin/build/menu';
+  $form_state['redirect'] = 'admin/build/menu';
+  return;
 }
 
 /**
@@ -461,8 +462,8 @@ function menu_edit_menu_form($type, $mid
   $form['type'] = array('#type' => 'value', '#value' => $item['type']);
   $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
   // Reuse the submit function of menu_edit_item_form.
-  $form['#submit']['menu_edit_item_form_submit'] = array();
-  $form['#validate']['menu_edit_item_form_validate'] = array();
+  $form['#submit'][] = 'menu_edit_item_form_submit';
+  $form['#validate'][] = 'menu_edit_item_form_validate';
   $form['#theme'] = 'menu_edit_item_form';
 
   return $form;
@@ -494,7 +495,7 @@ function menu_item_delete_form($mid) {
 /**
  * Process menu delete form submissions.
  */
-function menu_item_delete_form_submit($form_id, $form_values) {
+function menu_item_delete_form_submit($form_values, $form, &$form_state) {
   menu_delete_item($form_values['path']);
 
   $t_args = array('%title' => $form_values['title']);
@@ -507,7 +508,8 @@ function menu_item_delete_form_submit($f
     watchdog('menu', 'Deleted menu item %title.', $t_args, WATCHDOG_NOTICE);
   }
 
-  return 'admin/build/menu';
+  $form_state['redirect'] = 'admin/build/menu';
+  return;
 }
 
 /**
@@ -526,11 +528,12 @@ function menu_reset_item($mid) {
 /**
  * Process menu reset item form submissions.
  */
-function menu_reset_item_submit($form_id, $form_values) {
+function menu_reset_item_submit($form_values, $form, &$form_state) {
   menu_delete_item($form_values['mid']);
   drupal_set_message(t('The menu item was reset to its default settings.'));
 
-  return 'admin/build/menu';
+  $form_state['redirect'] = 'admin/build/menu';
+  return;
 }
 
 /**
Index: modules/node/content_types.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/content_types.inc,v
retrieving revision 1.29
diff -u -p -r1.29 content_types.inc
--- modules/node/content_types.inc	24 Apr 2007 13:53:13 -0000	1.29
+++ modules/node/content_types.inc	28 Apr 2007 19:34:23 -0000
@@ -216,7 +216,7 @@ function node_type_form($type = NULL) {
 /**
  * Implementation of hook_form_validate().
  */
-function node_type_form_validate($form_id, $form_values) {
+function node_type_form_validate($form_values, $form, &$form_state) {
   $type = new stdClass();
   $type->type = trim($form_values['type']);
   $type->name = trim($form_values['name']);
@@ -252,7 +252,7 @@ function node_type_form_validate($form_i
 /**
  * Implementation of hook_form_submit().
  */
-function node_type_form_submit($form_id, $form_values) {
+function node_type_form_submit($form_values, $form, &$form_state) {
   $op = isset($form_values['op']) ? $form_values['op'] : '';
 
   $type = new stdClass();
@@ -282,9 +282,12 @@ function node_type_form_submit($form_id,
     node_type_reset($type);
   }
   elseif ($op == t('Delete content type')) {
-    return 'admin/content/types/'. str_replace('_', '-', $type->old_type) .'/delete';
+    $form_state['redirect'] = 'admin/content/types/'. str_replace('_', '-', $type->old_type) .'/delete';
+    return;
   }
-
+if (empty($type)) {
+  var_export($type);
+}
   $status = node_type_save($type);
 
   $variables = $form_values;
@@ -336,7 +339,8 @@ function node_type_form_submit($form_id,
     watchdog('node', 'Added content type %name.', $t_args, WATCHDOG_NOTICE, l(t('view'), 'admin/content/types'));
   }
 
-  return 'admin/content/types';
+  $form_state['redirect'] = 'admin/content/types';
+  return;
 }
 
 /**
@@ -396,7 +400,7 @@ function node_type_delete_confirm($type)
 /**
  * Process content type delete confirm submissions.
  */
-function node_type_delete_confirm_submit($form_id, $form_values) {
+function node_type_delete_confirm_submit($form_values, $form, &$form_state) {
   node_type_delete($form_values['type']);
 
   $t_args = array('%name' => $form_values['name']);
@@ -406,5 +410,6 @@ function node_type_delete_confirm_submit
   node_types_rebuild();
   menu_rebuild();
 
-  return 'admin/content/types';
+  $form_state['redirect'] = 'admin/content/types';
+  return;
 }
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.805
diff -u -p -r1.805 node.module
--- modules/node/node.module	25 Apr 2007 21:28:00 -0000	1.805
+++ modules/node/node.module	28 Apr 2007 19:34:23 -0000
@@ -1060,7 +1060,7 @@ function node_configure() {
 /**
  * Form validate callback.
  */
-function node_configure_validate($form_id, $form_values) {
+function node_configure_validate($form_values, $form, &$form_state) {
   if ($form_values['op'] == t('Rebuild permissions')) {
     drupal_goto('admin/content/node-settings/rebuild');
   }
@@ -1077,10 +1077,11 @@ function node_configure_rebuild_confirm(
 /**
  * Handler for wipe confirmation
  */
-function node_configure_rebuild_confirm_submit($form_id, &$form) {
+function node_configure_rebuild_confirm_submit(&$form, $form, &$form_state) {
   node_access_rebuild();
   drupal_set_message(t('The node access table has been rebuilt.'));
-  return 'admin/content/node-settings';
+  $form_state['redirect'] = 'admin/content/node-settings';
+  return;
 }
 
 /**
@@ -1508,7 +1509,7 @@ function theme_node_filters($form) {
 /**
  * Process result from node administration filter form.
  */
-function node_filter_form_submit($form_id, $form_values) {
+function node_filter_form_submit($form_values, $form, &$form_state) {
   $filters = node_filters();
   switch ($form_values['op']) {
     case t('Filter'):
@@ -1536,7 +1537,7 @@ function node_filter_form_submit($form_i
 /**
  * Submit the node administration update form.
  */
-function node_admin_nodes_submit($form_id, $form_values) {
+function node_admin_nodes_submit($form_values, $form, &$form_state) {
   $operations = module_invoke_all('node_operations');
   $operation = $operations[$form_values['operation']];
   // Filter out unchecked nodes
@@ -1556,7 +1557,7 @@ function node_admin_nodes_submit($form_i
   }
 }
 
-function node_admin_nodes_validate($form_id, $form_values) {
+function node_admin_nodes_validate($form_values, $form, &$form_state) {
   $nodes = array_filter($form_values['nodes']);
   if (count($nodes) == 0) {
     form_set_error('', t('No items selected.'));
@@ -1677,14 +1678,15 @@ function node_multiple_delete_confirm() 
                       t('Delete all'), t('Cancel'));
 }
 
-function node_multiple_delete_confirm_submit($form_id, $form_values) {
+function node_multiple_delete_confirm_submit($form_values, $form, &$form_state) {
   if ($form_values['confirm']) {
     foreach ($form_values['nodes'] as $nid => $value) {
       node_delete($nid);
     }
     drupal_set_message(t('The items have been deleted.'));
   }
-  return 'admin/content/node';
+  $form_state['redirect'] = 'admin/content/node';
+  return;
 }
 
 /**
@@ -1997,7 +1999,7 @@ function node_validate($node, $form = ar
   node_invoke_nodeapi($node, 'validate', $form);
 }
 
-function node_form_validate($form_id, $form_values, $form) {
+function node_form_validate($form_values, $form, &$form_state) {
   node_validate($form_values, $form);
 }
 
@@ -2118,8 +2120,8 @@ function node_form($node, $form_values =
     $form['delete'] = array('#type' => 'button', '#value' => t('Delete'), '#weight' => 50);
   }
   $form['#after_build'] = array('node_form_add_preview');
-  $form['#submit']['node_form_submit'] = array();
-  $form['#validate']['node_form_validate'] = array();
+  $form['#submit'][] = 'node_form_submit';
+  $form['#validate'][] = 'node_form_validate';
   $form['#theme'] = 'node_form';
   return $form;
 }
@@ -2302,7 +2304,7 @@ function theme_node_log_message($log) {
   return '<div class="log"><div class="title">'. t('Log') .':</div>'. $log .'</div>';
 }
 
-function node_form_submit($form_id, $form_values) {
+function node_form_submit($form_values, $form, &$form_state) {
   global $user;
 
   // Fix up the node when required:
@@ -2321,14 +2323,17 @@ function node_form_submit($form_id, $for
   }
   if ($node->nid) {
     if (node_access('view', $node)) {
-      return 'node/'. $node->nid;
+      $form_state['redirect'] = 'node/'. $node->nid;
+      return;
     }
     else {
-      return '';
+      $form_state['redirect'] = '';
+      return;
     }
   }
   // it is very unlikely we get here
-  return FALSE;
+  $form_state['redirect'] = FALSE;
+  return;
 }
 
 /**
@@ -2339,7 +2344,7 @@ function node_delete_confirm($node) {
 
  return confirm_form($form,
    t('Are you sure you want to delete %title?', array('%title' => $node->title)),
-   $_GET['destination'] ? $_GET['destination'] : 'node/'. $node->nid,
+   !empty($_GET['destination']) ? $_GET['destination'] : 'node/'. $node->nid,
    t('This action cannot be undone.'),
    t('Delete'), t('Cancel'));
 }
@@ -2347,12 +2352,13 @@ function node_delete_confirm($node) {
 /**
  * Execute node deletion
  */
-function node_delete_confirm_submit($form_id, $form_values) {
+function node_delete_confirm_submit($form_values, $form, &$form_state) {
   if ($form_values['confirm']) {
     node_delete($form_values['nid']);
   }
 
-  return '<front>';
+  $form_state['redirect'] = '<front>';
+  return;
 }
 
 /**
@@ -2474,10 +2480,13 @@ function node_page_view($node, $cid = NU
 function node_page_edit($node) {
   if (isset($_POST['op']) && ($_POST['op'] == t('Delete'))) {
     // Note: we redirect from node/nid/edit to node/nid/delete to make the tabs disappear.
-    if ($_REQUEST['destination']) {
+    if (!empty($_REQUEST['destination'])) {
       $destination = drupal_get_destination();
       unset($_REQUEST['destination']);
     }
+    else {
+      $destination = '<front>';
+    }
     drupal_goto('node/'. $node->nid .'/delete', $destination);
   }
 
@@ -2603,14 +2612,14 @@ function node_form_alter(&$form, $form_i
       '#suffix' => '</div>',
     );
 
-    $form['#validate']['node_search_validate'] = array();
+    $form['#validate'][] = 'node_search_validate';
   }
 }
 
 /**
  * Form API callback for the search form. Registered in node_form_alter().
  */
-function node_search_validate($form_id, $form_values, $form) {
+function node_search_validate($form_values, $form, &$form_state) {
   // Initialise using any existing basic search keywords.
   $keys = $form_values['processed_keys'];
 
Index: modules/path/path.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/path/path.module,v
retrieving revision 1.114
diff -u -p -r1.114 path.module
--- modules/path/path.module	25 Apr 2007 21:28:00 -0000	1.114
+++ modules/path/path.module	28 Apr 2007 19:34:23 -0000
@@ -112,10 +112,11 @@ function path_admin_delete_confirm($pid)
 /**
  * Execute URL alias deletion
  **/
-function path_admin_delete_confirm_submit($form_id, $form_values) {
+function path_admin_delete_confirm_submit($form_values, $form, &$form_state) {
   if ($form_values['confirm']) {
     path_admin_delete($form_values['pid']);
-    return 'admin/build/path';
+    $form_state['redirect'] = 'admin/build/path';
+    return;
   }
 }
 
@@ -185,8 +186,8 @@ function path_set_alias($path = NULL, $a
  * Return a form for editing or creating an individual URL alias.
  */
 function path_form($edit = array('src' => '', 'dst' => '', 'language' => '', 'pid' => NULL)) {
-  $form['#submit']['path_form_submit'] = array();
-  $form['#validate']['path_form_validate'] = array();
+  $form['#submit'][] = 'path_form_submit';
+  $form['#validate'][] = 'path_form_validate';
   $form['#theme'] = 'path_form';
   $form['#alias'] = $edit;
 
@@ -364,7 +365,7 @@ function path_load($pid) {
 /**
  * Verify that a new URL alias is valid
  */
-function path_form_validate($form_id, $form_values) {
+function path_form_validate($form_values, $form, &$form_state) {
   $src = $form_values['src'];
   $dst = $form_values['dst'];
   $pid = isset($form_values['pid']) ? $form_values['pid'] : 0;
@@ -379,10 +380,11 @@ function path_form_validate($form_id, $f
 /**
  * Save a new URL alias to the database.
  */
-function path_form_submit($form_id, $form_values) {
+function path_form_submit($form_values, $form, &$form_state) {
   // Language is only set if locale module is enabled
   path_set_alias($form_values['src'], $form_values['dst'], isset($form_values['pid']) ? $form_values['pid'] : 0, isset($form_values['language']) ? $form_values['language'] : '');
 
   drupal_set_message(t('The alias has been saved.'));
-  return 'admin/build/path';
+  $form_state['redirect'] = 'admin/build/path';
+  return;
 }
Index: modules/profile/profile.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile/profile.module,v
retrieving revision 1.199
diff -u -p -r1.199 profile.module
--- modules/profile/profile.module	24 Apr 2007 13:53:14 -0000	1.199
+++ modules/profile/profile.module	28 Apr 2007 19:34:23 -0000
@@ -331,7 +331,7 @@ Unless you know what you are doing, it i
 /**
  * Validate profile_field_form submissions.
  */
-function profile_field_form_validate($form_id, $form_values) {
+function profile_field_form_validate($form_values, $form, &$form_state) {
   // Validate the 'field name':
   if (preg_match('/[^a-zA-Z0-9_-]/', $form_values['name'])) {
     form_set_error('name', t('The specified form name contains one or more illegal characters. Spaces or any other special characters except dash (-) and underscore (_) are not allowed.'));
@@ -367,7 +367,7 @@ function profile_field_form_validate($fo
 /**
  * Process profile_field_form submissions.
  */
-function profile_field_form_submit($form_id, $form_values) {
+function profile_field_form_submit($form_values, $form, &$form_state) {
   if (!isset($form_values['fid'])) {
     db_query("INSERT INTO {profile_fields} (title, name, explanation, category, type, weight, required, register, visibility, autocomplete, options, page) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, %d, '%s', '%s')", $form_values['title'], $form_values['name'], $form_values['explanation'], $form_values['category'], $form_values['type'], $form_values['weight'], $form_values['required'], $form_values['register'], $form_values['visibility'], $form_values['autocomplete'], $form_values['options'], $form_values['page']);
 
@@ -382,7 +382,8 @@ function profile_field_form_submit($form
   cache_clear_all();
   menu_rebuild();
 
-  return 'admin/user/profile';
+  $form_state['redirect'] = 'admin/user/profile';
+  return;
 }
 
 /**
@@ -406,7 +407,7 @@ function profile_field_delete($fid) {
 /**
  * Process a field delete form submission.
  */
-function profile_field_delete_submit($form_id, $form_values) {
+function profile_field_delete_submit($form_values, $form, &$form_state) {
   db_query('DELETE FROM {profile_fields} WHERE fid = %d', $form_values['fid']);
   db_query('DELETE FROM {profile_values} WHERE fid = %d', $form_values['fid']);
 
@@ -415,7 +416,8 @@ function profile_field_delete_submit($fo
   drupal_set_message(t('The field %field has been deleted.', array('%field' => $form_values['title'])));
   watchdog('profile', 'Profile field %field deleted.', array('%field' => $form_values['title']), WATCHDOG_NOTICE, l(t('view'), 'admin/user/profile'));
 
-  return 'admin/user/profile';
+  $form_state['redirect'] = 'admin/user/profile';
+  return;
 }
 
 /**
Index: modules/search/search.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/search/search.module,v
retrieving revision 1.219
diff -u -p -r1.219 search.module
--- modules/search/search.module	24 Apr 2007 13:53:14 -0000	1.219
+++ modules/search/search.module	28 Apr 2007 19:34:24 -0000
@@ -206,7 +206,7 @@ function _search_menu($name) {
 /**
  * Validate callback.
  */
-function search_admin_settings_validate($form_id, $form_values) {
+function search_admin_settings_validate($form_values, $form, &$form_state) {
   if ($form_values['op'] == t('Re-index site')) {
     drupal_goto('admin/settings/search/wipe');
   }
@@ -267,11 +267,12 @@ function search_wipe_confirm() {
 /**
  * Handler for wipe confirmation
  */
-function search_wipe_confirm_submit($form_id, &$form) {
+function search_wipe_confirm_submit(&$form, $form, &$form_state) {
   if ($form['confirm']) {
     search_wipe();
     drupal_set_message(t('The index will be rebuilt.'));
-    return 'admin/settings/search';
+    $form_state['redirect'] = 'admin/settings/search';
+    return;
   }
 }
 
@@ -1042,14 +1043,14 @@ function search_form($action = '', $keys
  * search_form_validate() is used solely to set the 'processed_keys' form
  * value for the basic search form.
  */
-function search_form_validate($form_id, $form_values, $form) {
+function search_form_validate($form_values, $form, &$form_state) {
   form_set_value($form['basic']['inline']['processed_keys'], trim($form_values['keys']));
 }
 
 /**
  * Process a search form submission.
  */
-function search_form_submit($form_id, $form_values) {
+function search_form_submit($form_values, $form, &$form_state) {
   $keys = $form_values['processed_keys'];
   if ($keys == '') {
     form_set_error('keys', t('Please enter some keywords.'));
@@ -1057,7 +1058,8 @@ function search_form_submit($form_id, $f
   }
 
   $type = $form_values['module'] ? $form_values['module'] : 'node';
-  return 'search/'. $type .'/'. $keys;
+  $form_state['redirect'] = 'search/'. $type .'/'. $keys;
+  return;
 }
 
 /**
@@ -1075,8 +1077,8 @@ function search_box($form_id) {
   // Always go to the search page since the search form is not guaranteed to be
   // on every page.
   $form['#action'] = url('search/node');
-  $form['#submit']['search_box_form_submit'] = array();
-  $form['#validate']['search_box_form_validate'] = array();
+  $form['#submit'][] = 'search_box_form_submit';
+  $form['#validate'][] = 'search_box_form_validate';
   $form['#theme'] = 'search_box_form';
 
   return $form;
@@ -1085,8 +1087,9 @@ function search_box($form_id) {
 /**
  * Process a block search form submission.
  */
-function search_box_form_submit($form_id, $form_values) {
-  return 'search/node/'. trim($form_values[$form_id .'_keys']);
+function search_box_form_submit($form_values, $form, &$form_state) {
+  $form_state['redirect'] = 'search/node/'. trim($form_values[$form_id .'_keys']);
+  return;
 }
 
 /**
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.99
diff -u -p -r1.99 system.install
--- modules/system/system.install	25 Apr 2007 21:34:32 -0000	1.99
+++ modules/system/system.install	28 Apr 2007 19:34:24 -0000
@@ -243,6 +243,16 @@ function system_install() {
         PRIMARY KEY (cid),
         INDEX expire (expire)
       ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
+      db_query("CREATE TABLE {cache_form} (
+        cid varchar(255) BINARY NOT NULL default '',
+        data longblob,
+        expire int NOT NULL default '0',
+        created int NOT NULL default '0',
+        headers text,
+        serialized int(1) NOT NULL default '0',
+        PRIMARY KEY (cid),
+        INDEX expire (expire)
+      ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
 
       db_query("CREATE TABLE {comments} (
         cid int NOT NULL auto_increment,
@@ -716,9 +726,19 @@ function system_install() {
         serialized int(1) NOT NULL default '0',
         PRIMARY KEY (cid)
       )");
+      db_query("CREATE TABLE {cache_form} (
+        cid varchar(255) NOT NULL default '',
+        data bytea,
+        expire int NOT NULL default '0',
+        created int NOT NULL default '0',
+        headers text,
+        serialized int(1) NOT NULL default '0',
+        PRIMARY KEY (cid)
+      )");
       db_query("CREATE INDEX {cache}_expire_idx ON {cache} (expire)");
       db_query("CREATE INDEX {cache_filter}_expire_idx ON {cache_filter} (expire)");
       db_query("CREATE INDEX {cache_page}_expire_idx ON {cache_page} (expire)");
+      db_query("CREATE INDEX {cache_form}_expire_idx ON {cache_form} (expire)");
 
       db_query("CREATE TABLE {comments} (
         cid serial,
@@ -3847,6 +3867,43 @@ function system_update_6012() {
 }
 
 /**
+ * Add the form cache table.
+ */
+function system_update_6013() {
+  $ret = array();
+
+  switch ($GLOBALS['db_type']) {
+    case 'pgsql':
+      $ret[] = update_sql("CREATE TABLE {cache_form} (
+        cid varchar(255) NOT NULL default '',
+        data bytea,
+        expire int NOT NULL default '0',
+        created int NOT NULL default '0',
+        headers text,
+        serialized int(1) NOT NULL default '0',
+        PRIMARY KEY (cid)
+    )");
+      $ret[] = update_sql("CREATE INDEX {cache_form}_expire_idx ON {cache_form} (expire)");
+      break;
+    case 'mysql':
+    case 'mysqli':
+      $ret[] = update_sql("CREATE TABLE {cache_form} (
+        cid varchar(255) NOT NULL default '',
+        data longblob,
+        expire int NOT NULL default '0',
+        created int NOT NULL default '0',
+        headers text,
+        serialized int(1) NOT NULL default '0',
+        PRIMARY KEY (cid),
+        INDEX expire (expire)
+      ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
+      break;
+  }
+
+  return $ret;
+}
+
+/**
  * @} End of "defgroup updates-5.x-to-6.x"
  * The next series of updates should start at 7000.
  */
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.471
diff -u -p -r1.471 system.module
--- modules/system/system.module	24 Apr 2007 19:49:01 -0000	1.471
+++ modules/system/system.module	28 Apr 2007 19:34:25 -0000
@@ -99,14 +99,14 @@ function system_elements() {
   $type['button'] = array('#input' => TRUE, '#name' => 'op', '#button_type' => 'submit', '#executes_submit_callback' => FALSE);
   $type['textfield'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128, '#autocomplete_path' => FALSE);
   $type['password'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128);
-  $type['password_confirm'] = array('#input' => TRUE, '#process' => array('expand_password_confirm' => array()));
+  $type['password_confirm'] = array('#input' => TRUE, '#process' => array('expand_password_confirm'));
   $type['textarea'] = array('#input' => TRUE, '#cols' => 60, '#rows' => 5, '#resizable' => TRUE);
-  $type['radios'] = array('#input' => TRUE, '#process' => array('expand_radios' => array()));
+  $type['radios'] = array('#input' => TRUE, '#process' => array('expand_radios'));
   $type['radio'] = array('#input' => TRUE, '#default_value' => NULL);
-  $type['checkboxes'] = array('#input' => TRUE, '#process' => array('expand_checkboxes' => array()), '#tree' => TRUE);
+  $type['checkboxes'] = array('#input' => TRUE, '#process' => array('expand_checkboxes'), '#tree' => TRUE);
   $type['select'] = array('#input' => TRUE, '#size' => 0, '#multiple' => FALSE);
-  $type['weight'] = array('#input' => TRUE, '#delta' => 10, '#default_value' => 0, '#process' => array('process_weight' => array()));
-  $type['date'] = array('#input' => TRUE, '#process' => array('expand_date' => array()), '#validate' => array('date_validate' => array()));
+  $type['weight'] = array('#input' => TRUE, '#delta' => 10, '#default_value' => 0, '#process' => array('process_weight'));
+  $type['date'] = array('#input' => TRUE, '#process' => array('expand_date' => array()), '#element_validate' => array('date_validate'));
   $type['file'] = array('#input' => TRUE, '#size' => 60);
 
   // Form structure
@@ -468,13 +468,13 @@ function system_admin_theme_settings() {
   // In order to give it our own submit, we have to give it the default submit
   // too because the presence of a #submit will prevent the default #submit
   // from being used. Also we want ours first.
-  $form['#submit']['system_admin_theme_submit'] = array();
-  $form['#submit']['system_settings_form_submit'] = array();
+  $form['#submit'][] = 'system_admin_theme_submit';
+  $form['#submit'][] = 'system_settings_form_submit';
   return system_settings_form($form);
 }
 
 
-function system_admin_theme_submit($form_id, $form_values) {
+function system_admin_theme_submit($form_values, $form, &$form_state) {
   // If we're changing themes, make sure the theme has its blocks initialized.
   if ($form_values['admin_theme'] != variable_get('admin_theme', '0')) {
     $result = db_query("SELECT status FROM {blocks} WHERE theme = '%s'", $form_values['admin_theme']);
@@ -732,8 +732,8 @@ function system_performance_settings() {
     '#description' => t("Some Drupal modules include their own CSS files. When these modules are enabled, each module's CSS file adds an additional HTTP request to the page, which can increase the load time of each page. These HTTP requests can also slightly increase server load. It is recommended to only turn this option on when your site is in production, as it can interfere with theme development. This option is disabled if you have not set up your files directory, or if your download method is set to private."),
   );
 
-  $form['#submit']['system_settings_form_submit'] = array();
-  $form['#submit']['drupal_clear_css_cache'] = array();
+  $form['#submit'][] = 'system_settings_form_submit';
+  $form['#submit'][] = 'drupal_clear_css_cache';
 
   return system_settings_form($form);
 }
@@ -1114,13 +1114,13 @@ function system_settings_form($form) {
   if (!empty($_POST) && form_get_errors()) {
     drupal_set_message(t('The settings have not been saved because of the errors.'), 'error');
   }
-  $form['#submit']['system_settings_form_submit'] = array();
-  $form['#validate']['system_settings_form_validate'] = array();
+  $form['#submit'][] = 'system_settings_form_submit';
+  $form['#validate'][] = 'system_settings_form_validate';
   $form['#theme'] = 'system_settings_form';
   return $form;
 }
 
-function system_theme_settings_submit($form_id, $form_values) {
+function system_theme_settings_submit($form_values, $form, &$form_state) {
   $op = isset($_POST['op']) ? $_POST['op'] : '';
   $key = $form_values['var'];
 
@@ -1146,7 +1146,7 @@ function system_theme_settings_submit($f
  * add an array_filter value to your form.
  *
  */
-function system_settings_form_submit($form_id, $form_values) {
+function system_settings_form_submit($form_values, $form, &$form_state) {
   $op = isset($form_values['op']) ? $form_values['op'] : '';
 
   // Exclude unnecessary elements.
@@ -1243,7 +1243,7 @@ function theme_system_themes_form($form)
 }
 
 
-function system_themes_form_submit($form_id, $form_values) {
+function system_themes_form_submit($form_values, $form, &$form_state) {
 
   db_query("UPDATE {system} SET status = 0 WHERE type = 'theme'");
 
@@ -1274,7 +1274,8 @@ function system_themes_form_submit($form
   list_themes(TRUE);
   menu_rebuild();
   drupal_set_message(t('The configuration options have been saved.'));
-  return 'admin/build/themes';
+  $form_state['redirect'] = 'admin/build/themes';
+  return;
 }
 
 /**
@@ -1292,12 +1293,13 @@ function system_themes_form_submit($form
  * @return
  *   The form array.
  */
-function system_modules($form_values = NULL) {
+function system_modules($dependencies = array()) {
   // Get current list of modules.
   $files = module_rebuild_cache();
-  if ($confirm_form = system_modules_confirm_form($files, $form_values)) {
-    return $confirm_form;
+  if ($dependencies) {
+    return system_modules_confirm_form($files, $dependencies);
   }
+  $dependencies = array();
 
   // Store module list for validation callback.
   $form['validation_modules'] = array('#type' => 'value', '#value' => $files);
@@ -1387,9 +1389,10 @@ function system_modules($form_values = N
     '#default_value' => $status,
     '#options' => $options,
     '#process' => array(
-      'expand_checkboxes' => array(),
-      'system_modules_disable' => array($disabled),
+      'expand_checkboxes',
+      'system_modules_disable',
     ),
+    '#disabled_modules' => $disabled,
   );
 
   // Handle throttle checkboxes, including overriding the
@@ -1400,9 +1403,10 @@ function system_modules($form_values = N
       '#default_value' => $throttle,
       '#options' => $options,
       '#process' => array(
-        'expand_checkboxes' => array(),
-        'system_modules_disable' => array(array_merge($modules_required, array('throttle'))),
+        'expand_checkboxes',
+        'system_modules_disable',
       ),
+      '#disabled_modules' => array_merge($modules_required, array('throttle')),
     );
   }
 
@@ -1410,7 +1414,6 @@ function system_modules($form_values = N
     '#type' => 'submit',
     '#value' => t('Save configuration'),
   );
-  $form['#multistep'] = TRUE;
   $form['#action'] = url('admin/build/modules/list/confirm');
 
   return $form;
@@ -1419,43 +1422,33 @@ function system_modules($form_values = N
 /**
  * Form process callback function to disable check boxes.
  */
-function system_modules_disable($form, $edit, $disabled) {
-  foreach ($disabled as $key) {
+function system_modules_disable($form, $edit) {
+  foreach ($form['#disabled_modules'] as $key) {
     $form[$key]['#attributes']['disabled'] = 'disabled';
   }
   return $form;
 }
 
-function system_modules_confirm_form($modules, $form_values = array()) {
+function system_modules_confirm_form($modules, $dependencies) {
   $form = array();
   $items = array();
 
-  // Check values for submitted dependency errors.
-  if ($dependencies = system_module_build_dependencies($modules, $form_values)) {
-    // preserve the already switched on modules
-    foreach ($modules as $name => $module) {
-      if ($module->status) {
-        $form['status'][$name] = array('#type' => 'hidden', '#value' => 1);
-      }
-    }
-
-    $form['validation_modules'] = array('#type' => 'value', '#value' => $modules);
-    $form['status']['#tree'] = TRUE;
-    foreach ($dependencies as $name => $missing_dependencies) {
-      $form['status'][$name] = array('#type' => 'hidden', '#value' => 1);
-      foreach ($missing_dependencies as $k => $dependency) {
-        $form['status'][$dependency] = array('#type' => 'hidden', '#value' => 1);
-        $info = $modules[$dependency]->info;
-        $missing_dependencies[$k] = $info['name'] ? $info['name'] : drupal_ucfirst($dependency);
-      }
-      $t_argument = array(
-        '@module' => $modules[$name]->info['name'],
-        '@dependencies' => implode(', ', $missing_dependencies),
-      );
-      $items[] = format_plural(count($missing_dependencies), 'You must enable the @dependencies module to install @module.', 'You must enable the @dependencies modules to install @module.', $t_argument);
-    }
-    $form['text'] = array('#value' => theme('item_list', $items));
+  $form['validation_modules'] = array('#type' => 'value', '#value' => $modules);
+  $form['status']['#tree'] = TRUE;
+  foreach ($dependencies as $name => $missing_dependencies) {
+    $form['status'][$name] = array('#type' => 'hidden', '#value' => 1);
+    foreach ($missing_dependencies as $k => $dependency) {
+      $form['status'][$dependency] = array('#type' => 'hidden', '#value' => 1);
+      $info = $modules[$dependency]->info;
+      $missing_dependencies[$k] = $info['name'] ? $info['name'] : drupal_ucfirst($dependency);
+    }
+    $t_argument = array(
+      '@module' => $modules[$name]->info['name'],
+      '@dependencies' => implode(', ', $missing_dependencies),
+    );
+    $items[] = format_plural(count($missing_dependencies), 'You must enable the @dependencies module to install @module.', 'You must enable the @dependencies modules to install @module.', $t_argument);
   }
+  $form['text'] = array('#value' => theme('item_list', $items));
 
   if ($form) {
     // Set some default form values
@@ -1495,22 +1488,28 @@ function system_module_build_dependencie
 /**
  * Submit callback; handles modules form submission.
  */
-function system_modules_submit($form_id, $form_values) {
+function system_modules_submit($form_values, $form, &$form_state) {
   include_once './includes/install.inc';
   $new_modules = array();
 
-  // Merge in disabled active modules since they should be enabled.
-  // They don't appear because disabled checkboxes are not submitted
-  // by browsers.
-  $form_values['status'] = array_merge($form_values['status'], $form_values['disabled_modules']);
-
-  // Check values for dependency that we can't install.
-  if ($dependencies = system_module_build_dependencies($form_values['validation_modules'], $form_values)) {
-    // These are the modules that depend on existing modules.
-    foreach (array_keys($dependencies) as $name) {
-      $form_values['status'][$name] = 0;
+  // If we are coming from the confirm form...
+  if (!isset($form_state['storage'])) {
+    // Merge in disabled active modules since they should be enabled.
+    // They don't appear because disabled checkboxes are not submitted
+    // by browsers.
+    $form_values['status'] = array_merge($form_values['status'], $form_values['disabled_modules']);
+
+    // Check values for dependency that we can't install.
+    if ($dependencies = system_module_build_dependencies($form_values['validation_modules'], $form_values)) {
+      // These are the modules that depend on existing modules.
+      foreach (array_keys($dependencies) as $name) {
+        $form_values['status'][$name] = 0;
+      }
     }
   }
+  else {
+    $dependencies = NULL;
+  }
 
   $enable_modules = array();
   $disable_modules = array();
@@ -1562,12 +1561,16 @@ function system_modules_submit($form_id,
 
   // If there where unmet dependencies and they haven't confirmed don't redirect.
   if ($dependencies && !isset($form_values['confirm'])) {
-    return FALSE;
+    $form_state['storage'] = $dependencies;
+    return;
   }
 
   drupal_clear_css_cache();
 
-  return 'admin/build/modules';
+  // Unset storage to indicate this form cycle is over.
+  unset($form_state['storage']);
+  $form_state['redirect'] = 'admin/build/modules';
+  return;
 }
 
 /**
@@ -1774,7 +1777,7 @@ function theme_system_modules_uninstall(
  * @param
  *   $form_values Submitted form values.
  */
-function system_modules_uninstall_validate($form_id, $form_values) {
+function system_modules_uninstall_validate($form_values, $form, &$form_state) {
   // Form submitted, but no modules selected.
   if (!count(array_filter($form_values['uninstall']))) {
     drupal_set_message(t('No modules selected.'), 'error');
@@ -1790,7 +1793,7 @@ function system_modules_uninstall_valida
  * @param
  *   $form_values Submitted form values.
  */
-function system_modules_uninstall_submit($form_id, $form_values) {
+function system_modules_uninstall_submit($form_values, $form, &$form_state) {
   // Make sure the install API is available.
   include_once './includes/install.inc';
 
Index: modules/taxonomy/taxonomy.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v
retrieving revision 1.352
diff -u -p -r1.352 taxonomy.module
--- modules/taxonomy/taxonomy.module	24 Apr 2007 18:23:48 -0000	1.352
+++ modules/taxonomy/taxonomy.module	28 Apr 2007 19:34:25 -0000
@@ -304,7 +304,7 @@ function taxonomy_form_vocabulary($edit 
 /**
  * Accept the form submission for a vocabulary and save the results.
  */
-function taxonomy_form_vocabulary_submit($form_id, $form_values) {
+function taxonomy_form_vocabulary_submit($form_values, $form, &$form_state) {
   // Fix up the nodes array to remove unchecked nodes.
   $form_values['nodes'] = array_filter($form_values['nodes']);
   switch (taxonomy_save_vocabulary($form_values)) {
@@ -318,7 +318,8 @@ function taxonomy_form_vocabulary_submit
       break;
   }
 
-  return 'admin/content/taxonomy';
+  $form_state['redirect'] = 'admin/content/taxonomy';
+  return;
 }
 
 function taxonomy_save_vocabulary(&$edit) {
@@ -391,11 +392,12 @@ function taxonomy_vocabulary_confirm_del
                   t('Cancel'));
 }
 
-function taxonomy_vocabulary_confirm_delete_submit($form_id, $form_values) {
+function taxonomy_vocabulary_confirm_delete_submit($form_values, $form, &$form_state) {
   $status = taxonomy_del_vocabulary($form_values['vid']);
   drupal_set_message(t('Deleted vocabulary %name.', array('%name' => $form_values['name'])));
   watchdog('taxonomy', 'Deleted vocabulary %name.', array('%name' => $form_values['name']), WATCHDOG_NOTICE);
-  return 'admin/content/taxonomy';
+  $form_state['redirect'] = 'admin/content/taxonomy';
+  return;
 }
 
 function taxonomy_form_term($vocabulary, $edit = array()) {
@@ -476,7 +478,7 @@ function taxonomy_form_term($vocabulary,
 /**
  * Accept the form submission for a taxonomy term and save the result.
  */
-function taxonomy_form_term_submit($form_id, $form_values) {
+function taxonomy_form_term_submit($form_values, $form, &$form_state) {
   switch (taxonomy_save_term($form_values)) {
     case SAVED_NEW:
       drupal_set_message(t('Created new term %term.', array('%term' => $form_values['name'])));
@@ -487,7 +489,8 @@ function taxonomy_form_term_submit($form
       watchdog('taxonomy', 'Updated term %term.', array('%term' => $form_values['name']), WATCHDOG_NOTICE, l(t('edit'), 'admin/content/taxonomy/edit/term/'. $form_values['tid']));
       break;
   }
-  return 'admin/content/taxonomy';
+  $form_state['redirect'] = 'admin/content/taxonomy';
+  return;
 }
 
 /**
@@ -623,11 +626,12 @@ function taxonomy_term_confirm_delete($t
                   t('Cancel'));
 }
 
-function taxonomy_term_confirm_delete_submit($form_id, $form_values) {
+function taxonomy_term_confirm_delete_submit($form_values, $form, &$form_state) {
   taxonomy_del_term($form_values['tid']);
   drupal_set_message(t('Deleted term %name.', array('%name' => $form_values['name'])));
   watchdog('taxonomy', 'Deleted term %name.', array('%name' => $form_values['name']), WATCHDOG_NOTICE);
-  return 'admin/content/taxonomy';
+  $form_state['redirect'] = 'admin/content/taxonomy';
+  return;
 }
 
 /**
Index: modules/throttle/throttle.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/throttle/throttle.module,v
retrieving revision 1.74
diff -u -p -r1.74 throttle.module
--- modules/throttle/throttle.module	24 Apr 2007 13:53:15 -0000	1.74
+++ modules/throttle/throttle.module	28 Apr 2007 19:34:25 -0000
@@ -156,7 +156,7 @@ function throttle_admin_settings() {
   return system_settings_form($form);
 }
 
-function throttle_admin_settings_validate($form_id, $form_values) {
+function throttle_admin_settings_validate($form_values, $form, &$form_state) {
   if (!is_numeric($form_values['throttle_anonymous']) || $form_values['throttle_anonymous'] < 0) {
     form_set_error('throttle_anonymous', t("%value is not a valid auto-throttle setting. Please enter a positive numeric value.", array('%value' => $form_values['throttle_anonymous'])));
   }
Index: modules/upload/upload.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/upload/upload.module,v
retrieving revision 1.158
diff -u -p -r1.158 upload.module
--- modules/upload/upload.module	13 Apr 2007 08:56:59 -0000	1.158
+++ modules/upload/upload.module	28 Apr 2007 19:34:25 -0000
@@ -119,7 +119,7 @@ function upload_init() {
 /**
  * Form API callback to validate the upload settings form.
  */
-function upload_admin_settings_validate($form_id, $form_values) {
+function upload_admin_settings_validate($form_values, $form, &$form_state) {
   if (($form_values['upload_max_resolution'] != '0')) {
     if (!preg_match('/^[0-9]+x[0-9]+$/', $form_values['upload_max_resolution'])) {
       form_set_error('upload_max_resolution', t('The maximum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Set to 0 for no restriction.'));
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.775
diff -u -p -r1.775 user.module
--- modules/user/user.module	24 Apr 2007 13:53:15 -0000	1.775
+++ modules/user/user.module	28 Apr 2007 19:34:26 -0000
@@ -522,8 +522,8 @@ function user_login_block() {
   $form = array(
     '#action' => url($_GET['q'], array('query' => drupal_get_destination())),
     '#id' => 'user-login-form',
-    '#validate' => array('user_login_validate' => array()),
-    '#submit' => array('user_login_submit' => array()),
+    '#validate' => array('user_login_validate'),
+    '#submit' => array('user_login_submit'),
   );
   $form['name'] = array('#type' => 'textfield',
     '#title' => t('Username'),
@@ -1055,7 +1055,7 @@ function user_login($msg = '') {
   return $form;
 }
 
-function user_login_validate($form_id, $form_values) {
+function user_login_validate($form_values, $form, &$form_state) {
   if ($form_values['name']) {
     if (user_is_blocked($form_values['name'])) {
       // blocked in user administration
@@ -1076,7 +1076,7 @@ function user_login_validate($form_id, $
   }
 }
 
-function user_login_submit($form_id, $form_values) {
+function user_login_submit($form_values, $form, &$form_state) {
   global $user;
   if ($user->uid) {
     watchdog('user', 'Session opened for %name.', array('%name' => $user->name));
@@ -1087,7 +1087,8 @@ function user_login_submit($form_id, $fo
     user_module_invoke('login', $form_values, $user);
 
     sess_regenerate();
-    return 'user/'. $user->uid;
+    $form_state['redirect'] = 'user/'. $user->uid;
+    return;
   }
 }
 
@@ -1170,7 +1171,7 @@ function user_pass() {
   return $form;
 }
 
-function user_pass_validate($form_id, $form_values) {
+function user_pass_validate($form_values, $form, &$form_state) {
   $name = trim($form_values['name']);
   if (valid_email_address($name)) {
     $account = user_load(array('mail' => $name, 'status' => 1));
@@ -1186,7 +1187,7 @@ function user_pass_validate($form_id, $f
   }
 }
 
-function user_pass_submit($form_id, $form_values) {
+function user_pass_submit($form_values, $form, &$form_state) {
   global $base_url;
 
   $account = $form_values['account'];
@@ -1206,7 +1207,8 @@ function user_pass_submit($form_id, $for
     watchdog('user', 'Error mailing password reset instructions to %name at %email.', array('%name' => $account->name, '%email' => $account->mail), WATCHDOG_ERROR);
     drupal_set_message(t('Unable to send mail. Please contact the site admin.'));
   }
-  return 'user';
+  $form_state['redirect'] = 'user';
+  return;
 }
 
 /**
@@ -1329,11 +1331,11 @@ function user_register() {
   return $form;
 }
 
-function user_register_validate($form_id, $form_values) {
+function user_register_validate($form_values, $form, &$form_state) {
   user_module_invoke('validate', $form_values, $form_values, 'account');
 }
 
-function user_register_submit($form_id, $form_values) {
+function user_register_submit($form_values, $form, &$form_state) {
   global $base_url;
   $admin = user_access('administer users');
 
@@ -1356,7 +1358,8 @@ function user_register_submit($form_id, 
 
   if (!$admin && array_intersect(array_keys($form_values), array('uid', 'roles', 'init', 'session', 'status'))) {
     watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
-    return 'user/register';
+    $form_state['redirect'] = 'user/register';
+    return;
   }
   //the unset below is needed to prevent these form values from being saved as user data
   unset($form_values['form_token'], $form_values['submit'], $form_values['op'], $form_values['notify'], $form_values['form_id'], $form_values['affiliates'], $form_values['destination']);
@@ -1377,7 +1380,8 @@ function user_register_submit($form_id, 
     drupal_set_message(t('<p>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 <a href="@settings">site information settings page</a>.</p><p> Your password is <strong>%pass</strong>. You may change your password below.</p>', 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;
 }
 
 
