? .DS_Store
? form.kpf
? form.patch
? sites/form
? sites/default/settings.php
Index: install.php
===================================================================
RCS file: /cvs/drupal/drupal/install.php,v
retrieving revision 1.47
diff -u -F^f -r1.47 install.php
--- install.php	11 May 2007 17:25:14 -0000	1.47
+++ install.php	12 May 2007 04:30:34 -0000
@@ -15,9 +15,17 @@
  *   The installation phase we should proceed to.
  */
 function install_main() {
-  global $profile, $install_locale;
+  global $profile, $install_locale, $conf;
+  require_once './includes/cache-install.inc';
   require_once './includes/bootstrap.inc';
   drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
+
+  // Because no persistent storage is available yet, functions
+  // that check for cached data will fail. During the installation
+  // process, we temporarily replace the normal cache system with
+  // a stubbed-out version that short-circuits the actual caching
+  // process and avoids any errors.
+  $conf['cache_inc'] = './includes/cache-install.inc';
   require_once './modules/system/system.install';
   require_once './includes/file.inc';
 
@@ -135,7 +143,8 @@ function install_verify_settings() {
     $db_path = ltrim(urldecode($url['path']), '/');
     $settings_file = './'. conf_path() .'/settings.php';
 
-    _install_settings_form_validate($db_prefix, $db_type, $db_user, $db_pass, $db_host, $db_port, $db_path, $settings_file);
+    $form_state = array();
+    _install_settings_form_validate($db_prefix, $db_type, $db_user, $db_pass, $db_host, $db_port, $db_path, $settings_file, $form_state);
     if (!form_get_errors()) {
       return TRUE;
     }
@@ -318,22 +327,23 @@ function install_settings_form($profile,
     $form['settings_file'] = array('#type' => 'value', '#value' => $settings_file);
     $form['_db_url'] = array('#type' => 'value');
     $form['#action'] = "install.php?profile=$profile" . ($install_locale ? "&locale=$install_locale" : '');
-    $form['#redirect'] = NULL;
+    $form['#redirect'] = FALSE;
   }
   return $form;
 }
+
 /**
  * Form API validate for install_settings form.
  */
-function install_settings_form_validate($form_id, $form_values, $form) {
+function install_settings_form_validate($form_values, $form, &$form_state) {
   global $db_url;
-  _install_settings_form_validate($form_values['db_prefix'], $form_values['db_type'], $form_values['db_user'], $form_values['db_pass'], $form_values['db_host'], $form_values['db_port'], $form_values['db_path'], $form_values['settings_file'], $form);
+  _install_settings_form_validate($form_values['db_prefix'], $form_values['db_type'], $form_values['db_user'], $form_values['db_pass'], $form_values['db_host'], $form_values['db_port'], $form_values['db_path'], $form_values['settings_file'], $form_state, $form);
 }
 
 /**
  * Helper function for install_settings_validate.
  */
-function _install_settings_form_validate($db_prefix, $db_type, $db_user, $db_pass, $db_host, $db_port, $db_path, $settings_file, $form = NULL) {
+function _install_settings_form_validate($db_prefix, $db_type, $db_user, $db_pass, $db_host, $db_port, $db_path, $settings_file, &$form_state, $form = NULL) {
   global $db_url;
 
   // Verify the table prefix
@@ -358,7 +368,7 @@ function _install_settings_form_validate
     // Verify
     $db_url = $db_type .'://'. urlencode($db_user) . ($db_pass ? ':'. urlencode($db_pass) : '') .'@'. ($db_host ? urlencode($db_host) : 'localhost') . ($db_port ? ":$db_port" : '') .'/'. urlencode($db_path);
     if (isset($form)) {
-      form_set_value($form['_db_url'], $db_url);
+      form_set_value($form['_db_url'], $db_url, $form_state);
     }
     $success = array();
 
@@ -377,7 +387,7 @@ function _install_settings_form_validate
 /**
  * Form API submit for install_settings form.
  */
-function install_settings_form_submit($form_id, $form_values) {
+function install_settings_form_submit($form_values) {
   global $profile, $install_locale;
 
   // Update global settings array and save
@@ -589,17 +599,27 @@ function install_tasks($profile, $task) 
 
     // We break the form up so we can tell when it's been successfully
     // submitted.
+
+    $form_state = array('storage' => NULL, 'submitted' => FALSE);
+
     $form = drupal_retrieve_form('install_configure_form');
+    $form_build_id = md5(mt_rand());
+    $form['#build_id'] = $form_build_id;
+    drupal_prepare_form('install_configure_form', $form, $form_state);
 
     // In order to find out if the form was successfully submitted or not,
     // we do a little song and dance to set the form to 'programmed' and check
     // to make sure this is really the form being submitted. It'd better be.
-    if ($_POST && $_POST['form_id'] == 'install_configure_form') {
+    if (!empty($_POST) && $_POST['form_id'] == 'install_configure_form') {
       $form['#programmed'] = TRUE;
       $form['#post'] = $_POST;
     }
+    else {
+      $form['#post'] = array();
+    }
 
-    if (!drupal_process_form('install_configure_form', $form)) {
+    drupal_process_form('install_configure_form', $form, $form_state);
+    if (empty($form_state['redirect'])) {
       $output = drupal_render_form('install_configure_form', $form);
       install_task_list('configure');
     }
@@ -832,7 +852,7 @@ function install_configure_form() {
   return $form;
 }
 
-function install_configure_form_validate($form_id, $form_values, $form) {
+function install_configure_form_validate($form_values, $form, &$form_state) {
   if ($error = user_validate_name($form_values['account']['name'])) {
     form_error($form['admin_account']['account']['name'], $error);
   }
@@ -844,13 +864,13 @@ function install_configure_form_validate
   }
 }
 
-function install_configure_form_submit($form_id, $form_values) {
+function install_configure_form_submit($form_values, $form, &$form_state) {
   variable_set('site_name', $form_values['site_name']);
   variable_set('site_mail', $form_values['site_mail']);
   variable_set('date_default_timezone', $form_values['date_default_timezone']);
   // Turn this off temporarily so that we can pass a password through.
   variable_set('user_email_verification', FALSE);
-  user_register_submit('user_register', $form_values['account']);
+  user_register_submit($form_values['account'], $form, $form_state);
   variable_set('user_email_verification', TRUE);
   if (isset($form_values['clean_url'])) {
     variable_set('clean_url', $form_values['clean_url']);
Index: includes/batch.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/batch.inc,v
retrieving revision 1.1
diff -u -F^f -r1.1 batch.inc
--- includes/batch.inc	4 May 2007 09:41:36 -0000	1.1
+++ includes/batch.inc	12 May 2007 04:30:34 -0000
@@ -188,6 +188,7 @@ function _batch_process() {
     }
   }
 
+  // TODO : if the last set was a 'form_submit', there is no 'operations', 'total', 'progress message' in $current_set => warnings
   if ($batch['progressive']) {
     $remaining  = count($current_set['operations']);
     $total      = $current_set['total'];
@@ -222,28 +223,18 @@ function &_batch_current_set() {
 
 /**
  * Move execution to the next batch set if any, executing the stored
- * form _submit callbacks along the way (possibly inserting additional batch sets)
+ * form _submit handlers along the way (thus possibly inserting
+ * additional batch sets)
  */
 function _batch_next_set() {
   $batch =& batch_get();
-  if (isset($batch['sets'][$batch['current_set']+1])) {
+  if (isset($batch['sets'][$batch['current_set'] + 1])) {
     $batch['current_set']++;
     $current_set =& _batch_current_set();
-    if (isset($current_set['form submit']) && (list($function, $args) = $current_set['form submit']) && function_exists($function)) {
-      // We have to keep our own copy of $form_values, to account
-      // for possible alteration by the submit callback.
-      if (isset($batch['form_values'])) {
-        $args[1] = $batch['form_values'];
-      }
-      $redirect = call_user_func_array($function, $args);
-      // Store the form_values only if needed, to limit the
-      // amount of data we store in the batch.
-      if (isset($batch['sets'][$batch['current_set']+1])) {
-        $batch['form_values'] = $args[1];
-      }
-      if (isset($redirect)) {
-        $batch['redirect'] = $redirect;
-      }
+    if (isset($current_set['form_submit']) && ($function = $current_set['form_submit']) && function_exists($function)) {
+      // We use our stored copies of $form and $form_state, to account for
+      // possible alteration by the submit handlers.
+      $function($batch['form_state']['values'], $batch['form'], $batch['form_state']);
     }
     return TRUE;
   }
@@ -274,15 +265,27 @@ function _batch_finished() {
     if (isset($_batch['destination'])) {
       $_REQUEST['destination'] = $_batch['destination'];
     }
-    $redirect = isset($_batch['redirect']) ? $_batch['redirect'] : $_batch['source_page'];
-    $form_redirect = isset($_batch['form_redirect']) ? $_batch['form_redirect'] : NULL;
-    // Let drupal_redirect_form handle redirection logic, using a bare pseudo form
-    // to limit the amount of data we store in the batch.
-    drupal_redirect_form(array('#redirect' => $form_redirect), $redirect);
-
-    // If we get here, $form['redirect']['#redirect'] was FALSE, and we are most
-    // probably dealing with a multistep form - not supported at the moment.
-    // Redirect to the originating page - first step of the form.
+
+    // Use $_batch['form_state']['redirect'], or $_batch['redirect'], or $_batch['source_page'].
+    if (isset($_batch['form_state']['redirect'])) {
+      $redirect = $_batch['form_state']['redirect'];
+    }
+    elseif (isset($_batch['redirect'])) {
+      $redirect = $_batch['redirect'];
+    }
+    else {
+      $redirect = $_batch['source_page'];
+    }
+
+    // Let drupal_redirect_form handle redirection logic.
+    $form = isset($batch['form']) ? $batch['form'] : array();
+    if (empty($_batch['form_state']['rebuild']) && empty($_batch['form_state']['storage'])) {
+      drupal_redirect_form($form, $redirect);
+    }
+
+    // We get here if $form['#redirect'] was FALSE, or if
+    // Save the final $form_state value, Redirect to the originating page.
+    $_SESSION['batch_form_state'] = $_batch['form_state'];
     drupal_goto($_batch['source_page']);
   }
 }
Index: includes/cache-install.inc
===================================================================
RCS file: includes/cache-install.inc
diff -N includes/cache-install.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/cache-install.inc	12 May 2007 04:30:34 -0000
@@ -0,0 +1,23 @@
+<?php
+// $Id$
+/**
+ * A stub cache implementation to be used during the installation
+ * process when database access is not yet available. Because Drupal's
+ * caching system never requires that cached data be present, these
+ * stub functions can short-circuit the process and sidestep the
+ * need for any persistent storage. Obviously, using this cache
+ * implementation during normal operations would have a negative impact
+ * on performance.
+ */
+
+function cache_get($key, $table = 'cache') {
+  return FALSE;
+}
+
+function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = NULL) {
+  return;
+}
+
+function cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE) {
+  return;
+}
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.194
diff -u -F^f -r1.194 form.inc
--- includes/form.inc	7 May 2007 10:15:57 -0000	1.194
+++ includes/form.inc	12 May 2007 04:30:35 -0000
@@ -19,7 +19,6 @@
  * Forms can also be built and submitted programmatically without any user input
  * using the drupal_execute() function.
  *
- *
  * For information on the format of the structured arrays used to define forms,
  * and more detailed explanations of the Form API workflow, see the
  * @link http://api.drupal.org/api/HEAD/file/developer/topics/forms_api_reference.html reference @endlink
@@ -27,95 +26,118 @@
  */
 
 /**
- * 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'];
+  $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  $expire = max(ini_get('session.cookie_lifetime'), 86400);
+
+  $args = func_get_args();
+
+  if (isset($_SESSION['batch_form_state'])) {
+    // We've been redirected here after a batch processing : the form has
+    // already been processed, so we grab the post-process $form_state value
+    // and move on to form display. See _batch_finished() function.
+    $form_state = $_SESSION['batch_form_state'];
+    unset($_SESSION['batch_form_state']);
   }
   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;
+    // 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;
+        }
+      }
     }
-    $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);
+    // 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.
+    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);
+      }
     }
-    drupal_prepare_form($args[0], $form);
-  }
+    $form['#post'] = $_POST;
 
-  return drupal_render_form($args[0], $form);
-}
+    // 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 be finished by 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 has 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;
+    $form = call_user_func_array('drupal_retrieve_form', $args);
 
-/**
- * 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]);
-      }
+    // We need a new build_id for the 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,
+ * Retrieves a form using a form_id, populates it with $form_state['values'],
  * processes it, and returns any validation errors encountered. This
  * function is the programmatic counterpart to drupal_get_form().
  *
@@ -124,43 +146,47 @@ 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. Most
+ *   important is the $form_state['values'] collection, a tree of data
+ *   used to simulate the incoming $_POST information from a user's
+ *   form 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
- * $values['name'] = 'robo-user';
- * $values['mail'] = 'robouser@example.com';
- * $values['pass'] = 'password';
- * drupal_execute('user_register', $values);
+ * $form_state = array();
+ * $form_state['values']['name'] = 'robo-user';
+ * $form_state['values']['mail'] = 'robouser@example.com';
+ * $form_state['values']['pass'] = 'password';
+ * drupal_execute('user_register', $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);
+ * $form_state['values']['title'] = 'My node';
+ * $form_state['values']['body'] = 'This is the body text!';
+ * $form_state['values']['name'] = 'robo-user';
+ * drupal_execute('story_node_form', $form_state, $node);
  */
-function drupal_execute($form_id, $form_values) {
+function drupal_execute($form_id, &$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 second parameter.
+  $args = array_slice($args, 3);
   array_unshift($args, $form_id);
+  $args[] = $form_state;
 
-  if (isset($form_values)) {
-    $form = call_user_func_array('drupal_retrieve_form', $args);
-    $form['#post'] = $form_values;
-    return drupal_process_form($form_id, $form);
-  }
+  $form = call_user_func_array('drupal_retrieve_form', $args);
+  $form['#post'] = $form_state['values'];
+  drupal_prepare_form($form_id, $form, $form_state);
+  drupal_process_form($form_id, $form, $form_state);
 }
 
 /**
@@ -171,17 +197,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 +216,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
@@ -202,7 +228,7 @@ function drupal_retrieve_form($form_id) 
     // So, we call the hook if $forms isn't yet populated, OR if it doesn't
     // yet have an entry for the requested form_id.
     if (!isset($forms) || !isset($forms[$form_id])) {
-      $forms = module_invoke_all('forms', $saved_args);
+      $forms = module_invoke_all('forms', $form_id, $args);
     }
     $form_definition = $forms[$form_id];
     if (isset($form_definition['callback arguments'])) {
@@ -232,47 +258,59 @@ 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.
- */
-function drupal_process_form($form_id, &$form) {
-  global $form_values, $form_submitted, $user, $form_button_counter;
-  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));
-
-  $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);
+ * @param $form_state
+ *   A keyed array containing the current state of the form. This
+ *   includes the current persistant storage data for the form, and
+ *   any data passed along by earlier steps when displaying a
+ *   multi-step form. Additional information, like the sanitized $_POST
+ *   data, is also accumulated here.
+ */
+function drupal_process_form($form_id, &$form, &$form_state) {
+  $form_state['values'] = array();
+
+  $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 ((!empty($form_state['submitted'])) && !form_get_errors() && empty($form_state['rebuild'])) {
+      $form_state['redirect'] = NULL;
+      form_execute_handlers('submit', $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) {
+        cache_clear_all('form_'. $form_state['values']['form_build_id'], 'cache_form');
+        cache_clear_all('storage_'. $form_state['values']['form_build_id'], 'cache_form');
+      }
+
+      // If batches were set in the submit handlers, we process them now,
+      // possibly ending execution.
       if ($batch =& batch_get()) {
+        // The batch uses its own copies of $form and $form_state for
+        // late execution of submit handers and post-batch redirection.
+        $batch['form'] = $form;
+        $batch['form_state'] = $form_state;
         $batch['progressive'] = !$form['#programmed'];
         batch_process();
-        // Progressive batch processing redirects to the progress page.
-        // Execution continues only if programmed form.
+        // Execution continues only for programmatic forms.
+        // For 'regular' forms, we get redirected to the batch processing
+        // page. Form redirection will be handled in _batch_finished(),
+        // after the batch is processed.
       }
-      if (!$form['#programmed']) {
-        drupal_redirect_form($form, $redirect);
+
+      // 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;
-  }
 }
 
 /**
@@ -285,25 +323,16 @@ 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
+ *   A keyed array containing the current state of the form. 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',
@@ -334,9 +363,12 @@ 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);
@@ -346,25 +378,24 @@ 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_id, $form, $form_state);
+  drupal_alter('form', $form, $form_id, $form_state);
 }
 
 
 /**
- * Validates user-submitted form data from a global variable using
+ * Validates user-submitted form data from the $form_state using
  * the validate functions defined in a structured form array.
  *
  * @param $form_id
@@ -372,10 +403,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. The current
+ *   user-submitted data is stored in $form_state['values'], though
+ *   form validation functions are passed an explicit copy of the
+ *   values for the sake of simplicity. Validation handlers can also
+ *   $form_state to pass information on to 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) {
-  global $form_values;
+function drupal_validate_form($form_id, $form, &$form_state) {
   static $validated_forms = array();
 
   if (isset($validated_forms[$form_id])) {
@@ -385,80 +424,17 @@ function drupal_validate_form($form_id, 
   // If the session token was set by drupal_prepare_form(), ensure that it
   // matches the current user's session.
   if (isset($form['#token'])) {
-    if (!drupal_valid_token($form_values['form_token'], $form['#token'])) {
+    if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) {
       // Setting this error will cause the form to fail validation.
       form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));
     }
   }
 
-  if (!$form['#programmed'] && !$form['#skip_duplicate_check'] && isset($_SESSION['last_submitted']['hash']) && $_SESSION['last_submitted']['hash'] == md5(serialize($form['form_id']['#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;
 }
 
 /**
- * Processes user-submitted form data from a global variable using
- * the submit functions defined in a structured form array.
- *
- * @param $form_id
- *   A unique string identifying the form for validation, submission,
- *   theming, and hook_form_alter functions.
- * @param $form
- *   An associative array containing the structure of the form.
- * @return
- *   A string containing the path of the page to display when processing
- *   is complete.
- *
- */
-function drupal_submit_form($form_id, $form) {
-  global $form_values;
-  $default_args = array($form_id, &$form_values);
-  $submitted = FALSE;
-  $goto = NULL;
-
-  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.
-        if ($batch =& batch_get()) {
-          // Some previous _submit callback has set a batch.
-          // We store the call in a special 'control' batch set for execution
-          // at the correct time during the batch processing workflow.
-          $batch['sets'][] = array('form submit' => array($function, $args));
-        }
-        else {
-          $redirect = call_user_func_array($function, $args);
-          if ($batch =& batch_get()) {
-            // The _submit callback has opened a batch: store the needed form info.
-            $batch['form_redirect'] = isset($form['#redirect']) ? $form['#redirect'] : NULL;
-          }
-        }
-        $submitted = TRUE;
-        if (isset($redirect)) {
-          $goto = $redirect;
-        }
-      }
-    }
-  }
-  // Successful submit. Hash this form's POST and store the hash in the
-  // session. We'll use this hash later whenever this user submits another
-  // form to make sure no identical forms get submitted twice.
-  if ($submitted && !$form['#skip_duplicate_check']) {
-    $_SESSION['last_submitted'] = array('destination' => $goto, 'hash' => md5(serialize($form['form_id']['#post'])));
-  }
-
-  if (isset($goto)) {
-    return $goto;
-  }
-}
-
-/**
  * Renders a structured form array into themed HTML.
  *
  * @param $form_id
@@ -469,11 +445,9 @@ function drupal_submit_form($form_id, $f
  * @return
  *   A string containing the path of the page to display when processing
  *   is complete.
- *
  */
 function drupal_render_form($form_id, &$form) {
   // Don't override #theme if someone already set it.
-
   if (!isset($form['#theme'])) {
     init_theme();
     $registry = theme_get_registry();
@@ -482,14 +456,6 @@ function drupal_render_form($form_id, &$
     }
   }
 
-  if (isset($form['#pre_render'])) {
-    foreach ($form['#pre_render'] as $function) {
-      if (function_exists($function)) {
-        $function($form_id, $form);
-      }
-    }
-  }
-
   $output = drupal_render($form);
   return $output;
 }
@@ -500,15 +466,15 @@ function drupal_render_form($form_id, &$
  * @param $form
  *   An associative array containing the structure of the form.
  * @param $redirect
- *   An optional string containing the destination path to redirect
+ *   An optional value containing the destination path to redirect
  *   to if none is specified by the form.
- *
  */
 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)) {
@@ -531,15 +497,25 @@ 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. The current
+ *   user-submitted data is stored in $form_state['values'], though
+ *   form validation functions are passed an explicit copy of the
+ *   values for the sake of simplicity. Validation handlers can also
+ *   $form_state to pass information on to 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.
  * @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 */
@@ -571,27 +547,27 @@ function _form_validate($elements, $form
           foreach ($value as $v) {
             if (!isset($options[$v])) {
               form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
-              watchdog('form', 'Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
+              watchdog('form', t('Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'])), WATCHDOG_ERROR);
             }
           }
         }
         elseif (!isset($options[$elements['#value']])) {
           form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
-          watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
+          watchdog('form', t('Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'])), WATCHDOG_ERROR);
         }
       }
     }
 
-    // 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.
+    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);
         }
       }
     }
@@ -600,6 +576,50 @@ function _form_validate($elements, $form
 }
 
 /**
+ * A helper function used to execute custom validation and submission
+ * handlers for a given form. Button-specific handlers are checked
+ * first. If none exist, the function falls back to form-level handlers.
+ *
+ * @param $type
+ *   The type of handler to execute. 'validate' or 'submit' are the
+ *   defaults used by Form API.
+ * @param $form
+ *   An associative array containing the structure of the form.
+ * @param $form_state
+ *   A keyed array containing the current state of the form. If the user
+ *   submitted the form by clicking a button with custom handler functions
+ *   defined, those handlers will be stored here.
+ */
+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))  {
+      if ($type == 'submit' && ($batch =& batch_get())) {
+        // Some previous _submit handler has set a batch. We store the call
+        // in a special 'control' batch set, for execution at the correct
+        // time during the batch processing workflow.
+        $batch['sets'][] = array('form_submit' => array($function));
+      }
+      else {
+        $function($form_state['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
  * foo will set an error for all its children.
@@ -649,20 +669,22 @@ function form_error(&$element, $message 
 }
 
 /**
- * Adds some required properties to each form element, which are used
- * internally in the form API. This function also automatically assigns
- * the value property from the $edit array, provided the element doesn't
- * already have an assigned value.
+ * Walk through the structured form array, adding any required
+ * properties to each element and mapping the incoming $_POST
+ * data to the proper elements.
  *
  * @param $form_id
  *   A unique string identifying the form for validation, submission,
  *   theming, and hook_form_alter functions.
  * @param $form
  *   An associative array containing the structure of the form.
+ * @param $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, as well as the sanitized
+ *   $_POST data.
  */
-function form_builder($form_id, $form) {
-  global $form_values, $form_submitted, $form_button_counter;
-
+function form_builder($form_id, $form, &$form_state) {
   // Initialize as unprocessed.
   $form['#processed'] = FALSE;
 
@@ -673,120 +695,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.
@@ -823,25 +732,187 @@ 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++;
   }
 
+  // The #after_build flag allows any piece of a form to be altered
+  // after normal input parsing has been completed.
   if (isset($form['#after_build']) && !isset($form['#after_build_done'])) {
     foreach ($form['#after_build'] as $function) {
+      $form = $function($form, $form_state['values'], $form_state);
+      $form['#after_build_done'] = TRUE;
+    }
+  }
+
+  // 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;
+}
+
+/**
+ * Populate the #value and #name properties of input elements so they
+ * can be processed and rendered. Also, execute any #process handlers
+ * attached to a specific element.
+ */
+function _form_builder_handle_input_element($form_id, &$form, &$form_state) {
+  if (isset($form['#type']) && $form['#type'] == 'form' && !empty($form['#programed'])) {
+    $form_state['submitted'] = TRUE;
+  }
+
+  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_state['values'][$form['#name']] = $form['#value'];
+
+      if (isset($form['#validate'])) {
+        $form_state['validate_handlers'] = $form['#validate'];
+      }
+      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'], $form_state);
+}
+
+/**
+ * Handle the special Internet Explorer one-button-form hit-enter-
+ * instead-of-clicking scenerio.
+ */
+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'] = empty($button['#submit']) ? NULL : $button['#submit'];
+      $form_state['validate_handlers'] = empty($button['#validate']) ? NULL : $button['#validate'];
+      $form_state['values'][$button['#name']] = $button['#value'];
+    }
+    // After handling the special IE case, we no longer need the buttons collection.
+    unset($form_state['buttons']);
+  }
 }
 
 /**
  * Use this function to make changes to form values in the form validate
- * phase, so they will be available in the submit phase in $form_values.
+ * phase, so they will be available in the submit phase in $form_state.
  *
  * Specifically, if $form['#parents'] is array('foo', 'bar')
  * and $value is 'baz' then this function will make
@@ -852,9 +923,8 @@ function form_builder($form_id, $form) {
  * @param $value
  *   The value for the form item.
  */
-function form_set_value($form, $value) {
-  global $form_values;
-  _form_set_value($form_values, $form, $form['#parents'], $value);
+function form_set_value($form, $value, &$form_state) {
+  _form_set_value($form_state['values'], $form, $form['#parents'], $value);
 }
 
 /**
@@ -875,7 +945,6 @@ function _form_set_value(&$form_values, 
     }
     _form_set_value($form_values[$parent], $form, $parents, $value);
   }
-  return $form;
 }
 
 /**
@@ -1128,14 +1197,14 @@ function expand_password_confirm($elemen
   $element['pass1'] =  array(
     '#type' => 'password',
     '#title' => t('Password'),
-    '#value' => $element['#value']['pass1'],
+    '#value' => empty($element['#value']) ? NULL : $element['#value']['pass1'],
   );
   $element['pass2'] =  array(
     '#type' => 'password',
     '#title' => t('Confirm password'),
-    '#value' => $element['#value']['pass2'],
+    '#value' => empty($element['#value']) ? NULL : $element['#value']['pass2'],
   );
-  $element['#validate'] = array('password_confirm_validate' => array());
+  $element['#element_validate'] = array('password_confirm_validate');
   $element['#tree'] = TRUE;
 
   if (isset($element['#size'])) {
@@ -1148,7 +1217,7 @@ function expand_password_confirm($elemen
 /**
  * Validate password_confirm element.
  */
-function password_confirm_validate($form) {
+function password_confirm_validate($form, &$form_state) {
   $pass1 = trim($form['pass1']['#value']);
   if (!empty($pass1)) {
     $pass2 = trim($form['pass2']['#value']);
@@ -1162,9 +1231,9 @@ function password_confirm_validate($form
 
   // Password field must be converted from a two-element array into a single
   // string regardless of validation results.
-  form_set_value($form['pass1'], NULL);
-  form_set_value($form['pass2'], NULL);
-  form_set_value($form, $pass1);
+  form_set_value($form['pass1'], NULL, $form_state);
+  form_set_value($form['pass2'], NULL, $form_state);
+  form_set_value($form, $pass1, $form_state);
 
   return $form;
 }
@@ -1249,7 +1318,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) {
@@ -1667,7 +1736,7 @@ function form_clean_id($id = NULL) {
  *   'finished' => 'my_finished_callback',
  * );
  * batch_set($batch);
- * // only needed if not inside a form _submit callback :
+ * // only needed if not inside a form _submit handler :
  * batch_process();
  * @endcode
  *
@@ -1804,7 +1873,7 @@ function batch_set($batch_definition) {
     $batch_set['init_message'] .= '<br/>&nbsp;';
     $batch_set['total'] = count($batch_set['operations']);
 
-    // If the batch is being processed (meaning we are executing a stored submit callback),
+    // If the batch is being processed (meaning we are executing a stored submit handler),
     // insert the new set after the current one.
     if (isset($batch['current_set'])) {
       // array_insert does not exist...
@@ -1824,7 +1893,7 @@ function batch_set($batch_definition) {
  * Unless the batch has been markes with 'progressive' = FALSE, the function
  * isses a drupal_goto and thus ends page execution.
  *
- * This function is not needed in form submit callbacks; Form API takes care
+ * This function is not needed in form submit handlers; Form API takes care
  * of batches issued during form submission.
  *
  * @param $redirect
@@ -1834,15 +1903,9 @@ function batch_set($batch_definition) {
  *   URL of the batch processing page.
  */
 function batch_process($redirect = NULL, $url = NULL) {
-  global $form_values, $user;
+  global $user;
   $batch =& batch_get();
 
-  // batch_process should not be called inside form _submit callbacks, or while a
-  // batch is already running. Neutralize the call if it is the case.
-  if (isset($batch['current_set']) || (isset($form_values) && !isset($batch['progressive']))) {
-    return;
-  }
-
   if (isset($batch)) {
     // Add process information
     $t = get_t();
@@ -1868,6 +1931,7 @@ function batch_process($redirect = NULL,
         $batch['destination'] = $_REQUEST['edit']['destination'];
         unset($_REQUEST['edit']['destination']);
       }
+
       db_query("INSERT INTO {batch} (bid, sid, timestamp, batch) VALUES (%d, %d, %d, '%s')", $batch['id'], $user->sid, time(), serialize($batch));
       drupal_goto($batch['url'], 'op=start&id='. $batch['id']);
     }
Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.122
diff -u -F^f -r1.122 locale.inc
--- includes/locale.inc	8 May 2007 09:48:14 -0000	1.122
+++ includes/locale.inc	12 May 2007 04:30:35 -0000
@@ -69,7 +69,7 @@ function theme_locale_languages_overview
 /**
  * Process language overview form submissions, updating existing languages.
  */
-function locale_languages_overview_form_submit($form_id, $form_values) {
+function locale_languages_overview_form_submit($form_values, $form, &$form_state) {
   $languages = language_list();
   $enabled_count = 0;
   foreach ($languages as $langcode => $language) {
@@ -95,7 +95,8 @@ function locale_languages_overview_form_
   // Changing the language settings impacts the interface.
   cache_clear_all('*', 'cache_page', TRUE);
 
-  return 'admin/settings/language';
+  $form_state['redirect'] = 'admin/settings/language';
+  return;
 }
 /**
  * @} End of "locale-language-overview"
@@ -151,8 +152,8 @@ function locale_languages_custom_form() 
     '#value' => t('Add custom language')
   );
   // Reuse the validation and submit functions of the predefined language setup form.
-  $form['#submit']['locale_languages_predefined_form_submit'] = array();
-  $form['#validate']['locale_languages_predefined_form_validate'] = array();
+  $form['#submit'][] = 'locale_languages_predefined_form_submit';
+  $form['#validate'][] = 'locale_languages_predefined_form_validate';
   return $form;
 }
 
@@ -170,8 +171,8 @@ function locale_languages_edit_form($lan
       '#type' => 'submit',
       '#value' => t('Save language')
     );
-    $form['#submit']['locale_languages_edit_form_submit'] = array();
-    $form['#validate']['locale_languages_edit_form_validate'] = array();
+    $form['#submit'][] = 'locale_languages_edit_form_submit';
+    $form['#validate'][] = 'locale_languages_edit_form_validate';
     return $form;
   }
   else {
@@ -252,7 +253,7 @@ function _locale_languages_common_contro
 /**
  * Validate the language addition form.
  */
-function locale_languages_predefined_form_validate($form_id, $form_values) {
+function locale_languages_predefined_form_validate($form_values, $form, &$form_state) {
   $langcode = $form_values['langcode'];
 
   if ($duplicate = db_num_rows(db_query("SELECT language FROM {languages} WHERE language = '%s'", $langcode)) != 0) {
@@ -268,14 +269,14 @@ function locale_languages_predefined_for
   }
   else {
     // Reuse the editing form validation routine if we add a custom language.
-    locale_languages_edit_form_validate($form_id, $form_values);
+    locale_languages_edit_form_validate($form_values, $form, $form_state);
   }
 }
 
 /**
  * Process the language addition form submission.
  */
-function locale_languages_predefined_form_submit($form_id, $form_values) {
+function locale_languages_predefined_form_submit($form_values, $form, &$form_state) {
   $langcode = $form_values['langcode'];
   if (isset($form_values['name'])) {
     // Custom language form.
@@ -288,13 +289,14 @@ function locale_languages_predefined_for
     locale_add_language($langcode, $lang[0], isset($lang[1]) ? $lang[1] : $lang[0], isset($lang[2]) ? $lang[2] : 0, '', $langcode);
   }
 
-  return 'admin/settings/language';
+  $form_state['redirect'] = 'admin/settings/language';
+  return;
 }
 
 /**
  * Validate the language editing form. Reused for custom language addition too.
  */
-function locale_languages_edit_form_validate($form_id, $form_values) {
+function locale_languages_edit_form_validate($form_values, $form, &$form_state) {
   if (!empty($form_values['domain']) && !empty($form_values['prefix'])) {
     form_set_error('prefix', t('Domain and path prefix values should not be set at the same time.'));
   }
@@ -313,7 +315,7 @@ function locale_languages_edit_form_vali
 /**
  * Process the language editing form submission.
  */
-function locale_languages_edit_form_submit($form_id, $form_values) {
+function locale_languages_edit_form_submit($form_values, $form, &$form_state) {
   db_query("UPDATE {languages} SET name = '%s', native = '%s', domain = '%s', prefix = '%s', direction = %d WHERE language = '%s'", $form_values['name'], $form_values['native'], $form_values['domain'], $form_values['prefix'], $form_values['direction'], $form_values['langcode']);
   $default = language_default();
   if ($default->language == $form_values['langcode']) {
@@ -323,7 +325,8 @@ function locale_languages_edit_form_subm
     }
     variable_set('language_default', $default);
   }
-  return 'admin/settings/language';
+  $form_state['redirect'] = 'admin/settings/language';
+  return;
 }
 /**
  * @} End of "locale-language-add-edit"
@@ -366,7 +369,7 @@ function locale_languages_delete_form($l
 /**
  * Process language deletion submissions.
  */
-function locale_languages_delete_form_submit($form_id, $form_values) {
+function locale_languages_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']);
@@ -380,7 +383,8 @@ function locale_languages_delete_form_su
   // Changing the language settings impacts the interface:
   cache_clear_all('*', 'cache_page', TRUE);
 
-  return 'admin/settings/language';
+  $form_state['redirect'] = 'admin/settings/language';
+  return;
 }
 /**
  * @} End of "locale-language-add-edit"
@@ -416,10 +420,11 @@ function locale_languages_configure_form
 /**
  * Submit function for language negotiation settings.
  */
-function locale_languages_configure_form_submit($form_id, $form_values) {
+function locale_languages_configure_form_submit($form_values, $form, &$form_state) {
   variable_set('language_negotiation', $form_values['language_negotiation']);
   drupal_set_message(t('Language negotiation configuration saved.'));
-  return 'admin/settings/language';
+  $form_state['redirect'] = 'admin/settings/language';
+  return;
 }
 /**
  * @} End of "locale-languages-negotiation"
@@ -588,7 +593,7 @@ function locale_translate_import_form() 
 /**
  * Process the locale import form submission.
  */
-function locale_translate_import_form_submit($form_id, $form_values) {
+function locale_translate_import_form_submit($form_values, $form, &$form_state) {
   // Ensure we have the file uploaded
   if ($file = file_check_upload('file')) {
 
@@ -613,7 +618,8 @@ function locale_translate_import_form_su
     return 'admin/build/translate/import';
   }
 
-  return 'admin/build/translate';
+  $form_state['redirect'] = 'admin/build/translate';
+  return;
 }
 /**
  * @} End of "locale-translate-import"
@@ -682,15 +688,15 @@ function locale_translate_export_pot_for
   );
   $form['export']['submit'] = array('#type' => 'submit', '#value' => t('Export'));
   // Reuse PO export submission callback.
-  $form['#submit']['locale_translate_export_po_form_submit'] = array();
-  $form['#validate']['locale_translate_export_po_form_validate'] = array();
+  $form['#submit'][] = 'locale_translate_export_po_form_submit';
+  $form['#validate'][] = 'locale_translate_export_po_form_validate';
   return $form;
 }
 
 /**
  * Process a translation (or template) export form submission.
  */
-function locale_translate_export_po_form_submit($form_id, $form_values) {
+function locale_translate_export_po_form_submit($form_values, $form, &$form_state) {
   // If template is required, language code is not given.
   _locale_export_po(isset($form_values['langcode']) ? $form_values['langcode'] : NULL, $form_values['group']);
 }
@@ -759,7 +765,7 @@ function locale_translate_edit_form($lid
  * Process string editing form submissions.
  * Saves all translations of one string submitted from a form.
  */
-function locale_translate_edit_form_submit($form_id, $form_values) {
+function locale_translate_edit_form_submit($form_values, $form, &$form_state) {
   $lid = $form_values['lid'];
   foreach ($form_values['translations'] as $key => $value) {
     $trans = db_fetch_object(db_query("SELECT translation FROM {locales_target} WHERE lid = %d AND language = '%s'", $lid, $key));
@@ -774,10 +780,9 @@ function locale_translate_edit_form_subm
 
   // Refresh the locale cache.
   locale_refresh_cache();
-  // Rebuild the menu, strings may have changed.
-  menu_rebuild();
 
-  return 'admin/build/translate/search';
+  $form_state['redirect'] = 'admin/build/translate/search';
+  return;
 }
 /**
  * @} End of "locale-translate-edit"
Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.165
diff -u -F^f -r1.165 menu.inc
--- includes/menu.inc	6 May 2007 05:47:51 -0000	1.165
+++ includes/menu.inc	12 May 2007 04:30:35 -0000
@@ -127,18 +127,6 @@
 define('MENU_DEFAULT_LOCAL_TASK', MENU_IS_LOCAL_TASK | MENU_LINKS_TO_PARENT);
 
 /**
- * Custom items are those defined by the administrator. Reserved for internal
- * use; do not return from hook_menu() implementations.
- */
-define('MENU_CUSTOM_ITEM', MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB | MENU_CREATED_BY_ADMIN | MENU_MODIFIABLE_BY_ADMIN);
-
-/**
- * Custom menus are those defined by the administrator. Reserved for internal
- * use; do not return from hook_menu() implementations.
- */
-define('MENU_CUSTOM_MENU', MENU_IS_ROOT | MENU_VISIBLE_IN_TREE | MENU_CREATED_BY_ADMIN | MENU_MODIFIABLE_BY_ADMIN);
-
-/**
  * @} End of "Menu item types".
  */
 
@@ -157,37 +145,31 @@
  * @} End of "Menu status codes".
  */
 
-/**
- * @Name Menu operations
- * @{
- * Menu helper possible operations.
- */
-
-define('MENU_HANDLE_REQUEST', 0);
-define('MENU_RENDER_LINK', 1);
 
 /**
  * @} End of "Menu operations."
  */
 
 /**
- * @Name Menu alterations
+ * @Name Menu tree parameters
  * @{
- * Menu alter phases
+ * Menu tree
  */
 
-/**
- * Alter the menu as defined in modules, keys are like user/%user.
+ /**
+ * The maximum number of path elements for a menu callback
  */
-define('MENU_ALTER_MODULE_DEFINED', 0);
+define('MENU_MAX_PARTS', 6);
+
 
 /**
- * Alter the menu after the first preprocessing phase, keys are like user/%.
+ * The maximum depth of a menu links tree - matches the number of p columns.
  */
-define('MENU_ALTER_PREPROCESSED', 1);
+define('MENU_MAX_DEPTH', 6);
+
 
 /**
- * @} End of "Menu alterations".
+ * @} End of "Menu tree parameters".
  */
 
 /**
@@ -283,50 +265,27 @@ function menu_unserialize($data, $map) {
 }
 
 /**
- * Replaces the statically cached item for a given path.
+ * Get the menu callback for the a path.
  *
  * @param $path
- *   The path
- * @param $item
- *   The menu item. This is a menu entry, an associative array,
- *   with keys like title, access callback, access arguments etc.
+ *   A path, or NULL for the current path
  */
-function menu_set_item($path, $item) {
-  menu_get_item($path, $item);
-}
-
-function menu_get_item($path = NULL, $item = NULL) {
+function menu_get_item($path = NULL) {
   static $items;
   if (!isset($path)) {
     $path = $_GET['q'];
   }
-  if (isset($item)) {
-    $items[$path] = $item;
-  }
   if (!isset($items[$path])) {
     $original_map = arg(NULL, $path);
-    $parts = array_slice($original_map, 0, 6);
+    $parts = array_slice($original_map, 0, MENU_MAX_PARTS);
     list($ancestors, $placeholders) = menu_get_ancestors($parts);
-    $item->active_trail = array();
+
     if ($item = db_fetch_object(db_query_range('SELECT * FROM {menu} WHERE path IN ('. implode (',', $placeholders) .') ORDER BY fit DESC', $ancestors, 0, 1))) {
-      // We need to access check the parents to match the navigation tree
-      // behaviour. The last parent is always the item itself.
-      $args = explode(',', $item->parents);
-      $placeholders = implode(', ', array_fill(0, count($args), '%d'));
-      $result = db_query('SELECT * FROM {menu} WHERE mid IN ('. $placeholders .') ORDER BY mleft', $args);
-      $item->access = TRUE;
-      while ($item->access && ($parent = db_fetch_object($result)))  {
-        $map = _menu_translate($parent, $original_map);
-        if ($map === FALSE) {
-          $items[$path] = FALSE;
-          return FALSE;
-        }
-        if ($parent->access) {
-          $item->active_trail[] = $parent;
-        }
-        else {
-          $item->access = FALSE;
-        }
+
+      $map = _menu_translate($item, $original_map);
+      if ($map === FALSE) {
+        $items[$path] = FALSE;
+        return FALSE;
       }
       if ($item->access) {
         $item->map = $map;
@@ -339,7 +298,7 @@ function menu_get_item($path = NULL, $it
 }
 
 /**
- * Execute the handler associated with the active menu item.
+ * Execute the page callback associated with the current path
  */
 function menu_execute_active_handler() {
   if ($item = menu_get_item()) {
@@ -349,77 +308,48 @@ function menu_execute_active_handler() {
 }
 
 /**
- * Handles dynamic path translation, title and description translation and
- * menu access control.
- *
- * When a user arrives on a page such as node/5, this function determines
- * what "5" corresponds to, by inspecting the page's menu path definition,
- * node/%node. This will call node_load(5) to load the corresponding node
- * object.
- *
- * It also works in reverse, to allow the display of tabs and menu items which
- * contain these dynamic arguments, translating node/%node to node/5.
- * This operation is called MENU_RENDER_LINK.
- *
- * Translation of menu item titles and descriptions are done here to
- * allow for storage of English strings in the database, and be able to
- * generate menus in the language required to generate the current page.
+ * Loads objects into the map as defined in the
+ * $item->load_functions.
  *
  * @param $item
  *   A menu item object
  * @param $map
  *   An array of path arguments (ex: array('node', '5'))
- * @param $operation
- *   The path translation operation to perform:
- *   - MENU_HANDLE_REQUEST: An incoming page reqest; map with appropriate callback.
- *   - MENU_RENDER_LINK: Render an internal path as a link.
  * @return
- *   Returns the map with objects loaded as defined in the
- *   $item->load_functions. Also, $item->link_path becomes the path ready
- *   for printing, aliased. $item->alias becomes TRUE to mark this, so you can
- *   just pass (array)$item to l() as the third parameter.
- *   $item->access becomes TRUE if the item is accessible, FALSE otherwise.
-  */
-function _menu_translate(&$item, $map, $operation = MENU_HANDLE_REQUEST) {
-  // Check if there are dynamic arguments in the path that need to be calculated.
-  // If there are to_arg_functions, then load_functions is also not empty
-  // because it was built so in menu_rebuild. Therefore, it's enough to test
-  // load_functions.
+ *   Returns the TRUE for success, FALSE if an object cannot be loaded
+ */
+function _menu_load_objects($item, &$map) {
   if ($item->load_functions) {
     $load_functions = unserialize($item->load_functions);
-    $to_arg_functions = unserialize($item->to_arg_functions);
-    $path_map = ($operation == MENU_HANDLE_REQUEST) ? $map : explode('/', $item->path);
-    foreach ($load_functions as $index => $load_function) {
-      // Translate place-holders into real values.
-      if ($operation == MENU_RENDER_LINK) {
-        if (isset($to_arg_functions[$index])) {
-          $to_arg_function = $to_arg_functions[$index];
-          $return = $to_arg_function(!empty($map[$index]) ? $map[$index] : '');
-          if (!empty($map[$index]) || isset($return)) {
-            $path_map[$index] = $return;
-          }
-          else {
-            unset($path_map[$index]);
-          }
-        }
-        else {
-          $path_map[$index] = isset($map[$index]) ? $map[$index] : '';
-        }
-      }
-      // We now have a real path regardless of operation, map it.
-      if ($load_function) {
-        $return = $load_function(isset($path_map[$index]) ? $path_map[$index] : '');
+    $path_map = $map;
+    foreach ($load_functions as $index => $function) {
+      if ($function) {
+
+        $return = $function(isset($path_map[$index]) ? $path_map[$index] : '');
         // If callback returned an error or there is no callback, trigger 404.
         if ($return === FALSE) {
           $item->access = FALSE;
+          $map = FALSE;
           return FALSE;
         }
         $map[$index] = $return;
       }
     }
-    // Re-join the path with the new replacement value and alias it.
-    $item->link_path = drupal_get_path_alias(implode('/', $path_map));
   }
+  return TRUE;
+}
+
+/**
+ * Check access to a menu item using the access callback
+ *
+ * @param $item
+ *   A menu item object
+ * @param $map
+ *   An array of path arguments (ex: array('node', '5'))
+ * @return
+ *   $item->access becomes TRUE if the item is accessible, FALSE otherwise.
+ */
+function _menu_check_access(&$item, $map) {
   // Determine access callback, which will decide whether or not the current user has
   // access to this path.
   $callback = $item->access_callback;
@@ -438,11 +368,12 @@ function _menu_translate(&$item, $map, $
       $item->access = call_user_func_array($callback, $arguments);
     }
   }
-  $item->alias = TRUE;
+}
 
+function _menu_item_localize(&$item) {
   // Translate the title to allow storage of English title strings
-  // in the database, yet be able to display them in the language
-  // required to generate the page in.
+  // in the database, yet display of them in the language required
+  // by the current user..
   $callback = $item->title_callback;
   // t() is a special case. Since it is used very close to all the time,
   // we handle it directly instead of using indirect, slower methods.
@@ -467,31 +398,242 @@ function _menu_translate(&$item, $map, $
   if (!empty($item->description)) {
     $item->description = t($item->description);
   }
+}
+
+/**
+ * Handles dynamic path translation and menu access control.
+ *
+ * When a user arrives on a page such as node/5, this function determines
+ * what "5" corresponds to, by inspecting the page's menu path definition,
+ * node/%node. This will call node_load(5) to load the corresponding node
+ * object.
+ *
+ * It also works in reverse, to allow the display of tabs and menu items which
+ * contain these dynamic arguments, translating node/%node to node/5.
+ * This operation is called MENU_RENDER_LINK.
+ *
+ * Translation of menu item titles and descriptions are done here to
+ * allow for storage of English strings in the database, and translation
+ * to the language required to generate the current page
+ *
+ * @param $item
+ *   A menu item object
+ * @param $map
+ *   An array of path arguments (ex: array('node', '5'))
+ * @param $to_arg
+ *   Execute $item->to_arg_functions or not. Use only if you want to render a
+ *   path from the menu table, for example tabs.
+ * @return
+ *   Returns the map with objects loaded as defined in the
+ *   $item->load_functions. $item->access becomes TRUE if the item is
+ *   accessible, FALSE otherwise. $item->href is set according to the map.
+ *   If an error occurs during calling the load_functions (like trying to load
+ *   a non existing node) then this function return FALSE.
+ */
+function _menu_translate(&$item, $map, $to_arg = FALSE) {
+  $path_map = $map;
+  if (!_menu_load_objects($item, $map)) {
+    // An error occured loading an object
+    $item->access = FALSE;
+    return FALSE;
+  }
+  if ($to_arg) {
+    _menu_link_map_translate($path_map, $item->to_arg_functions);
+  }
+
+  // Generate the link path for the page request or local tasks
+  $link_map = explode('/', $item->path);
+  for ($i = 0; $i < $item->number_parts; $i++) {
+    if ($link_map[$i] == '%') {
+      $link_map[$i] = $path_map[$i];
+    }
+  }
+  $item->href = implode('/', $link_map);
+  _menu_check_access($item, $map);
+
+  _menu_item_localize($item);
 
   return $map;
 }
 
 /**
+ * This function translates the objects in the map to current values with
+ * helper functions.
+ *
+ * @param map
+ *   An array of path arguments (ex: array('node', '5'))
+ * @param $to_arg_functions
+ *   An array of helper function (ex: array(1 => 'node_load'))
+ */
+function _menu_link_map_translate(&$map, $to_arg_functions) {
+  if ($to_arg_functions) {
+    $to_arg_functions = unserialize($to_arg_functions);
+    foreach ($to_arg_functions as $index => $function) {
+      // Translate place-holders into real values.
+      $arg = $function(!empty($map[$index]) ? $map[$index] : '');
+      if (!empty($map[$index]) || isset($arg)) {
+        $map[$index] = $arg;
+      }
+      else {
+        unset($map[$index]);
+      }
+    }
+  }
+}
+
+/**
+ * When rendering a menu link, this function is similar to
+ * _menu_translate() but does other link-specific preparation.
+ *
+ * @param $item
+ *   A menu item object
+ * @return
+ *   Returns the map of path arguments with objects loaded as defined in the
+ *   $item->load_functions.
+ *   $item->access becomes TRUE if the item is accessible, FALSE otherwise.
+ *   $item->href is altered if there is a to_arg function.
+ */
+function _menu_link_translate(&$item) {
+  if ($item->external) {
+    $item->access = 1;
+    $map = array();
+  }
+  else {
+    $map = explode('/', $item->href);
+    _menu_link_map_translate($map, $item->to_arg_functions);
+    $item->href = implode('/', $map);
+
+    // Note- skip callbacks without real values for their arguments
+    if (strpos($item->href, '%') !== FALSE) {
+      $item->access = FALSE;
+      return FALSE;
+    }
+    if (!_menu_load_objects($item, $map)) {
+      // An error occured loading an object
+      $item->access = FALSE;
+      return FALSE;
+    }
+    // TODO: menu_tree may set this ahead of time for links to nodes
+    if (!isset($item->access)) {
+      _menu_check_access($item, $map);
+    }
+    // If the link title matches that of a router item, locaize it.
+    if (isset($item->title) && ($item->title == $item->link_title)) {
+      _menu_item_localize($item);
+      $item->link_title = $item->title;
+    }
+  }
+  $item->options = unserialize($item->options);
+
+  return $map;
+}
+
+/**
+ * Returns a rendered menu tree. The tree is expanded based on the current
+ * path and dynamic paths are also changed accordingly.
+ *
+ * @param $menu_name
+ *   The name of the menu.
+ * @return
+ *   The rendered HTML of that menu on the current page.
+ */
+function menu_tree($menu_name = 'navigation') {
+  static $menu_output = array();
+
+  if (!isset($menu_output[$menu_name])) {
+    $tree = menu_tree_data($menu_name);
+    $menu_output[$menu_name] = menu_tree_output($tree);
+  }
+  return $menu_output[$menu_name];
+}
+
+/**
  * Returns a rendered menu tree.
+ *
+ * @param $tree
+ *   A data structure representing the tree as returned from menu_tree_data.
+ * @return
+ *   The rendered HTML of that data structure.
  */
-function menu_tree() {
-  if ($item = menu_get_item()) {
-    if ($item->access) {
-      $args = explode(',', $item->parents);
-      $placeholders = implode(', ', array_fill(0, count($args), '%d'));
+function menu_tree_output($tree) {
+  $output = '';
+
+  foreach ($tree as $data) {
+    if (!$data['link']->hidden) {
+      $link = theme('menu_item_link', $data['link']);
+      if ($data['below']) {
+        $output .= theme('menu_item', $link, $data['link']->has_children, menu_tree_output($data['below']));
+      }
+      else {
+        $output .= theme('menu_item', $link, $data['link']->has_children);
+      }
     }
-    // Show the root menu for access denied.
-    else {
-      $args = 0;
-      $placeholders = '%d';
+  }
+  return $output ? theme('menu_tree', $output) : '';
+}
+
+/**
+ * Get the data structure representing a named menu tree, based on the current
+ * page. The tree order is maintained by storing each parent in an invidual
+ * field, see http://drupal.org/node/141866 for more.
+ *
+ * @param $menu_name
+ *   The named menu links to return
+ * @return
+ *   An array of menu links, in the order they should be rendered. The array
+ *   is a list of associative arrays -- these have two keys, link and below.
+ *   link is a menu item, ready for theming as a link. Below represents the
+ *   submenu below the link if there is one and it is a similar list that was
+ *   described so far.
+ */
+function menu_tree_data($menu_name = 'navigation') {
+  static $tree = array();
+
+  if ($item = menu_get_item()) {
+    if (!isset($tree[$menu_name])) {
+      if ($item->access) {
+        $parents = db_fetch_array(db_query("SELECT p1, p2, p3, p4, p5 FROM {menu_links} WHERE menu_name = '%s' AND href = '%s'", $menu_name, $item->href));
+        // We may be on a local task that's not in the links
+        // TODO how do we hadle the case like a local task on a specific node in the menu?
+        if (empty($parents)) {
+          $parents = db_fetch_array(db_query("SELECT p1, p2, p3, p4, p5 FROM {menu_links} WHERE menu_name = '%s' AND href = '%s'", $menu_name, $item->tab_root));
+        }
+        $parents[] = '0';
+
+        $args = array_unique($parents);
+        $placeholders = implode(', ', array_fill(0, count($args), '%d'));
+        if (variable_get('menu_expanded', 0)) {
+          do {
+            $result = db_query("SELECT mlid FROM {menu_links} ml WHERE expanded = 1 AND ml.menu_name = '%s' AND ml.plid IN (". $placeholders .') AND ml.mlid NOT IN ('. $placeholders .')', array_merge(array($menu_name), $args, $args));
+            while ($items = db_fetch_object($result)) {
+              $args[] = $items->mlid;
+            }
+            $placeholders = implode(', ', array_fill(0, count($args), '%d'));
+          } while (db_num_rows($result));
+        }
+        array_unshift($args, $menu_name);
+      }
+      // Show the root menu for access denied.
+      else {
+        $args = array('navigation', '0');
+        $placeholders = '%d';
+      }
+      list(, $tree[$menu_name]) = _menu_tree_data(db_query("
+        SELECT *, ml.weight + 50000 AS weight FROM {menu_links} ml INNER JOIN {menu} m ON m.path = ml.router_path
+        WHERE ml.menu_name = '%s' AND ml.plid IN (". $placeholders .")
+        ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC", $args));
+
+      // TODO: cache_set() for the untranslated links
+      // TODO: special case node links and access check via db_rewite_sql()
+      // TODO: access check / _menu_link_translate on each here
     }
-    list(, $menu) = _menu_tree(db_query('SELECT * FROM {menu} WHERE pid IN ('. $placeholders .') AND visible = 1 ORDER BY mleft', $args));
-    return $menu;
+    return $tree[$menu_name];
   }
 }
 
+
 /**
- * Renders a menu tree from a database result resource.
+ * Build the data representing a menu tree
  *
  * The function is a bit complex because the rendering of an item depends on
  * the next menu item. So we are always rendering the element previously
@@ -501,71 +643,76 @@ function menu_tree() {
  *   The database result.
  * @param $depth
  *   The depth of the current menu tree.
- * @param $link
- *   The first link in the current menu tree.
- * @param $has_children
- *  Whether the first link has children.
+ * @param $previous_element
+ *   The previous menu link in the current menu tree.
  * @return
- *   A list, the first element is the first item after the submenu, the second
- *   is the rendered HTML of the children.
+ *   See menu_tree_data for a description of the data structure.
  */
-function _menu_tree($result = NULL, $depth = 0, $link = '', $has_children = FALSE) {
-  static $map;
+function _menu_tree_data($result = NULL, $depth = 1, $previous_element = '') {
   $remnant = NULL;
-  $tree = '';
-  // Fetch the current path and cache it.
-  if (!isset($map)) {
-    $map = arg(NULL);
-  }
+  $tree = array();
   while ($item = db_fetch_object($result)) {
     // Access check and handle dynamic path translation.
-    _menu_translate($item, $map, MENU_RENDER_LINK);
+    // TODO - move this to the parent function, so the untranslated link data
+    // can be cached.
+    _menu_link_translate($item);
     if (!$item->access) {
       continue;
     }
-    if ($item->attributes) {
-      $item->attributes = unserialize($item->attributes);
-    }
+    // The weights are uniform 5 digits because of the 50000 offset in the
+    // query. We slap mlid at the end of the index to make very sure that
+    // we never create the same index twice.
+    $index = $previous_element ? ($previous_element->weight .' '. $previous_element->title . $previous_element->mlid) : '';
     // The current item is the first in a new submenu.
     if ($item->depth > $depth) {
-      // _menu_tree returns an item and the HTML of the rendered menu tree.
-      list($item, $menu) = _menu_tree($result, $item->depth, theme('menu_item_link', $item), $item->has_children);
-      // Theme the menu.
-      $menu = $menu ? theme('menu_tree', $menu) : '';
-      // $link is the previous element.
-      $tree .= $link ? theme('menu_item', $link, $has_children, $menu) : $menu;
+      // _menu_tree returns an item and the menu tree structure.
+      list($item, $below) = _menu_tree_data($result, $item->depth, $item);
+      $tree[$index] = array(
+        'link' => $previous_element,
+        'below' => $below,
+        'expanded' => '',
+      );
+      // We need to fail back more than one level.
+      if ($item->depth < $depth) {
+        ksort($tree);
+        return array($item, $tree);
+      }
       // This will be the link to be output in the next iteration.
-      $link = $item ? theme('menu_item_link', $item) : '';
-      $has_children = $item ? $item->has_children : FALSE;
+      $previous_element = $item;
     }
-    // We are in the same menu. We render the previous element.
+    // We are in the same menu. We render the previous element, $previous_element.
     elseif ($item->depth == $depth) {
-      // $link is the previous element.
-      $tree .= theme('menu_item', $link, $has_children);
+      if ($previous_element) { // Only the first time
+        $tree[$index] = array(
+          'link' => $previous_element,
+          'below' => '',
+        );
+      }
       // This will be the link to be output in the next iteration.
-      $link = theme('menu_item_link', $item);
-      $has_children = $item->has_children;
+      $previous_element = $item;
     }
-    // The submenu ended with the previous item, we need to pass back the
-    // current element.
+    // The submenu ended with the previous item, so pass back the current item.
     else {
       $remnant = $item;
       break;
     }
   }
-  if ($link) {
+  if ($previous_element) {
     // We have one more link dangling.
-    $tree .= theme('menu_item', $link, $has_children);
+    $tree[$previous_element->weight .' '. $previous_element->title .' '. $previous_element->mlid] = array(
+      'link' => $previous_element,
+      'below' => '',
+    );
   }
+  ksort($tree);
   return array($remnant, $tree);
 }
 
 /**
  * Generate the HTML output for a single menu link.
  */
-function theme_menu_item_link($item) {
-  $link = (array)$item;
-  return l($link['title'], $link['link_path'], $link);
+function theme_menu_item_link($link) {
+  return l($link->link_title, $link->href, $link->options);
 }
 
 /**
@@ -616,320 +763,31 @@ function menu_get_active_help() {
   return $output;
 }
 
-function menu_path_is_external($path) {
-  $colonpos = strpos($path, ':');
-  return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) == check_plain($path);
-}
-
 /**
- * Populate the database representation of the menu.
+ * Build a list of named menus.
  */
-function menu_rebuild() {
-  // TODO: split menu and menu links storage.
-  $menu = module_invoke_all('menu');
-
-  // Alter the menu as defined in modules, keys are like user/%user.
-  drupal_alter('menu', $menu, MENU_ALTER_MODULE_DEFINED);
-  db_query('DELETE FROM {menu}');
-  $mid = 1;
-
-  // First pass: separate callbacks from pathes, making pathes ready for
-  // matching. Calculate fitness, and fill some default values.
-  foreach ($menu as $path => $item) {
-    $load_functions = array();
-    $to_arg_functions = array();
-    $fit = 0;
-    $move = FALSE;
-    if (!isset($item['_external'])) {
-      $item['_external'] = menu_path_is_external($path);
-    }
-    if ($item['_external']) {
-      $number_parts = 0;
-      $parts = array();
-    }
-    else {
-      $parts = explode('/', $path, 6);
-      $number_parts = count($parts);
-      // We store the highest index of parts here to save some work in the fit
-      // calculation loop.
-      $slashes = $number_parts - 1;
-      // extract functions
-      foreach ($parts as $k => $part) {
-        $match = FALSE;
-        if (preg_match('/^%([a-z_]*)$/', $part, $matches)) {
-          if (empty($matches[1])) {
-            $match = TRUE;
-            $load_functions[$k] = NULL;
-          }
-          else {
-            if (function_exists($matches[1] .'_to_arg')) {
-              $to_arg_functions[$k] = $matches[1] .'_to_arg';
-              $load_functions[$k] = NULL;
-              $match = TRUE;
-            }
-            if (function_exists($matches[1] .'_load')) {
-              $load_functions[$k] = $matches[1] .'_load';
-              $match = TRUE;
-            }
-          }
-        }
-        if ($match) {
-          $parts[$k] = '%';
-        }
-        else {
-          $fit |=  1 << ($slashes - $k);
-        }
-      }
-      if ($fit) {
-        $move = TRUE;
-      }
-      else {
-        // If there is no %, it fits maximally.
-        $fit = (1 << $number_parts) - 1;
-      }
-    }
-    $item['load_functions'] = empty($load_functions) ? '' : serialize($load_functions);
-    $item['to_arg_functions'] = empty($to_arg_functions) ? '' : serialize($to_arg_functions);
-    $item += array(
-      'title' => '',
-      'weight' => 0,
-      'type' => MENU_NORMAL_ITEM,
-      '_number_parts' => $number_parts,
-      '_parts' => $parts,
-      '_fit' => $fit,
-      '_mid' => $mid++,
-      '_children' => array(),
-    );
-    $item += array(
-      '_visible' => (bool)($item['type'] & MENU_VISIBLE_IN_TREE),
-      '_tab' => (bool)($item['type'] & MENU_IS_LOCAL_TASK),
-    );
-    if ($move) {
-      $new_path = implode('/', $item['_parts']);
-      unset($menu[$path]);
-    }
-    else {
-      $new_path = $path;
-    }
-    $menu_path_map[$path] = $new_path;
-    $menu[$new_path] = $item;
-  }
-
-  // Alter the menu after the first preprocessing phase, keys are like user/%.
-  drupal_alter('menu', $menu, MENU_ALTER_PREPROCESSED);
-
-  $menu_path_map[''] = '';
-  // Second pass: prepare for sorting and find parents.
-  foreach ($menu as $path => $item) {
-    $item = &$menu[$path];
-    $parent_path = $path;
-    $parents = array($item['_mid']);
-    $depth = 1;
-    if (isset($item['parent']) && isset($menu_path_map[$item['parent']])) {
-      $item['parent'] = $menu_path_map[$item['parent']];
-    }
-    if ($item['_visible'] || $item['_tab']) {
-      while ($parent_path) {
-        if (isset($menu[$parent_path]['parent'])) {
-          if (isset($menu_path_map[$menu[$parent_path]['parent']])) {
-            $menu[$parent_path]['parent'] = $menu_path_map[$menu[$parent_path]['parent']];
-          }
-          $parent_path = $menu[$parent_path]['parent'];
-        }
-        else {
-          $parent_path = substr($parent_path, 0, strrpos($parent_path, '/'));
-        }
-        if (isset($menu[$parent_path]) && $menu[$parent_path]['_visible']) {
-          $parent = $menu[$parent_path];
-          $parents[] = $parent['_mid'];
-          $depth++;
-          if (!isset($item['_pid'])) {
-            $item['_pid'] = $parent['_mid'];
-            $item['_visible_parent_path'] = $parent_path;
-          }
-        }
-      }
-    }
-    $parents[] = 0;
-    $parents = implode(',', array_reverse($parents));
-    // Store variables and set defaults.
-    $item += array(
-      '_pid' => 0,
-      '_depth' => ($item['_visible'] ? $depth : $item['_number_parts']),
-      '_parents' => $parents,
-      '_slashes' => $slashes,
-    );
-    // This sorting works correctly only with positive numbers,
-    // so we shift negative weights to be positive.
-    $sort[$path] = $item['_depth'] . sprintf('%05d', $item['weight'] + 50000) . $item['title'];
-    unset($item);
-  }
-  array_multisort($sort, $menu);
-
-  // We are now sorted, so let's build the tree.
-  $children = array();
-  foreach ($menu as $path => $item) {
-    if (!empty($item['_pid'])) {
-      $menu[$item['_visible_parent_path']]['_children'][] = $path;
-    }
-  }
-  menu_renumber($menu);
-
-  // Apply inheritance rules.
-  foreach ($menu as $path => $item) {
-    if ($item['_external']) {
-      $item['access callback'] = 1;
-    }
-    else {
-      $item = &$menu[$path];
-      for ($i = $item['_number_parts'] - 1; $i; $i--) {
-        $parent_path = implode('/', array_slice($item['_parts'], 0, $i));
-        if (isset($menu[$parent_path])) {
-          $parent = $menu[$parent_path];
-          // If a callback is not found, we try to find the first parent that
-          // has this callback. When found, its callback argument will also be
-          // copied but only if there is none in the current item.
-
-          // Because access is checked for each visible parent as well, we only
-          // inherit if arguments were given without a callback. Otherwise the
-          // inherited check would be identical to that of the parent. We do
-          // not inherit from visible parents which are themselves inherited.
-          if (!isset($item['access callback']) && isset($parent['access callback']) && !(isset($parent['access inherited']) && $parent['_visible'])) {
-            if (isset($item['access arguments'])) {
-              $item['access callback'] = $parent['access callback'];
-            }
-            else {
-              $item['access callback'] = 1;
-              // If a children of this element has an argument, we need to pair
-              // that with a real callback, not the 1 we set above.
-              $item['access inherited'] = TRUE;
-            }
-          }
-
-          // Unlike access callbacks, there are no shortcuts for page callbacks.
-          if (!isset($item['page callback']) && isset($parent['page callback'])) {
-            $item['page callback'] = $parent['page callback'];
-            if (!isset($item['page arguments']) && isset($parent['page arguments'])) {
-              $item['page arguments'] = $parent['page arguments'];
-            }
-          }
-        }
-      }
-      if (!isset($item['access callback'])) {
-        $item['access callback'] = isset($item['access arguments']) ? 'user_access' : 0;
-      }
-      if (is_bool($item['access callback'])) {
-        $item['access callback'] = intval($item['access callback']);
-      }
-      if (empty($item['page callback'])) {
-        $item['access callback'] = 0;
-      }
-    }
+function menu_get_names($reset = FALSE) {
+  static $names;
+  // TODO - use cache system to save this
 
-    if ($item['_tab']) {
-      if (isset($item['parent'])) {
-        $item['_depth'] = $item['parent'] ? $menu[$item['parent']]['_depth'] + 1 : 1;
-      }
-      else {
-        $item['parent'] = implode('/', array_slice($item['_parts'], 0, $item['_number_parts'] - 1));
-      }
+  if ($reset || empty($names)) {
+    $names = array();
+    $result = db_query("SELECT DISTINCT(menu_name) FROM {menu_links} ORDER BY menu_name");
+    while ($name = db_fetch_array($result)) {
+      $names[] = $name['menu_name'];
     }
-    else {
-      // Non-tab items specified the parent for visible links, and it's
-      // stored in parents, parent stores the tab parent.
-      $item['parent'] = $path;
-    }
-
-    $insert_item = $item;
-    unset($item);
-    $item = $insert_item + array(
-      'access arguments' => array(),
-      'access callback' => '',
-      'page arguments' => array(),
-      'page callback' => '',
-      '_mleft' => 0,
-      '_mright' => 0,
-      'block callback' => '',
-      'title arguments' => array(),
-      'title callback' => 't',
-      'description' => '',
-      'position' => '',
-      'attributes' => '',
-      'query' => '',
-      'fragment' => '',
-      'absolute' => '',
-      'html' => '',
-    );
-    $link_path = $item['to_arg_functions'] ? $path : drupal_get_path_alias($path);
-
-    $item['title arguments'] = empty($item['title arguments']) ? '' : serialize($item['title arguments']);
-
-    if ($item['attributes']) {
-      $item['attributes'] = serialize($item['attributes']);
-    }
-
-    // Check for children that are visible in the menu
-    $has_children = FALSE;
-    foreach ($item['_children'] as $child) {
-      if ($menu[$child]['_visible']) {
-        $has_children = TRUE;
-        break;
-      }
-    }
-    // We remove disabled items here -- this way they will be numbered in the
-    // tree so the menu overview screen can show them.
-    if (!empty($item['disabled'])) {
-      $item['_visible'] = FALSE;
-    }
-    db_query("INSERT INTO {menu} (
-      mid, pid, path, load_functions, to_arg_functions,
-      access_callback, access_arguments, page_callback, page_arguments,
-      title_callback, title_arguments, fit, number_parts, visible,
-      parents, depth, has_children, tab, title, parent,
-      type, mleft, mright, block_callback, description, position,
-      link_path, attributes, query, fragment, absolute, html)
-      VALUES (%d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s',
-      %d, %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s', %d, %d, '%s', '%s',
-      '%s', '%s', '%s', '%s', '%s', %d, %d)",
-      $item['_mid'], $item['_pid'], $path, $item['load_functions'],
-      $item['to_arg_functions'], $item['access callback'],
-      serialize($item['access arguments']), $item['page callback'],
-      serialize($item['page arguments']), $item['title callback'],
-      $item['title arguments'], $item['_fit'],
-      $item['_number_parts'], $item['_visible'], $item['_parents'],
-      $item['_depth'], $has_children, $item['_tab'],
-      $item['title'], $item['parent'], $item['type'], $item['_mleft'],
-      $item['_mright'], $item['block callback'], $item['description'],
-      $item['position'], $link_path,
-      $item['attributes'], $item['query'], $item['fragment'],
-      $item['absolute'], $item['html']);
   }
+  return $names;
 }
 
-function menu_renumber(&$tree) {
-  foreach ($tree as $key => $element) {
-    if (!isset($tree[$key]['_mleft'])) {
-      _menu_renumber($tree, $key);
-    }
-  }
-}
-
-function _menu_renumber(&$tree, $key) {
-  static $counter = 1;
-  if (!isset($tree[$key]['_mleft'])) {
-    $tree[$key]['_mleft'] = $counter++;
-    foreach ($tree[$key]['_children'] as $child_key) {
-      _menu_renumber($tree, $child_key);
-    }
-    $tree[$key]['_mright'] = $counter++;
-  }
-}
-
-// Placeholders.
 function menu_primary_links() {
+  $tree = menu_tree_data('primary links');
+  return array();
 }
 
 function menu_secondary_links() {
+  $tree = menu_tree_data('secondary links');
+  return array();
 }
 
 /**
@@ -941,59 +799,76 @@ function menu_secondary_links() {
  *   An array of links to the tabs.
  */
 function menu_local_tasks($level = 0) {
-  static $tabs = array(), $parents = array(), $parents_done = array();
+  static $tabs = array();
+
   if (empty($tabs)) {
     $router_item = menu_get_item();
     if (!$router_item || !$router_item->access) {
       return array();
     }
-    $map = arg(NULL);
-    do {
-      // Tabs are router items that have the same parent. If there is a new
-      // parent, let's add it the queue.
-      if (!empty($router_item->parent)) {
-        $parents[] = $router_item->parent;
-        // Do not add the same item twice.
-        $router_item->parent = '';
-      }
-      $parent = array_shift($parents);
-      // Do not process the same parent twice.
-      if (isset($parents_done[$parent])) {
-        continue;
+    // Get all tabs
+    $result = db_query("SELECT * FROM {menu} WHERE tab_root = '%s' AND tab_parent != '' ORDER BY weight, title", $router_item->tab_root);
+    $map = arg();
+    $children = array();
+    $tab_parent = array();
+
+    while ($item = db_fetch_object($result)) {
+      $children[$item->tab_parent][$item->path] = $item;
+      $tab_parent[$item->path] = $item->tab_parent;
+    }
+
+    // Find all tabs below the current path
+    $path = $router_item->path;
+    while (isset($children[$path])) {
+      $tabs_current = '';
+      $next_path = '';
+      foreach ($children[$path] as $item) {
+         _menu_translate($item, $map, TRUE);
+        if ($item->access) {
+          $link = l($item->title, $item->href); // TODO options?
+          // The default task is always active.
+          if ($item->type == MENU_DEFAULT_LOCAL_TASK) {
+            $tabs_current .= theme('menu_local_task', $link, TRUE);
+            $next_path = $item->path;
+          }
+          else {
+            $tabs_current .= theme('menu_local_task', $link);
+          }
+        }
       }
-      // This loads all the tabs.
-      $result = db_query("SELECT * FROM {menu} WHERE parent = '%s' AND tab = 1 ORDER BY mleft", $parent);
+      $path = $next_path;
+      $tabs[$item->number_parts] = $tabs_current;
+    }
+
+    // Find all tabs at the same level or above the current one
+    $parent = $router_item->tab_parent;
+    $path = $router_item->path;
+    $current = $router_item;
+    while (isset($children[$parent])) {
       $tabs_current = '';
-      while ($item = db_fetch_object($result)) {
-        // This call changes the path from for example user/% to user/123 and
-        // also determines whether we are allowed to access it.
-         _menu_translate($item, $map, MENU_RENDER_LINK);
+      $next_path = '';
+      $next_parent = '';
+      foreach ($children[$parent] as $item) {
+         _menu_translate($item, $map, TRUE);
         if ($item->access) {
-          $depth = $item->depth;
-          $link = l($item->title, $item->link_path, (array)$item);
+          $link = l($item->title, $item->href); // TODO options?
           // We check for the active tab.
-          if ($item->path == $router_item->path || (!$router_item->tab && $item->type == MENU_DEFAULT_LOCAL_TASK)) {
+          if ($item->path == $path) {
             $tabs_current .= theme('menu_local_task', $link, TRUE);
-            // Let's try to find the router item one level up.
-            $next_router_item = db_fetch_object(db_query("SELECT path, tab, parent FROM {menu} WHERE path = '%s'", $item->parent));
-            // We will need to inspect one level down.
-            $parents[] = $item->path;
+            $next_path = $item->tab_parent;
+            if (isset($tab_parent[$next_path])) {
+              $next_parent = $tab_parent[$next_path];
+            }
           }
           else {
             $tabs_current .= theme('menu_local_task', $link);
           }
         }
       }
-      // If there are tabs, let's add them
-      if ($tabs_current) {
-        $tabs[$depth] = $tabs_current;
-      }
-      $parents_done[$parent] = TRUE;
-      if (isset($next_router_item)) {
-        $router_item = $next_router_item;
-      }
-      unset($next_router_item);
-    } while ($parents);
+      $path = $next_path;
+      $parent = $next_parent;
+      $tabs[$item->number_parts] = $tabs_current;
+    }
     // Sort by depth
     ksort($tabs);
     // Remove the depth, we are interested only in their relative placement.
@@ -1003,52 +878,119 @@ function menu_local_tasks($level = 0) {
 }
 
 function menu_primary_local_tasks() {
-  return menu_local_tasks();
+  return menu_local_tasks(0);
 }
 
 function menu_secondary_local_tasks() {
   return menu_local_tasks(1);
 }
 
+function menu_set_active_menu_name($menu_name = NULL) {
+  static $active;
+
+  if (isset($menu_name)) {
+    $active = $menu_name;
+  }
+  elseif (!isset($active)) {
+    $active = 'navigation';
+  }
+  return $active;
+}
+
+function menu_get_active_menu_name() {
+  return menu_set_active_menu_name();
+}
+
 function menu_set_active_item() {
 }
 
+function menu_set_active_trail($new_trail = NULL) {
+  static $trail;
+
+  if (isset($new_trail)) {
+    $trail = $new_trail;
+  }
+  elseif (!isset($trail)) {
+    $trail = array();
+    $h = array('link_title' => t('Home'), 'href' => '<front>', 'options' => array(), 'type' => 0, 'title' => '');
+    $trail[] = (object)$h;
+    $item = menu_get_item();
+    if ($item->tab_parent) { // We are on a tab
+      $href = $item->tab_root;
+    }
+    else {
+      $href = $item->href;
+    }
+
+    $tree = menu_tree_data(menu_get_active_menu_name());
+    $curr = array_shift($tree);
+
+    while ($curr) {
+      if ($curr['link']->href == $href){
+        $trail[] = $curr['link'];
+        $curr = FALSE;
+      }
+      else {
+        if ($curr['below']) {
+          $trail[] = $curr['link'];
+          $tree = $curr['below'];
+        }
+        $curr = array_shift($tree);
+      }
+    }
+  }
+  return $trail;
+}
+
+function menu_get_active_trail() {
+  return menu_set_active_trail();
+}
+
 function menu_set_location() {
 }
 
 function menu_get_active_breadcrumb() {
-  $breadcrumb = array(l(t('Home'), ''));
+  $breadcrumb = array();
   $item = menu_get_item();
   if ($item && $item->access) {
-    foreach ($item->active_trail as $parent) {
-      $breadcrumb[] = l($parent->title, $parent->link_path, (array)$parent);
+    $active_trail = menu_get_active_trail();
+
+    foreach ($active_trail as $parent) {
+      $breadcrumb[] = l($parent->link_title, $parent->href, $parent->options);
+    }
+    $end = end($active_trail);
+    // Don't show a link to the current page in the bc trail.
+
+    if ($item->href == $end->href || ($item->type == MENU_DEFAULT_LOCAL_TASK)) {
+      array_pop($breadcrumb);
     }
   }
   return $breadcrumb;
 }
 
 function menu_get_active_title() {
-  $item = menu_get_item();
-  foreach (array_reverse($item->active_trail) as $item) {
-    if (!($item->type & MENU_IS_LOCAL_TASK)) {
+  $active_trail = menu_get_active_trail();
+
+  foreach (array_reverse($active_trail) as $item) {
+    if (!(bool)($item->type & MENU_IS_LOCAL_TASK)) {
       return $item->title;
     }
   }
 }
 
 /**
- * Get a menu item by its mid, access checked and link translated for
+ * Get a menu item by its mlid, access checked and link translated for
  * rendering.
  *
- * @param $mid
- *   The mid of the menu item.
+ * @param $mlid
+ *   The mlid of the menu item.
  * @return
  *   A menu object, with $item->access filled and link translated for
  *   rendering.
  */
-function menu_get_item_by_mid($mid) {
-  if ($item = db_fetch_object(db_query('SELECT * FROM {menu} WHERE mid = %d', $mid))) {
-    _menu_translate($item, arg(), MENU_RENDER_LINK);
+function menu_get_item_by_mlid($mlid) {
+  if ($item = db_fetch_object(db_query("SELECT * FROM {menu_links} ml LEFT JOIN {menu} m ON m.path = ml.router_path WHERE mlid = %d", $mlid))) {
+    _menu_link_translate($item);
     if ($item->access) {
       return $item;
     }
@@ -1057,20 +999,414 @@ function menu_get_item_by_mid($mid) {
 }
 
 /**
- * Returns the rendered local tasks. The default implementation renders
- * them as tabs.
- *
- * @ingroup themeable
- */
+* Returns the rendered local tasks. The default implementation renders
+* them as tabs.
+*
+* @ingroup themeable
+*/
 function theme_menu_local_tasks() {
-  $output = '';
+ $output = '';
+
+ if ($primary = menu_primary_local_tasks()) {
+   $output .= "<ul class=\"tabs primary\">\n". $primary ."</ul>\n";
+ }
+ if ($secondary = menu_secondary_local_tasks()) {
+   $output .= "<ul class=\"tabs secondary\">\n". $secondary ."</ul>\n";
+ }
 
-  if ($primary = menu_primary_local_tasks()) {
-    $output .= "<ul class=\"tabs primary\">\n". $primary ."</ul>\n";
+ return $output;
+}
+
+function menu_cache_clear($menu_name = 'navigation') {
+  // TODO: starting stub.  This will be called whenever an item is added to or
+  // moved from a named menu
+
+}
+
+/**
+ * This should be called any time broad changes might have been made to the
+ * router items or menu links.
+ */
+function menu_cache_clear_all() {
+  cache_clear_all('*', 'menu', TRUE);
+}
+
+/**
+ * Populate the database representation of the {menu{ table (router items)
+ * and the navigation menu in the {menu_links} table.
+ */
+function menu_rebuild() {
+  menu_cache_clear_all();
+  $menu = menu_router_build(TRUE);
+  _menu_navigation_links_rebuild($menu);
+}
+
+/**
+ * Collect, alter and store the menu definitions.
+ */
+function menu_router_build() {
+  db_query('DELETE FROM {menu}');
+  $callbacks = module_invoke_all('menu');
+  // Alter the menu as defined in modules, keys are like user/%user.
+  drupal_alter('menu', $callbacks);
+  $menu = _menu_router_build($callbacks);
+  return $menu;
+}
+
+function _menu_navigation_links_rebuild($menu) {
+  // Add normal and suggested items as links.
+  $menu_links = array();
+  foreach ($menu as $path => $item) {
+    if ($item['type'] == MENU_CALLBACK || $item['type'] == MENU_SUGGESTED_ITEM) {
+      $item['hidden'] = $item['type'];
+    }
+    $item += array(
+      'menu name' => 'navigation',
+      'link_title' => $item['title'],
+      'href' => $path,
+      'module' => 'system',
+      'hidden' => 0,
+    );
+    // We add nonexisting items.
+    if ($item['_visible'] && !db_result(db_query("SELECT COUNT(*) FROM {menu_links} WHERE menu_name = '%s' AND href = '%s'", $item['menu name'], $item['href']))) {
+      $menu_links[$path] = $item;
+      $sort[$path] = $item['_number_parts'];
+    }
   }
-  if ($secondary = menu_secondary_local_tasks()) {
-    $output .= "<ul class=\"tabs secondary\">\n". $secondary ."</ul>\n";
+  if ($menu_links) {
+    // Make sure no children comes before its parent.
+    array_multisort($sort, SORT_NUMERIC, $menu_links);
+
+    foreach ($menu_links as $item) {
+      menu_link_save($item, $menu);
+    }
   }
+  $placeholders = implode(', ', array_fill(0, count($menu), "'%s'"));
+  // Remove those items which router path does not exist any more.
+  db_query('DELETE FROM {menu_links} WHERE router_path NOT IN ('. $placeholders .')', array_keys($menu));
+}
 
-  return $output;
+/**
+ * Save a menu link.
+ *
+ * @param $item
+ *   An array representing a menu link item. Mandatory key is only href and
+ *  link_title. Possible keys are
+ *     menu name   default is navigation
+ *     weight      default is 0
+ *     expanded    whether the item is expanded.
+ *     options     An array of options, @see l for more.
+ *     mlid        If it's an existing item, this comes from the database.
+ *                 Never set by hand.
+ *     plid        The mlid of the parent.
+ *     router_path The path of the relevant router item.
+ */
+function menu_link_save(&$item, $_menu = NULL) {
+  static $menu;
+
+  if (isset($_menu)) {
+    $menu = $_menu;
+  }
+  elseif (!isset($menu)) {
+    $menu = menu_router_build();
+  }
+
+  drupal_alter('menu_link', $item, $menu);
+
+  $item['_external'] = menu_path_is_external($item['href']);
+  // Load defaults.
+  $item += array(
+    'menu name' => 'navigation',
+    'weight' => 0,
+    'link_title' => '',
+    'hidden' => 0,
+    'has_children' => 0,
+    'expanded' => 0,
+    'options' => empty($item['description']) ? array() : array('attributes' => array('title' => $item['description'])),
+  );
+  $existing_item = array();
+  if (isset($item['mlid'])) {
+    $existing_item = db_fetch_array(db_query("SELECT * FROM {menu_links} WHERE mlid = %d", $item['mlid']));
+  }
+  else {
+    $existing_item = db_fetch_array(db_query("SELECT * FROM {menu_links} WHERE menu_name = '%s' AND href = '%s'", $item['menu name'], $item['href']));
+  }
+
+  if (empty($existing_item)) {
+    $item['mlid'] = db_next_id('{menu_links}_mlid');
+  }
+
+  $menu_name = $item['menu name'];
+  $new_path = !$existing_item || ($existing_item['href'] != $item['href']);
+
+  // Find the parent.
+  if (isset($item['plid'])) {
+    $parent = db_fetch_array(db_query("SELECT * FROM {menu_links} WHERE mlid = %d", $item['plid']));
+  }
+  else { //
+    $parent_path = $item['href'];
+    do {
+      $parent_path = substr($parent_path, 0, strrpos($parent_path, '/'));
+      $parent = db_fetch_array(db_query("SELECT * FROM {menu_links} WHERE menu_name = '%s' AND href = '%s'", $menu_name, $parent_path));
+    } while ($parent === FALSE && $parent_path);
+  }
+  // Menu callbacks need to be in the links table for breadcrumbs, but can't
+  // be parents if they are generated directly from a router item
+  if (empty($parent['mlid']) || $parent['hidden'] == MENU_CALLBACK) {
+    $item['plid'] =  0;
+  }
+  else {
+    $item['plid'] = $parent['mlid'];
+  }
+
+  if (!$item['plid']) {
+    $item['p1'] = $item['mlid'];
+    $item['p2'] = $item['p3'] = $item['p4'] = $item['p5'] = $item['p6'] = 0;
+    $item['depth'] = 1;
+  }
+  else {
+    // Cannot add beyond the maximum depth.
+    if ($parent['depth'] >= (MENU_MAX_DEPTH)) {
+      return FALSE;
+    }
+    $item['depth'] = $parent['depth'] + 1;
+    _menu_parents_copy($item, $parent);
+    $item['p'. $item['depth']] = $item['mlid'];
+  }
+
+  if ($item['plid'] != $existing_item['plid']) {
+
+    // TODO: UPDATE the parents of the children of the current item
+    // TODO: check the has_children status of the previous parent
+  }
+  // Find the callback.
+  if (empty($item['router_path']) || $new_path) {
+    if ($item['_external']) {
+      $item['router_path'] = '';
+    }
+    else {
+      // Find the router path which will serve this paths.
+      $item['parts'] = explode('/', $item['href'], MENU_MAX_PARTS);
+      $item['router_path'] = $item['href'];
+      if (!isset($menu[$item['router_path']])) {
+        list($ancestors) = menu_get_ancestors($item['parts']);
+        while ($ancestors && (!isset($menu[$item['router_path']]))) {
+          $item['router_path'] = array_pop($ancestors);
+        }
+      }
+      if (empty($item['router_path'])) {
+        return FALSE;
+      }
+    }
+  }
+  if (!empty($existing_item)) {
+    db_query("UPDATE {menu_links} SET menu_name = '%s', plid = %d, href = '%s',
+      router_path = '%s', hidden = %d, external = %d, has_children = %d,
+      expanded = %d, weight = %d,  depth = %d,
+      p1 = %d, p2 = %d, p3 = %d, p4 = %d, p5 = %d, p6 = %d,
+      module = '%s', link_title = '%s', options = '%s' WHERE mlid = %d",
+      $item['menu name'], $item['plid'], $item['href'],
+      $item['router_path'], $item['hidden'], $item['_external'], $item['has_children'],
+      $item['expanded'],$item['weight'],  $item['depth'],
+      $item['p1'], $item['p2'], $item['p3'], $item['p4'], $item['p5'], $item['p6'],
+      $item['module'],  $item['link_title'], serialize($item['options']), $item['mlid']);
+  }
+  else {
+    db_query("INSERT INTO {menu_links} (
+      menu_name, mlid, plid, href,
+      router_path, hidden, external, has_children,
+      expanded, weight, depth,
+      p1, p2, p3, p4, p5, p6,
+      module, link_title, options) VALUES (
+      '%s', %d, %d, '%s',
+      '%s', %d, %d, %d,
+      %d, %d, %d,
+      %d, %d, %d, %d, %d, %d,
+      '%s', '%s', '%s')",
+      $item['menu name'], $item['mlid'], $item['plid'], $item['href'],
+      $item['router_path'], $item['hidden'], $item['_external'], $item['has_children'],
+      $item['expanded'],$item['weight'],  $item['depth'],
+      $item['p1'], $item['p2'], $item['p3'], $item['p4'], $item['p5'], $item['p6'],
+      $item['module'],  $item['link_title'], serialize($item['options']));
+ }
+
+  if ($item['plid'] && !$item['hidden']) {
+    db_query("UPDATE {menu_links} SET has_children = 1 WHERE mlid = %d", $item['plid']);
+  }
+
+  // Are there expanded items?
+  variable_set('menu_expanded', db_result(db_query_range('SELECT expanded FROM {menu_links} WHERE expanded = 1', 0, 1)));
+
+  return TRUE;
+}
+
+function _menu_parents_copy(&$dest, $source, $offset = 0){
+  $i = 1 + $offset;
+  $depth = 0;
+  for ($j = 1; $i <= MENU_MAX_DEPTH; $i++) {
+    $dest['p'. $i] = $source['p'. $j];
+    $j++;
+  }
 }
+
+function _menu_router_build($callbacks) {
+  // First pass: separate callbacks from pathes, making pathes ready for
+  // matching. Calculate fitness, and fill some default values.
+  $menu = array();
+  foreach ($callbacks as $path => $item) {
+    $load_functions = array();
+    $to_arg_functions = array();
+    $fit = 0;
+    $move = FALSE;
+
+    $parts = explode('/', $path, MENU_MAX_PARTS);
+    $number_parts = count($parts);
+    // We store the highest index of parts here to save some work in the fit
+    // calculation loop.
+    $slashes = $number_parts - 1;
+    // extract functions
+    foreach ($parts as $k => $part) {
+      $match = FALSE;
+      if (preg_match('/^%([a-z_]*)$/', $part, $matches)) {
+        if (empty($matches[1])) {
+          $match = TRUE;
+          $load_functions[$k] = NULL;
+        }
+        else {
+          if (function_exists($matches[1] .'_to_arg')) {
+            $to_arg_functions[$k] = $matches[1] .'_to_arg';
+            $load_functions[$k] = NULL;
+            $match = TRUE;
+          }
+          if (function_exists($matches[1] .'_load')) {
+            $load_functions[$k] = $matches[1] .'_load';
+            $match = TRUE;
+          }
+        }
+      }
+      if ($match) {
+        $parts[$k] = '%';
+      }
+      else {
+        $fit |=  1 << ($slashes - $k);
+      }
+    }
+    if ($fit) {
+      $move = TRUE;
+    }
+    else {
+      // If there is no %, it fits maximally.
+      $fit = (1 << $number_parts) - 1;
+    }
+    $item['load_functions'] = empty($load_functions) ? '' : serialize($load_functions);
+    $item['to_arg_functions'] = empty($to_arg_functions) ? '' : serialize($to_arg_functions);
+    $item += array(
+      'title' => '',
+      'weight' => 0,
+      'type' => MENU_NORMAL_ITEM,
+      '_number_parts' => $number_parts,
+      '_parts' => $parts,
+      '_fit' => $fit,
+    );
+    $item += array(
+      '_visible' => (bool)($item['type'] & MENU_VISIBLE_IN_BREADCRUMB),
+      '_tab' => (bool)($item['type'] & MENU_IS_LOCAL_TASK),
+    );
+    if ($move) {
+      $new_path = implode('/', $item['_parts']);
+      $menu[$new_path] = $item;
+      $sort[$new_path] = $number_parts;
+    }
+    else {
+      $menu[$path] = $item;
+      $sort[$path] = $number_parts;
+    }
+  }
+  array_multisort($sort, SORT_NUMERIC, $menu);
+
+  // Apply inheritance rules.
+  foreach ($menu as $path => $v) {
+    $item = &$menu[$path];
+    if (!isset($item['access callback']) && isset($item['access arguments'])) {
+      $item['access callback'] = 'user_access'; // Default callback
+    }
+    if (!$item['_tab']) {
+      // Non-tab items
+      $item['tab_parent'] = '';
+      $item['tab_root'] = $path;
+    }
+    for ($i = $item['_number_parts'] - 1; $i; $i--) {
+      $parent_path = implode('/', array_slice($item['_parts'], 0, $i));
+      if (isset($menu[$parent_path])) {
+
+        $parent = $menu[$parent_path];
+
+        if (!isset($item['tab_parent'])) {
+          // parent stores the parent of the path.
+          $item['tab_parent'] = $parent_path;
+        }
+        if (!isset($item['tab_root']) && !$parent['_tab']) {
+          $item['tab_root'] = $parent_path;
+        }
+        // If a callback is not found, we try to find the first parent that
+        // has a callback.
+        if (!isset($item['access callback']) && isset($parent['access callback'])) {
+          $item['access callback'] = $parent['access callback'];
+          if (!isset($item['access arguments']) && isset($parent['access arguments'])) {
+            $item['access arguments'] = $parent['access arguments'];
+          }
+        }
+        // Same for page callbacks.
+        if (!isset($item['page callback']) && isset($parent['page callback'])) {
+          $item['page callback'] = $parent['page callback'];
+          if (!isset($item['page arguments']) && isset($parent['page arguments'])) {
+            $item['page arguments'] = $parent['page arguments'];
+          }
+        }
+      }
+    }
+    if (!isset($item['access callback']) || empty($item['page callback'])) {
+      $item['access callback'] = 0;
+    }
+    if (is_bool($item['access callback'])) {
+      $item['access callback'] = intval($item['access callback']);
+    }
+
+    $item += array(
+      'access arguments' => array(),
+      'access callback' => '',
+      'page arguments' => array(),
+      'page callback' => '',
+      'block callback' => '',
+      'title arguments' => array(),
+      'title callback' => 't',
+      'description' => '',
+      'position' => '',
+      'tab_parent' => '',
+      'tab_root' => $path,
+      'path' => $path,
+    );
+    db_query("INSERT INTO {menu}
+      (path, load_functions, to_arg_functions, access_callback,
+      access_arguments, page_callback, page_arguments, fit,
+      number_parts, tab_parent, tab_root,
+      title, title_callback, title_arguments,
+      type, block_callback, description, position, weight)
+      VALUES ('%s', '%s', '%s', '%s',
+      '%s', '%s', '%s', %d,
+      %d, '%s', '%s',
+      '%s', '%s', '%s',
+      %d, '%s', '%s', '%s', %d)",
+      $path, $item['load_functions'], $item['to_arg_functions'], $item['access callback'],
+      serialize($item['access arguments']), $item['page callback'], serialize($item['page arguments']), $item['_fit'],
+      $item['_number_parts'], $item['tab_parent'], $item['tab_root'],
+      $item['title'], $item['title callback'], serialize($item['title arguments']),
+      $item['type'], $item['block callback'], $item['description'], $item['position'], $item['weight']);
+  }
+  return $menu;
+}
+
+function menu_path_is_external($path) {
+  $colonpos = strpos($path, ':');
+  return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) == check_plain($path);
+}
\ No newline at end of file
Index: modules/aggregator/aggregator.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.module,v
retrieving revision 1.338
diff -u -F^f -r1.338 aggregator.module
--- modules/aggregator/aggregator.module	30 Apr 2007 17:03:22 -0000	1.338
+++ modules/aggregator/aggregator.module	12 May 2007 04:30:35 -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.258
diff -u -F^f -r1.258 block.module
--- modules/block/block.module	30 Apr 2007 17:03:23 -0000	1.258
+++ modules/block/block.module	12 May 2007 04:30:35 -0000
@@ -292,7 +292,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'] : '';
@@ -498,7 +498,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.'));
@@ -506,7 +506,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']);
@@ -516,7 +516,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;
   }
 }
 
@@ -527,7 +528,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.'));
   }
@@ -536,7 +537,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) {
@@ -554,7 +555,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;
 }
 
 /**
@@ -571,12 +573,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.419
diff -u -F^f -r1.419 book.module
--- modules/book/book.module	30 Apr 2007 17:03:24 -0000	1.419
+++ modules/book/book.module	12 May 2007 04:30:35 -0000
@@ -199,12 +199,12 @@ function book_insert($node) {
 /**
  * Implementation of hook_submit().
  */
-function book_submit(&$node) {
+function book_submit(&$form_values) {
   global $user;
   // Set default values for non-administrators.
   if (!user_access('administer nodes')) {
-    $node->revision = 1;
-    $node->uid = $user->uid;
+    $form_values['revision'] = 1;
+    $form_values['uid'] = $user->uid;
   }
 }
 
@@ -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.19
diff -u -F^f -r1.19 color.module
--- modules/color/color.module	6 May 2007 05:47:51 -0000	1.19
+++ modules/color/color.module	12 May 2007 04:30:35 -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.541
diff -u -F^f -r1.541 comment.module
--- modules/comment/comment.module	30 Apr 2007 17:03:24 -0000	1.541
+++ modules/comment/comment.module	12 May 2007 04:30:35 -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,11 +1676,11 @@ 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);
 }
 
-function _comment_form_submit($form_values) {
+function _comment_form_submit(&$form_values, &$form_state) {
   if (!isset($form_values['date'])) {
     $form_values['date'] = 'now';
   }
@@ -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) {
-  $form_values = _comment_form_submit($form_values);
+function comment_form_submit($form_values, $form, &$form_state) {
+  $form_values = _comment_form_submit($form_values, $form_state);
   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.83
diff -u -F^f -r1.83 contact.module
--- modules/contact/contact.module	30 Apr 2007 17:03:24 -0000	1.83
+++ modules/contact/contact.module	12 May 2007 04:30:35 -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.5
diff -u -F^f -r1.5 dblog.module
--- modules/dblog/dblog.module	30 Apr 2007 17:03:24 -0000	1.5
+++ modules/dblog/dblog.module	12 May 2007 04:30:35 -0000
@@ -369,7 +369,7 @@ function theme_dblog_filters($form) {
   return $output;
 }
 
-function dblog_filter_form_validate($form_id, $form_values) {
+function dblog_filter_form_validate($form_values, $form, &$form_state) {
   if ($form_values['op'] == t('Filter') && empty($form_values['type']) && empty($form_values['severity'])) {
     form_set_error('type', t('You must select something to filter by.'));
   }
@@ -378,7 +378,7 @@ function dblog_filter_form_validate($for
 /**
  * Process result from dblog administration filter form.
  */
-function dblog_filter_form_submit($form_id, $form_values) {
+function dblog_filter_form_submit($form_id, $form_values, &$form_state) {
   $op = $form_values['op'];
   $filters = dblog_filters();
   switch ($op) {
Index: modules/filter/filter.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v
retrieving revision 1.172
diff -u -F^f -r1.172 filter.module
--- modules/filter/filter.module	30 Apr 2007 17:03:24 -0000	1.172
+++ modules/filter/filter.module	12 May 2007 04:30:35 -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.396
diff -u -F^f -r1.396 forum.module
--- modules/forum/forum.module	7 May 2007 17:02:54 -0000	1.396
+++ modules/forum/forum.module	12 May 2007 04:30:35 -0000
@@ -348,11 +348,11 @@ function forum_view(&$node, $teaser = FA
  * Check in particular that only a "leaf" term in the associated taxonomy
  * vocabulary is selected, not a "container" term.
  */
-function forum_submit(&$node) {
+function forum_submit(&$form_values) {
   // Make sure all fields are set properly:
-  $node->icon = !empty($node->icon) ? $node->icon : '';
+  $form_values['icon'] = $form_values['icon'] ? $form_values['icon'] : '';
 
-  if ($node->taxonomy) {
+  if ($form_values['taxonomy']) {
     // Get the forum terms from the (cached) tree
     $tree = taxonomy_get_tree(_forum_get_vid());
     if ($tree) {
@@ -360,16 +360,16 @@ function forum_submit(&$node) {
         $forum_terms[] = $term->tid;
       }
     }
-    foreach ($node->taxonomy as $term) {
+    foreach ($form_values['taxonomy'] as $term) {
       if (in_array($term, $forum_terms)) {
-        $node->tid = $term;
+        $form_values['tid'] = $term;
       }
     }
-    $old_tid = db_result(db_query_range("SELECT tid FROM {forum} WHERE nid = %d ORDER BY vid DESC", $node->nid, 0, 1));
+    $old_tid = db_result(db_query_range("SELECT tid FROM {forum} WHERE nid = %d ORDER BY vid DESC", $form_values['nid'], 0, 1));
     if ($old_tid) {
-      if (($node->tid != $old_tid) && $node->shadow) {
+      if (($form_values['tid'] != $old_tid) && $form_values['shadow']) {
         // A shadow copy needs to be created. Retain new term and add old term.
-        $node->taxonomy[] = $old_tid;
+        $form_values['taxonomy'][] = $old_tid;
       }
     }
   }
@@ -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,8 +568,8 @@ function forum_form_forum($edit = array(
 /**
  * Process forum form and container form submissions.
  */
-function forum_form_submit($form_id, $form_values) {
-  if ($form_id == 'forum_form_container') {
+function forum_form_submit($form_values, $form, &$form_state) {
+  if ($form['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/menu/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v
retrieving revision 1.109
diff -u -F^f -r1.109 menu.module
--- modules/menu/menu.module	30 Apr 2007 17:03:25 -0000	1.109
+++ modules/menu/menu.module	12 May 2007 04:30:35 -0000
@@ -126,33 +126,10 @@ function menu_menu() {
 }
 
 /**
- * Implementation of hook_menu_alter.
+ * Implementation of hook_navigation_links_alter.
  */
-function menu_menu_alter(&$menu, $phase) {
-  switch ($phase) {
-    case MENU_ALTER_MODULE_DEFINED:
-      foreach ($menu as $path => $item) {
-        if (isset($item['_custom_item']) && $item['_custom_item'] && !$item['_external']) {
-          list($ancestors, $placeholders) = menu_get_ancestors(explode('/', $path, 6));
-          // Remove the item itself, custom items need to inherit from an existing item.
-          array_shift($ancestors);
-          array_shift($placeholders);
-          $inherit_item = db_fetch_object(db_query_range('SELECT * FROM {menu} WHERE path IN ('. implode (',', $placeholders) .') ORDER BY fit DESC', $ancestors, 0, 1));
-          drupal_set_message(var_export($inherit_item, TRUE));
-          $menu[$path]['access callback'] = $inherit_item->access_callback;
-          $menu[$path]['access arguments'] = unserialize($inherit_item->access_arguments);
-          $menu[$path]['page callback'] = $inherit_item->page_callback;
-          $menu[$path]['page arguments'] = unserialize($inherit_item->page_arguments);
-        }
-      }
-      break;
-    case MENU_ALTER_PREPROCESSED:
-      $result = db_query('SELECT * FROM {menu_custom} me WHERE admin = 0');
-      while ($item = db_fetch_array($result)) {
-        $menu[$item['path']] = $item + $menu[$item['path']];
-      }
-      break;
-  }
+function menu_navigation_links_alter(&$menu) {
+
 }
 
 /**
@@ -161,6 +138,8 @@ function menu_menu_alter(&$menu, $phase)
  */
 function menu_overview() {
   $header = array(t('Menu item'), t('Expanded'), array('data' => t('Operations'), 'colspan' => '3'));
+  
+  
   $result = db_query('SELECT m.*, me.disabled FROM {menu} m LEFT JOIN {menu_custom} me ON m.path = me.path WHERE visible = 1 OR (disabled = 1 AND admin = 0) ORDER BY mleft');
   $map = arg();
   $rows = array();
@@ -321,7 +300,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 +320,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 +441,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 +474,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 +487,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 +507,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;
 }
 
 /**
@@ -674,6 +656,10 @@ function _menu_form_alter(&$form, $form_
   }
 }
 
+function menu_get_root_menus() {
+  return array();
+}
+
 /**
  * Menu callback; presents menu configuration options.
  */
Index: modules/node/content_types.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/content_types.inc,v
retrieving revision 1.30
diff -u -F^f -r1.30 content_types.inc
--- modules/node/content_types.inc	7 May 2007 12:32:55 -0000	1.30
+++ modules/node/content_types.inc	12 May 2007 04:30:35 -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,7 +282,8 @@ 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;
   }
 
   $status = node_type_save($type);
@@ -336,7 +337,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 +398,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 +408,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.811
diff -u -F^f -r1.811 node.module
--- modules/node/node.module	11 May 2007 16:55:58 -0000	1.811
+++ modules/node/node.module	12 May 2007 04:30:35 -0000
@@ -173,7 +173,7 @@ function node_mark($nid, $timestamp) {
 /**
  * See if the user used JS to submit a teaser.
  */
-function node_teaser_js(&$form, $form_values) {
+function node_teaser_js(&$form, $form_values, &$form_state) {
   // Glue the teaser to the body.
   if (isset($form['#post']['teaser_js'])) {
     if (trim($form_values['teaser_js'])) {
@@ -185,7 +185,7 @@ function node_teaser_js(&$form, $form_va
       $body = '<!--break-->'. $form_values['body'];
     }
     // Pass value onto preview/submit
-    form_set_value($form['body'], $body);
+    form_set_value($form['body'], $body, $form_state);
     // Pass value back onto form
     $form['body']['#value'] = $body;
   }
@@ -1066,7 +1066,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');
   }
@@ -1083,10 +1083,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;
 }
 
 /**
@@ -1247,6 +1248,7 @@ function node_menu() {
     'title' => 'Edit',
     'page callback' => 'node_page_edit',
     'page arguments' => array(1),
+    'access callback' => 'node_access',
     'access arguments' => array('update', 1),
     'weight' => 1,
     'type' => MENU_LOCAL_TASK);
@@ -1254,6 +1256,7 @@ function node_menu() {
     'title' => 'Delete',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('node_delete_confirm', 1),
+    'access callback' => 'node_access',
     'access arguments' => array('delete', 1),
     'weight' => 1,
     'type' => MENU_CALLBACK);
@@ -1514,7 +1517,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'):
@@ -1542,7 +1545,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
@@ -1562,7 +1565,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.'));
@@ -1683,14 +1686,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;
 }
 
 /**
@@ -1956,10 +1960,6 @@ function node_submit($node) {
     $node->created = $node->date ? strtotime($node->date) : NULL;
   }
 
-  // Do node-type-specific validation checks.
-  node_invoke($node, 'submit');
-  node_invoke_nodeapi($node, 'submit');
-
   $node->validated = TRUE;
 
   return $node;
@@ -2003,7 +2003,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);
 }
 
@@ -2025,9 +2025,15 @@ function node_object_prepare(&$node) {
 /**
  * Generate the node add/edit form array.
  */
-function node_form($node, $form_values = NULL) {
+function node_form($node, $form_state = NULL) {
   global $user;
 
+  if (isset($form_state['node'])) {
+    $node = $form_state['node'] + (array)$node;
+  }
+  if (isset($form_state['node_preview'])) {
+    $form['#prefix'] = $form_state['node_preview'];
+  }
   $node = (object)$node;
   foreach (array('body', 'title', 'format') as $key) {
     if (!isset($node->$key)) {
@@ -2050,7 +2056,7 @@ function node_form($node, $form_values =
   // Changed must be sent to the client, for later overwrite error checking.
   $form['changed'] = array('#type' => 'hidden', '#default_value' => isset($node->changed) ? $node->changed : NULL);
   // Get the node-specific bits.
-  if ($extra = node_invoke($node, 'form', $form_values)) {
+  if ($extra = node_invoke($node, 'form', $form_state)) {
     $form = array_merge_recursive($form, $extra);
   }
   if (!isset($form['title']['#weight'])) {
@@ -2118,46 +2124,43 @@ function node_form($node, $form_values =
   }
 
   // Add the buttons.
-  $form['preview'] = array('#type' => 'button', '#value' => t('Preview'), '#weight' => 40);
-  $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'), '#weight' => 45);
+  $form['preview'] = array(
+    '#type' => 'submit',
+    '#value' => t('Preview'),
+    '#weight' => 40,
+    '#submit' => array('node_form_build_preview'),
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Submit'),
+    '#weight' => 45,
+    '#submit' => array('node_form_submit'),
+  );
   if (!empty($node->nid) && node_access('delete', $node)) {
     $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['#validate'][] = 'node_form_validate';
   $form['#theme'] = 'node_form';
   return $form;
 }
 
-function node_form_add_preview($form) {
-  global $form_values;
-
-  $op = isset($form_values['op']) ? $form_values['op'] : '';
-  if ($op == t('Preview')) {
-    // Invoke full validation for the form, to protect against cross site
-    // request forgeries (CSRF) and setting arbitrary values for fields such as
-    // the input format. Preview the node only when form validation does not
-    // set any errors.
-    drupal_validate_form($form['form_id']['#value'], $form);
-    if (!form_get_errors()) {
-      // Because the node preview may display a form, we must render it
-      // outside the node submission form tags using the #prefix property
-      // (i.e. to prevent illegally nested forms).
-      // If the node form already has a #prefix, we must preserve it.
-      // In this case, we put the preview before the #prefix so we keep
-      // the #prefix as "close" to the rest of the form as possible,
-      // for example, to keep a <div> only around the form, not the
-      // preview. We pass the global $form_values here to preserve
-      // changes made during form validation.
-      $preview = node_preview((object)$form_values);
-      $form['#prefix'] = isset($form['#prefix']) ? $preview . $form['#prefix'] : $preview;
-    }
-  }
-  if (variable_get('node_preview', 0) && (form_get_errors() || $op != t('Preview'))) {
-    unset($form['submit']);
-  }
-  return $form;
+function node_form_build_preview($form_values, $form, &$form_state) {
+  // We do not want to execute button level handlers, we want the form level
+  // handlers to go in and change the submitted values.
+  unset($form_state['submit_handlers']);
+  form_execute_handlers('submit', $form, $form_state);
+  // Because the node preview may display a form, we must render it
+  // outside the node submission form tags using the #prefix property
+  // (i.e. to prevent illegally nested forms).
+  // If the node form already has a #prefix, we must preserve it.
+  // In this case, we put the preview before the #prefix so we keep
+  // the #prefix as "close" to the rest of the form as possible,
+  // for example, to keep a <div> only around the form, not the
+  // preview. We pass the global $form_values here to preserve
+  // changes made during form validation.
+  $form_state['node_preview'] = node_preview((object)$form_state['values']);
+  $form_state['rebuild'] = TRUE;
+  $form_state['node'] = $form_state['values'];
 }
 
 function theme_node_form($form) {
@@ -2289,7 +2292,7 @@ function node_preview($node) {
  */
 function theme_node_preview($node) {
   $output = '<div class="preview">';
-  if ($node->teaser && $node->teaser != $node->body) {
+  if (!empty($node->teaser) && !empty($node->body) && $node->teaser != $node->body) {
     drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "&lt;!--break--&gt;" (without the quotes) to fine-tune where your post gets split.</span>'));
     $output .= '<h3>'. t('Preview trimmed version') .'</h3>';
     $output .= node_view(drupal_clone($node), 1, FALSE, 0);
@@ -2308,7 +2311,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:
@@ -2448,6 +2451,7 @@ function node_page_default() {
 
     $default_message = t('<h1 class="title">Welcome to your new Drupal website!</h1><p>Please follow these steps to set up and start using your website:</p>');
     $default_message .= '<ol>';
+
     $default_message .= '<li>'. t('<strong>Configure your website</strong> Once logged in, visit the <a href="@admin">administration section</a>, where you can <a href="@config">customize and configure</a> all aspects of your website.', array('@admin' => url('admin'), '@config' => url('admin/settings'))) .'</li>';
     $default_message .= '<li>'. t('<strong>Enable additional functionality</strong> Next, visit the <a href="@modules">module list</a> and enable features which suit your specific needs. You can find additional modules in the <a href="@download_modules">Drupal modules download section</a>.', array('@modules' => url('admin/build/modules'), '@download_modules' => 'http://drupal.org/project/modules')) .'</li>';
     $default_message .= '<li>'. t('<strong>Customize your website design</strong> To change the "look and feel" of your website, visit the <a href="@themes">themes section</a>. You may choose from one of the included themes or download additional themes from the <a href="@download_themes">Drupal themes download section</a>.', array('@themes' => url('admin/build/themes'), '@download_themes' => 'http://drupal.org/project/themes')) .'</li>';
@@ -2606,14 +2610,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'];
 
@@ -2643,7 +2647,7 @@ function node_search_validate($form_id, 
     $keys .= ' "'. str_replace('"', ' ', $form_values['phrase']) .'"';
   }
   if (!empty($keys)) {
-    form_set_value($form['basic']['inline']['processed_keys'], trim($keys));
+    form_set_value($form['basic']['inline']['processed_keys'], trim($keys), $form_state);
   }
 }
 
Index: modules/path/path.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/path/path.module,v
retrieving revision 1.117
diff -u -F^f -r1.117 path.module
--- modules/path/path.module	10 May 2007 10:00:50 -0000	1.117
+++ modules/path/path.module	12 May 2007 04:30:35 -0000
@@ -105,10 +105,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;
   }
 }
 
@@ -127,13 +128,11 @@ function path_set_alias($path = NULL, $a
   if ($path && !$alias) {
     // Delete based on path
     db_query("DELETE FROM {url_alias} WHERE src = '%s' AND language = '%s'", $path, $language);
-    db_query("UPDATE {menu} SET link_path = path WHERE path = '%s'", $path);
     drupal_clear_path_cache();
   }
   else if (!$path && $alias) {
     // Delete based on alias
     db_query("DELETE FROM {url_alias} WHERE dst = '%s' AND language = '%s'", $alias, $language);
-    db_query("UPDATE {menu} SET link_path = path WHERE link_path = '%s'", $alias);
     drupal_clear_path_cache();
   }
   else if ($path && $alias) {
@@ -169,7 +168,6 @@ function path_set_alias($path = NULL, $a
     }
     if ($alias_count == 0 || $path_count == 0) {
       drupal_clear_path_cache();
-      db_query("UPDATE {menu} SET link_path = '%s' WHERE path = '%s'", $alias, $path);
     }
   }
 }
@@ -178,8 +176,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['#alias'] = $edit;
 
   $form['src'] = array(
@@ -369,7 +367,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;
@@ -384,12 +382,13 @@ 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/poll/poll.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.module,v
retrieving revision 1.227
diff -u -F^f -r1.227 poll.module
--- modules/poll/poll.module	30 Apr 2007 17:03:27 -0000	1.227
+++ modules/poll/poll.module	12 May 2007 04:30:36 -0000
@@ -99,10 +99,14 @@ function poll_delete($node) {
 /**
  * Implementation of hook_submit().
  */
-function poll_submit(&$node) {
+function poll_node_form_submit(&$form_values, $form, &$form_state) {
   // Renumber fields
-  $node->choice = array_values($node->choice);
-  $node->teaser = poll_teaser($node);
+  $form_values['choice'] = array_values($form_values['choice']);
+  $form_values['teaser'] = poll_teaser((object)$form_values);
+  $form_state['choices'] = $form_values['choices'];
+  if ($form_values['morechoices']) {
+    $form_state['choices'] *= 2;
+  }
 }
 
 /**
@@ -132,7 +136,7 @@ function poll_validate($node) {
 /**
  * Implementation of hook_form().
  */
-function poll_form($node, $form_values = NULL) {
+function poll_form($node, $form_state = NULL) {
   $admin = user_access('administer nodes');
   $type = node_get_types('type', $node);
   $form['title'] = array(
@@ -143,11 +147,8 @@ function poll_form($node, $form_values =
     '#weight' => -1
   );
 
-  if (isset($form_values)) {
-    $choices = $form_values['choices'];
-    if ($form_values['morechoices']) {
-      $choices *= 2;
-    }
+  if (isset($form_state['choices'])) {
+    $choices = $form_state['choices'];
   }
   else {
     $choices = max(2, empty($node->choice) ? 5 : count($node->choice));
@@ -174,7 +175,7 @@ function poll_form($node, $form_values =
     '#type' => 'checkbox',
     '#parents' => array('morechoices'),
     '#title' => t('Need more choices'),
-    '#value' => 0,
+    '#default_value' => 0,
     '#description' => t("If the amount of boxes above isn't enough, check this box and click the Preview button below to add some more."),
     '#weight' => 1,
   );
@@ -183,14 +184,14 @@ function poll_form($node, $form_values =
     $form['choice'][$a]['chtext'] = array(
       '#type' => 'textfield',
       '#title' => t('Choice @n', array('@n' => ($a + 1))),
-      '#default_value' => isset($node->choice) ? $node->choice[$a]['chtext'] : '',
+      '#default_value' => isset($node->choice[$a]) ? $node->choice[$a]['chtext'] : '',
     );
 
     if ($admin) {
       $form['choice'][$a]['chvotes'] = array(
         '#type' => 'textfield',
         '#title' => t('Votes for choice @n', array('@n' => ($a + 1))),
-        '#default_value' => isset($node->choice) ? (int)$node->choice[$a]['chvotes'] : 0,
+        '#default_value' => isset($node->choice[$a]) ? (int)$node->choice[$a]['chvotes'] : 0,
         '#size' => 5, '#maxlength' => 7
       );
     }
Index: modules/profile/profile.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile/profile.module,v
retrieving revision 1.200
diff -u -F^f -r1.200 profile.module
--- modules/profile/profile.module	30 Apr 2007 17:03:27 -0000	1.200
+++ modules/profile/profile.module	12 May 2007 04:30:36 -0000
@@ -331,7 +331,7 @@ function profile_field_form($arg = NULL)
 /**
  * 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.220
diff -u -F^f -r1.220 search.module
--- modules/search/search.module	30 Apr 2007 17:03:27 -0000	1.220
+++ modules/search/search.module	12 May 2007 04:30:36 -0000
@@ -207,7 +207,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');
   }
@@ -268,11 +268,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;
   }
 }
 
@@ -1043,14 +1044,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) {
-  form_set_value($form['basic']['inline']['processed_keys'], trim($form_values['keys']));
+function search_form_validate($form_values, $form, &$form_state) {
+  form_set_value($form['basic']['inline']['processed_keys'], trim($form_values['keys'], $form_state));
 }
 
 /**
  * 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.'));
@@ -1058,7 +1059,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;
 }
 
 /**
@@ -1076,8 +1078,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;
@@ -1086,8 +1088,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.105
diff -u -F^f -r1.105 system.install
--- modules/system/system.install	11 May 2007 07:33:46 -0000	1.105
+++ modules/system/system.install	12 May 2007 04:30:36 -0000
@@ -252,6 +252,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,
@@ -336,8 +346,6 @@ function system_install() {
       ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
 
       db_query("CREATE TABLE {menu} (
-        mid int NOT NULL default 0,
-        pid int NOT NULL default 0,
         path varchar(255) NOT NULL default '',
         load_functions varchar(255) NOT NULL default '',
         to_arg_functions varchar(255) NOT NULL default '',
@@ -347,32 +355,46 @@ function system_install() {
         page_arguments text,
         fit int NOT NULL default 0,
         number_parts int NOT NULL default 0,
-        mleft int NOT NULL default 0,
-        mright int NOT NULL default 0,
-        visible int NOT NULL default 0,
-        parents varchar(255) NOT NULL default '',
-        depth int NOT NULL default 0,
-        has_children int NOT NULL default 0,
-        tab int NOT NULL default 0,
+        tab_parent varchar(255) NOT NULL default '',
+        tab_root varchar(255) NOT NULL default '',
         title varchar(255) NOT NULL default '',
         title_callback varchar(255) NOT NULL default '',
         title_arguments varchar(255) NOT NULL default '',
-        parent varchar(255) NOT NULL default '',
         type int NOT NULL default 0,
         block_callback varchar(255) NOT NULL default '',
         description varchar(255) NOT NULL default '',
         position varchar(255) NOT NULL default '',
-        link_path varchar(255) NOT NULL default '',
-        attributes varchar(255) NOT NULL default '',
-        query varchar(255) NOT NULL default '',
-        fragment varchar(255) NOT NULL default '',
-        absolute INT NOT NULL default 0,
-        html INT NOT NULL default 0,
+        weight int NOT NULL default 0,
         PRIMARY KEY  (path),
         KEY fit (fit),
-        KEY visible (visible),
-        KEY pid (pid),
-        KEY parent (parent)
+        KEY tab_parent (tab_parent)
+      ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
+
+      db_query("CREATE TABLE {menu_links} (
+        menu_name varchar(64) NOT NULL default '',
+        mlid int NOT NULL default '0',
+        plid int NOT NULL default '0',
+        href varchar(255) NOT NULL default '',
+        router_path varchar(255) NOT NULL default '',
+        hidden smallint NOT NULL default '0',
+        external smallint NOT NULL default '0',
+        has_children int NOT NULL default '0',
+        expanded smallint NOT NULL default '0',
+        weight int NOT NULL default '0',
+        depth int NOT NULL default '0',
+        p1 int NOT NULL default '0',
+        p2 int NOT NULL default '0',
+        p3 int NOT NULL default '0',
+        p4 int NOT NULL default '0',
+        p5 int NOT NULL default '0',
+        p6 int NOT NULL default '0',
+        module varchar(255) NOT NULL default 'system',
+        link_title varchar(255) NOT NULL default '',
+        options text,
+        PRIMARY KEY (mlid),
+        KEY parents (plid, p1, p2, p3, p4, p5),
+        KEY menu_name_path (menu_name, href),
+        KEY menu_children_expanded (has_children, expanded)
       ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
 
       db_query("CREATE TABLE {node} (
@@ -734,9 +756,19 @@ function system_install() {
         serialized smallint 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,
@@ -821,8 +853,6 @@ function system_install() {
       )");
 
       db_query("CREATE TABLE {menu} (
-        mid int NOT NULL default 0,
-        pid int NOT NULL default 0,
         path varchar(255) NOT NULL default '',
         load_functions varchar(255) NOT NULL default '',
         to_arg_functions varchar(255) NOT NULL default '',
@@ -832,35 +862,48 @@ function system_install() {
         page_arguments text,
         fit int NOT NULL default 0,
         number_parts int NOT NULL default 0,
-        mleft int NOT NULL default 0,
-        mright int NOT NULL default 0,
-        visible int NOT NULL default 0,
-        parents varchar(255) NOT NULL default '',
-        depth int NOT NULL default 0,
-        has_children int NOT NULL default 0,
-        tab int NOT NULL default 0,
+        tab_parent varchar(255) NOT NULL default '',
+        tab_root varchar(255) NOT NULL default '',
         title varchar(255) NOT NULL default '',
         title_callback varchar(255) NOT NULL default '',
         title_arguments varchar(255) NOT NULL default '',
-        parent varchar(255) NOT NULL default '',
         type int NOT NULL default 0,
         block_callback varchar(255) NOT NULL default '',
         description varchar(255) NOT NULL default '',
         position varchar(255) NOT NULL default '',
-        link_path varchar(255) NOT NULL default '',
-        attributes varchar(255) NOT NULL default '',
-        query varchar(255) NOT NULL default '',
-        fragment varchar(255) NOT NULL default '',
-        absolute INT NOT NULL default 0,
-        html INT NOT NULL default 0,
-        PRIMARY KEY (path)
+        weight int NOT NULL default 0,
+        PRIMARY KEY  (path),
       )");
-
       db_query("CREATE INDEX {menu}_fit_idx ON {menu} (fit)");
-      db_query("CREATE INDEX {menu}_visible_idx ON {menu} (visible)");
-      db_query("CREATE INDEX {menu}_parent_idx ON {menu} (parent)");
-      db_query("CREATE INDEX {menu}_pid_idx ON {menu} (parent)");
+      db_query("CREATE INDEX {menu}_tab_parent_idx ON {menu} (tab_parent)");
 
+      db_query("CREATE TABLE {menu_links} (
+        menu_name varchar(64) NOT NULL default '',
+        mlid int NOT NULL default '0',
+        plid int NOT NULL default '0',
+        href varchar(255) NOT NULL default '',
+        router_path varchar(255) NOT NULL default '',
+        hidden smallint NOT NULL default '0',
+        external smallint NOT NULL default '0',
+        has_children int NOT NULL default '0',
+        expanded smallint NOT NULL default '0',
+        weight int NOT NULL default '0',
+        depth int NOT NULL default '0',
+        p1 int NOT NULL default '0',
+        p2 int NOT NULL default '0',
+        p3 int NOT NULL default '0',
+        p4 int NOT NULL default '0',
+        p5 int NOT NULL default '0',
+        p6 int NOT NULL default '0',
+        module varchar(255) NOT NULL default 'system',
+        link_title varchar(255) NOT NULL default '',
+        options text,
+        PRIMARY KEY (mlid)
+      )");
+      db_query("CREATE INDEX {menu_links}_parents_idx ON {menu_links} (plid, p1, p2, p3, p4, p5)");
+      db_query("CREATE INDEX {menu_links}_menu_name_idx ON {menu_links} (menu_name, href)");
+      db_query("CREATE INDEX {menu_links}_children_expanded_idx ON {menu_links} (has_children, expanded)");
+      
       db_query("CREATE TABLE {node} (
         nid serial CHECK (nid >= 0),
         vid int_unsigned NOT NULL default '0',
@@ -2126,15 +2169,15 @@ function system_update_151() {
         $num_inserted ++;
         $node_unalias = db_fetch_array(db_query("SELECT src FROM {url_alias} WHERE dst = '%s'", $links['link'][$i]));
         if (isset($node_unalias) && is_array($node_unalias)) {
-          $link_path = $node_unalias['src'];
+          $href = $node_unalias['src'];
         }
         else {
-          $link_path = $links['link'][$i];
+          $href = $links['link'][$i];
         }
 
         $mid = db_next_id('{menu}_mid');
         $ret[] = update_sql("INSERT INTO {menu} (mid, pid, path, title, description, weight, type) " .
-                             "VALUES ($mid, {$menus[$loop]['pid']}, '" . db_escape_string($link_path) .
+                             "VALUES ($mid, {$menus[$loop]['pid']}, '" . db_escape_string($href) .
                              "', '" . db_escape_string($links['text'][$i]) .
                              "', '" . db_escape_string($links['description'][$i]) . "', 0, 118)");
       }
@@ -3885,6 +3928,42 @@ function system_update_6014() {
   return array();
 }
 
+/**
+ * Add the form cache table.
+ */
+function system_update_6015() {
+  $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"
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.474
diff -u -F^f -r1.474 system.module
--- modules/system/system.module	6 May 2007 05:47:52 -0000	1.474
+++ modules/system/system.module	12 May 2007 04:30:36 -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
@@ -394,11 +394,11 @@ function system_main_admin_page($arg = N
   if (system_status(TRUE)) {
     drupal_set_message(t('One or more problems were detected with your Drupal installation. Check the <a href="@status">status report</a> for more information.', array('@status' => url('admin/logs/status'))), 'error');
   }
-
-  $map = arg(NULL);
-  $result = db_query("SELECT * FROM {menu} WHERE path LIKE 'admin/%%' AND depth = 2 AND visible = 1 AND path != 'admin/help' ORDER BY mleft");
+  $result = db_query("SELECT * FROM {menu_links} ml INNER JOIN {menu} m ON ml.router_path = m.path 
+                      WHERE ml.href like 'admin/%' AND ml.href != 'admin/help' AND ml.depth = 2 AND ml.menu_name = 'navigation' 
+                      ORDER BY p1 ASC, p2 ASC, p3 ASC");
   while ($item = db_fetch_object($result)) {
-    _menu_translate($item, $map, MENU_RENDER_LINK);
+    _menu_link_translate($item);
     if (!$item->access) {
       continue;
     }
@@ -418,11 +418,14 @@ function system_main_admin_page($arg = N
  * Provide a single block on the administration overview page.
  */
 function system_admin_menu_block($item) {
-  $map = arg(NULL);
   $content = array();
-  $result = db_query('SELECT * FROM {menu} WHERE depth = %d AND %d < mleft AND mright < %d AND visible = 1 ORDER BY mleft', $item->depth + 1, $item->mleft, $item->mright);
+  if (!isset($item->mlid)) {
+    $item->mlid = db_result(db_query("SELECT mlid FROM {menu_links} ml WHERE ml.router_path = '%s' AND menu_name = 'navigation'", $item->path));
+  }
+  $result = db_query("SELECT * FROM {menu_links} ml INNER JOIN {menu} m ON ml.router_path = m.path 
+                      WHERE ml.plid = '%s' AND ml.menu_name = 'navigation' ORDER BY m.weight, m.title", $item->mlid);
   while ($item = db_fetch_object($result)) {
-    _menu_translate($item, $map, MENU_RENDER_LINK);
+    _menu_link_translate($item);
     if (!$item->access) {
       continue;
     }
@@ -474,13 +477,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']);
@@ -749,8 +752,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);
 }
@@ -1168,13 +1171,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'];
 
@@ -1200,7 +1203,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.
@@ -1305,7 +1308,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'");
 
@@ -1336,7 +1339,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;
 }
 
 /**
@@ -1354,12 +1358,13 @@ function system_themes_form_submit($form
  * @return
  *   The form array.
  */
-function system_modules($form_values = NULL) {
+function system_modules($form_state = array()) {
   // Get current list of modules.
   $files = module_rebuild_cache();
-  if ($confirm_form = system_modules_confirm_form($files, $form_values)) {
-    return $confirm_form;
+  if (!empty($form_state['storage'])) {
+    return system_modules_confirm_form($files, $form_state['storage']);
   }
+  $dependencies = array();
 
   // Store module list for validation callback.
   $form['validation_modules'] = array('#type' => 'value', '#value' => $files);
@@ -1449,9 +1454,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
@@ -1462,9 +1468,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')),
     );
   }
 
@@ -1472,7 +1479,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;
@@ -1481,43 +1487,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
@@ -1557,22 +1553,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();
@@ -1624,12 +1626,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;
 }
 
 /**
@@ -1700,12 +1706,12 @@ function theme_system_modules($form) {
  * @return
  *   A form array representing the currently disabled modules.
  */
-function system_modules_uninstall($form_values = NULL) {
+function system_modules_uninstall($form_state = NULL) {
   // Make sure the install API is available.
   include_once './includes/install.inc';
 
   // Display the confirm form if any modules have been submitted.
-  if ($confirm_form = system_modules_uninstall_confirm_form($form_values)) {
+  if (isset($form_state) && $confirm_form = system_modules_uninstall_confirm_form($form_state['storage'])) {
     return $confirm_form;
   }
 
@@ -1735,10 +1741,9 @@ function system_modules_uninstall($form_
       '#options' => $options,
     );
     $form['buttons']['submit'] = array(
-      '#type' => 'button',
+      '#type' => 'submit',
       '#value' => t('Uninstall'),
     );
-    $form['#multistep'] = TRUE;
     $form['#action'] = url('admin/build/modules/uninstall/confirm');
   }
   else {
@@ -1756,14 +1761,14 @@ function system_modules_uninstall($form_
  * @return
  *   A form array representing modules to confirm.
  */
-function system_modules_uninstall_confirm_form($form_values) {
+function system_modules_uninstall_confirm_form($storage) {
   // Nothing to build.
-  if (!isset($form_values)) {
+  if (!isset($storage)) {
     return;
   }
 
   // Construct the hidden form elements and list items.
-  foreach (array_filter($form_values['uninstall']) as $module => $value) {
+  foreach (array_filter($storage['uninstall']) as $module => $value) {
     $info = drupal_parse_info_file(dirname(drupal_get_filename('module', $module)) .'/'. $module .'.info');
     $uninstall[] = $info['name'];
     $form['uninstall'][$module] = array('#type' => 'hidden',
@@ -1773,8 +1778,8 @@ function system_modules_uninstall_confir
 
   // Display a confirm form if modules have been selected.
   if (isset($uninstall)) {
+    $form['#confirmed'] = TRUE;
     $form['uninstall']['#tree'] = TRUE;
-    $form['#multistep'] = TRUE;
     $form['modules'] = array('#value' => '<p>'. t('The following modules will be completely uninstalled from your site, and <em>all data from these modules will be lost</em>!') .'</p>'. theme('item_list', $uninstall));
     $form = confirm_form(
       $form,
@@ -1836,7 +1841,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');
@@ -1852,16 +1857,23 @@ 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';
 
-  // Call the uninstall routine for each selected module.
-  foreach (array_filter($form_values['uninstall']) as $module => $value) {
-    drupal_uninstall_module($module);
+  if (!empty($form['#confirmed'])) {
+    // Call the uninstall routine for each selected module.
+    foreach (array_filter($form_values['uninstall']) as $module => $value) {
+      drupal_uninstall_module($module);
+    }
+    drupal_set_message(t('The selected modules have been uninstalled.'));
+
+    unset($form_state['storage']);
+    $form_state['redirect'] = 'admin/build/modules/uninstall';
+  }
+  else {
+    $form_state['storage'] = $form_values;
   }
-  drupal_set_message(t('The selected modules have been uninstalled.'));
-  drupal_goto('admin/build/modules/uninstall');
 }
 
 /**
@@ -2401,14 +2413,14 @@ function theme_admin_block_content($cont
         $item['attributes'] = array();
       }
       $item['attributes'] += array('title' => $item['description']);
-      $output .= '<li class="leaf">'. l($item['title'], $item['path'], $item) .'</li>';
+      $output .= '<li class="leaf">'. l($item['title'], $item['href'], $item['options']) .'</li>';
     }
     $output .= '</ul>';
   }
   else {
     $output = '<dl class="admin-list">';
     foreach ($content as $item) {
-      $output .= '<dt>'. l($item['title'], $item['path'], $item) .'</dt>';
+      $output .= '<dt>'. l($item['title'], $item['href'], $item['options']) .'</dt>';
       $output .= '<dd>'. $item['description'] .'</dd>';
     }
     $output .= '</dl>';
Index: modules/taxonomy/taxonomy.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v
retrieving revision 1.354
diff -u -F^f -r1.354 taxonomy.module
--- modules/taxonomy/taxonomy.module	4 May 2007 08:38:34 -0000	1.354
+++ modules/taxonomy/taxonomy.module	12 May 2007 04:30:36 -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,9 @@ function taxonomy_form_vocabulary_submit
       break;
   }
 
-  return 'admin/content/taxonomy';
+  $form_state['vid'] = $form_values['vid'];
+  $form_state['redirect'] = 'admin/content/taxonomy';
+  return;
 }
 
 function taxonomy_save_vocabulary(&$edit) {
@@ -391,11 +393,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 +479,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 +490,10 @@ 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['tid'] = $form_values['tid'];
+  $form_state['redirect'] = 'admin/content/taxonomy';
+  return;
 }
 
 /**
@@ -623,11 +629,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.75
diff -u -F^f -r1.75 throttle.module
--- modules/throttle/throttle.module	30 Apr 2007 17:03:29 -0000	1.75
+++ modules/throttle/throttle.module	12 May 2007 04:30:36 -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/tracker/tracker.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/tracker/tracker.module,v
retrieving revision 1.147
diff -u -F^f -r1.147 tracker.module
--- modules/tracker/tracker.module	30 Apr 2007 17:03:29 -0000	1.147
+++ modules/tracker/tracker.module	12 May 2007 04:30:36 -0000
@@ -40,7 +40,6 @@ function tracker_menu() {
     'title' => 'My recent posts',
     'type' => MENU_LOCAL_TASK,
     'access callback' => 'user_is_logged_in',
-    'access arguments' => array(1),
   );
 
   $items['user/%user/track'] = array(
Index: modules/upload/upload.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/upload/upload.module,v
retrieving revision 1.159
diff -u -F^f -r1.159 upload.module
--- modules/upload/upload.module	30 Apr 2007 17:03:29 -0000	1.159
+++ modules/upload/upload.module	12 May 2007 04:30:36 -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.'));
@@ -326,7 +326,7 @@ function _upload_prepare(&$node) {
     // Scale image uploads.
     $file = _upload_image($file);
 
-    $key = 'upload_'. (isset($_SESSION['file_previews']) ? 0 : count($_SESSION['file_previews']));
+    $key = 'upload_'. (!isset($_SESSION['file_previews']) ? 0 : count($_SESSION['file_previews']));
     $file->fid = $key;
     $file->source = $key;
     $file->list = variable_get('upload_list_default', 1);
@@ -813,7 +813,13 @@ function _upload_form($node) {
       '#suffix' => '</div>',
     );
     $form['new']['upload'] = array('#type' => 'file', '#title' => t('Attach new file'), '#size' => 40);
-    $form['new']['attach'] = array('#type' => 'button', '#value' => t('Attach'), '#name' => 'attach', '#id' => 'attach-button');
+    $form['new']['attach'] = array(
+      '#type' => 'submit',
+      '#value' => t('Attach'),
+      '#name' => 'attach',
+      '#id' => 'attach-button',
+      '#submit' => array(),
+    );
     // The class triggers the js upload behaviour.
     $form['attach-url'] = array('#type' => 'hidden', '#value' => url('upload/js', array('absolute' => TRUE)), '#attributes' => array('class' => 'upload'));
   }
@@ -872,7 +878,7 @@ function _upload_image($file) {
   $info = image_get_info($file->filepath);
 
   if ($info) {
-    list($width, $height) = explode('x', variable_get('upload_max_resolution', 0));
+    list($width, $height) = explode('x', variable_get('upload_max_resolution', '0x0'));
     if ($width && $height) {
       $result = image_scale($file->filepath, $file->filepath, $width, $height);
       if ($result) {
@@ -906,9 +912,9 @@ function upload_js() {
     '#tree' => FALSE,
     '#parents' => array(),
   );
-  $GLOBALS['form_button_counter'] = array(0, 0);
   drupal_alter('form', $form, 'upload_js');
-  $form = form_builder('upload_js', $form);
+  $form_state = array('submitted' => FALSE);
+  $form = form_builder('upload_js', $form, $form_state);
   $output = theme('status_messages') . drupal_render($form);
   // We send the updated file attachments form.
   print drupal_to_js(array('status' => TRUE, 'data' => $output));
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.779
diff -u -F^f -r1.779 user.module
--- modules/user/user.module	10 May 2007 19:55:23 -0000	1.779
+++ modules/user/user.module	12 May 2007 04:30:36 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: user.module,v 1.779 2007/05/10 19:55:23 dries Exp $
+// $Id: user.module,v 1.779 2007/05/10 10:00:50 dries Exp $
 
 /**
  * @file
@@ -313,35 +313,34 @@ function user_validate_mail($mail) {
   }
 }
 
-function user_validate_picture($file, &$edit, $user) {
-  global $form_values;
-  // Initialize the picture:
-  $form_values['picture'] = $user->picture;
-
-  // Check that uploaded file is an image, with a maximum file size
-  // and maximum height/width.
-  $info = image_get_info($file->filepath);
-  list($maxwidth, $maxheight) = explode('x', variable_get('user_picture_dimensions', '85x85'));
-
-  if (!$info || !$info['extension']) {
-    form_set_error('picture_upload', t('The uploaded file was not an image.'));
-  }
-  else if (image_get_toolkit()) {
-    image_scale($file->filepath, $file->filepath, $maxwidth, $maxheight);
-  }
-  else if (filesize($file->filepath) > (variable_get('user_picture_file_size', '30') * 1000)) {
-    form_set_error('picture_upload', t('The uploaded image is too large; the maximum file size is %size kB.', array('%size' => variable_get('user_picture_file_size', '30'))));
-  }
-  else if ($info['width'] > $maxwidth || $info['height'] > $maxheight) {
-    form_set_error('picture_upload', t('The uploaded image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'))));
-  }
+function user_validate_picture(&$form_values, $form, &$form_state) {
+  // If required, validate the uploaded picture.
+  if (isset($form['picture']) && ($file = file_check_upload('picture_upload'))) {
+    // Check that uploaded file is an image, with a maximum file size
+    // and maximum height/width.
+    $info = image_get_info($file->filepath);
+    list($maxwidth, $maxheight) = explode('x', variable_get('user_picture_dimensions', '85x85'));
 
-  if (!form_get_errors()) {
-    if ($file = file_save_upload('picture_upload', variable_get('user_picture_path', 'pictures') .'/picture-'. $user->uid .'.'. $info['extension'], 1)) {
-      $form_values['picture'] = $file->filepath;
+    if (!$info || !$info['extension']) {
+      form_set_error('picture_upload', t('The uploaded file was not an image.'));
     }
-    else {
-      form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist or is not writable.", array('%directory' => variable_get('user_picture_path', 'pictures'))));
+    else if (image_get_toolkit()) {
+      image_scale($file->filepath, $file->filepath, $maxwidth, $maxheight);
+    }
+    else if (filesize($file->filepath) > (variable_get('user_picture_file_size', '30') * 1000)) {
+      form_set_error('picture_upload', t('The uploaded image is too large; the maximum file size is %size kB.', array('%size' => variable_get('user_picture_file_size', '30'))));
+    }
+    else if ($info['width'] > $maxwidth || $info['height'] > $maxheight) {
+      form_set_error('picture_upload', t('The uploaded image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'))));
+    }
+
+    if (!form_get_errors()) {
+      if ($file = file_save_upload('picture_upload', variable_get('user_picture_path', 'pictures') .'/picture-'. $form['#uid'] .'.'. $info['extension'], 1)) {
+        $form_values['picture'] = $file->filepath;
+      }
+      else {
+        form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist or is not writable.", array('%directory' => variable_get('user_picture_path', 'pictures'))));
+      }
     }
   }
 }
@@ -522,8 +521,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'),
@@ -661,7 +660,7 @@ function user_block($op = 'list', $delta
 
 function theme_user_picture($account) {
   if (variable_get('user_pictures', 0)) {
-    if ($account->picture && file_exists($account->picture)) {
+    if (!empty($account->picture) && file_exists($account->picture)) {
       $picture = file_create_url($account->picture);
     }
     else if (variable_get('user_picture_default', '')) {
@@ -967,7 +966,7 @@ function user_init() {
 }
 
 function user_current_load($arg) {
-  return user_load($arg);
+  return $arg ? user_load($arg) : user_load($GLOBALS['user']->uid);
 }
 
 function user_current_to_arg() {
@@ -1056,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
@@ -1077,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));
@@ -1088,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;
   }
 }
 
@@ -1171,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));
@@ -1180,14 +1180,14 @@ function user_pass_validate($form_id, $f
     $account = user_load(array('name' => $name, 'status' => 1));
   }
   if (isset($account->uid)) {
-    form_set_value(array('#parents' => array('account')), $account);
+    form_set_value(array('#parents' => array('account')), $account, $form_state);
   }
   else {
     form_set_error('name', t('Sorry, %name is not recognized as a user name or an e-mail address.', array('%name' => $name)));
   }
 }
 
-function user_pass_submit($form_id, $form_values) {
+function user_pass_submit($form_values, $form, &$form_state) {
   global $base_url;
 
   $account = $form_values['account'];
@@ -1207,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;
 }
 
 /**
@@ -1326,15 +1327,16 @@ function user_register() {
     $form = array_merge($form, $extra);
   }
   $form['submit'] = array('#type' => 'submit', '#value' => t('Create new account'), '#weight' => 30);
+  $form['#validate'][] = 'user_register_validate';
 
   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');
 
@@ -1357,7 +1359,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']);
@@ -1368,6 +1371,8 @@ function user_register_submit($form_id, 
     $merge_data['status'] = variable_get('user_register', 1) == 1;
   }
   $account = user_save('', array_merge($form_values, $merge_data));
+  $form_state['user'] = $account;
+
   watchdog('user', 'New user: %name (%email).', array('%name' => $name, '%email' => $mail), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit'));
 
   $variables = array('!username' => $name, '!site' => variable_get('site_name', 'Drupal'), '!password' => $pass, '!uri' => $base_url, '!uri_brief' => substr($base_url, strlen('http://')), '!mailto' => $mail, '!date' => format_date(time()), '!login_uri' => url('user', array('absolute' => TRUE)), '!edit_uri' => url('user/'. $account->uid .'/edit', array('absolute' => TRUE)), '!login_url' => user_pass_reset_url($account));
@@ -1378,9 +1383,11 @@ function user_register_submit($form_id, 
     if (variable_get('user_email_verification', TRUE)) {
       drupal_set_message(t('</p><p> Your password is <strong>%pass</strong>. You may change your password below.</p>', array('%pass' => $pass)));
     }
+
     user_authenticate($account->name, trim($pass));
 
-    return 'user/1/edit';
+    $form_state['redirect'] = 'user/1/edit';
+    return;
   }
   else {
     if ($admin && !$notify) {
@@ -1392,7 +1399,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.
@@ -1406,7 +1414,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 {
@@ -1498,7 +1507,9 @@ function user_edit_form($uid, $edit, $re
       $form['picture']['picture_delete'] = array('#type' => 'hidden');
     }
     $form['picture']['picture_upload'] = array('#type' => 'file', '#title' => t('Upload picture'), '#size' => 48, '#description' => t('Your virtual face or picture. Maximum dimensions are %dimensions and the maximum size is %size kB.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'), '%size' => variable_get('user_picture_file_size', '30'))) .' '. variable_get('user_picture_guidelines', ''));
+    $form['#validate'][] = 'user_validate_picture';
   }
+  $form['#uid'] = $uid;
 
   return $form;
 }
@@ -1528,11 +1539,6 @@ function _user_edit_validate($uid, &$edi
   else if (drupal_is_denied('mail', $edit['mail'])) {
     form_set_error('mail', t('The e-mail address %email has been denied access.', array('%email' => $edit['mail'])));
   }
-
-  // If required, validate the uploaded picture.
-  if ($file = file_check_upload('picture_upload')) {
-    user_validate_picture($file, $edit, $user);
-  }
 }
 
 function _user_edit_submit($uid, &$edit) {
@@ -1587,6 +1593,7 @@ function user_edit($category = 'account'
     $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'), '#weight' => 31);
   }
   $form['#attributes']['enctype'] = 'multipart/form-data';
+  $form['#validate'][] = 'user_edit_validate';
 
   drupal_set_title(check_plain($account->name));
   return $form;
@@ -1618,7 +1625,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']))) {
@@ -1628,7 +1635,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']);
@@ -1639,7 +1646,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) {
@@ -1701,8 +1709,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;
 }
@@ -1712,8 +1720,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;
 }
@@ -1723,8 +1731,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;
 }
@@ -1739,13 +1747,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'])) {
@@ -1816,10 +1824,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;
 }
 
 /**
@@ -2008,7 +2017,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)) {
@@ -2025,8 +2034,8 @@ function user_admin_perm_submit($form_id
 
   drupal_set_message(t('The changes have been saved.'));
 
-  // Clear the cached pages and menus:
-  menu_rebuild();
+  // Clear the cached pages
+  cache_clear_all();
 
 }
 
@@ -2073,13 +2082,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']))) {
@@ -2097,7 +2106,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.'));
@@ -2114,7 +2123,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) {
@@ -2248,7 +2258,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.
@@ -2267,7 +2277,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.'));
@@ -2419,14 +2429,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() {
@@ -2787,7 +2798,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) {
@@ -2811,7 +2822,8 @@ function user_filter_form_submit($form_i
       return;
   }
 
-  return 'admin/user/user';
+  $form_state['redirect'] = 'admin/user/user';
+  return;
 }
 
 
