? missing_semi1981.patch
? multiple_menus_0.patch
? separate_menu_tables_1.patch
? separate_menu_tables_2.patch
? separate_menu_tables_3.patch
? separate_menu_tables_4.patch
? separate_menu_tables_5.patch
Index: index.php
===================================================================
RCS file: /cvs/drupal/drupal/index.php,v
retrieving revision 1.93
diff -u -p -r1.93 index.php
--- index.php	6 Apr 2007 13:27:20 -0000	1.93
+++ index.php	29 Apr 2007 16:08:45 -0000
@@ -11,7 +11,7 @@
 
 require_once './includes/bootstrap.inc';
 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
-
+menu_rebuild();
 $return = menu_execute_active_handler();
 
 // Menu status constants are integers; page content is a string.
? includes/foo_menu.inc
? sites/rosalind.local
Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.163
diff -u -p -r1.163 menu.inc
--- includes/menu.inc	24 Apr 2007 08:26:58 -0000	1.163
+++ includes/menu.inc	29 Apr 2007 16:08:46 -0000
@@ -311,9 +311,24 @@ function menu_get_item($path = NULL, $it
     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.
+      $map = _menu_translate($item, $original_map);
+      if ($map === FALSE) {
+        $items[$path] = FALSE;
+        return FALSE;
+      }
+      if ($item->access) {
+        $item->map = $map;
+        $item->page_arguments = array_merge(menu_unserialize($item->page_arguments, $map), array_slice($parts, $item->number_parts));
+      }
+    }
+    $items[$path] = $item;
+  }
+  return $items[$path];
+      
+  // TODO breadcrumb building
       $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);
+      $result = db_query('SELECT * FROM {menu_links} 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);
@@ -324,18 +339,12 @@ function menu_get_item($path = NULL, $it
         if ($parent->access) {
           $item->active_trail[] = $parent;
         }
+/* this doesn't really make sense with multiple menus
         else {
           $item->access = FALSE;
-        }
-      }
-      if ($item->access) {
-        $item->map = $map;
-        $item->page_arguments = array_merge(menu_unserialize($item->page_arguments, $map), array_slice($parts, $item->number_parts));
+        }*/
       }
-    }
-    $items[$path] = $item;
-  }
-  return $items[$path];
+
 }
 
 /**
@@ -364,57 +373,57 @@ function menu_execute_active_handler() {
  *   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->load_functions. 
  *   $item->access becomes TRUE if the item is accessible, FALSE otherwise.
+ *   $item->link_path is set, or altered if there is a to_arg function.
   */
-function _menu_translate(&$item, $map, $operation = MENU_HANDLE_REQUEST) {
+function _menu_translate(&$item, $map) {
   // 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.
-  if ($item->load_functions) {
+  $path_map = explode('/', $item->path);
+  foreach($map as $i => $part) {
+    $path_map[$i] = $part;
+  }
+  
+  if (!empty($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]);
-          }
+      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 {
-          $path_map[$index] = isset($map[$index]) ? $map[$index] : '';
+          unset($path_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] : '');
-        // If callback returned an error or there is no callback, trigger 404.
-        if ($return === FALSE) {
-          $item->access = FALSE;
-          return FALSE;
-        }
-        $map[$index] = $return;
+      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] : '');
+      // If callback returned an error or there is no callback, trigger 404.
+      if ($return === FALSE) {
+        $item->access = 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));
   }
+  elseif (!empty($item->external)) {
+    $item->access = 1;
+  }
+  // Re-join the path including any replacement values
+  $item->link_path = implode('/', $path_map);
+
   // Determine access callback, which will decide whether or not the current user has
   // access to this path.
   $callback = $item->access_callback;
@@ -433,25 +442,31 @@ function _menu_translate(&$item, $map, $
       $item->access = call_user_func_array($callback, $arguments);
     }
   }
-  $item->alias = TRUE;
   return $map;
 }
 
+function _menu_check_access($item, $map) {  
+ 
+  return $access;
+}
+
 /**
  * Returns a rendered menu tree.
  */
-function menu_tree() {
+function menu_tree($menu_name = 'navigation') {
   if ($item = menu_get_item()) {
     if ($item->access) {
-      $args = explode(',', $item->parents);
+      $parents = db_result(db_query("SELECT parents FROM {menu_links} WHERE menu_name = '%s' AND path = '%s'", $menu_name, $item->path));
+      $args = explode(',', $parents);
+      array_unshift($args, $menu_name);
       $placeholders = implode(', ', array_fill(0, count($args), '%d'));
     }
     // Show the root menu for access denied.
     else {
-      $args = 0;
+      $args = array('navigation', 0);
       $placeholders = '%d';
     }
-    list(, $menu) = _menu_tree(db_query('SELECT * FROM {menu} WHERE pid IN ('. $placeholders .') AND visible = 1 ORDER BY mleft', $args));
+    list(, $menu) = _menu_tree(db_query("SELECT * FROM {menu_links} ml LEFT JOIN {menu} m ON m.path = ml.path WHERE ml.menu_name = '%s' AND ml.pid IN ('. $placeholders .') AND disabled = 0 ORDER BY mleft", $args));
     return $menu;
   }
 }
@@ -476,22 +491,21 @@ function menu_tree() {
  *   is the rendered HTML of the children.
  */
 function _menu_tree($result = NULL, $depth = 0, $link = '', $has_children = FALSE) {
-  static $map;
+
   $remnant = NULL;
   $tree = '';
   // Fetch the current path and cache it.
-  if (!isset($map)) {
-    $map = arg(NULL);
-  }
+
   while ($item = db_fetch_object($result)) {
+
+    $map = $item->external ? array() : explode('/', $item->link_path);
+    
     // Access check and handle dynamic path translation.
     _menu_translate($item, $map, MENU_RENDER_LINK);
     if (!$item->access) {
       continue;
     }
-    if ($item->attributes) {
-      $item->attributes = unserialize($item->attributes);
-    }
+    $item->options = unserialize($item->options);
     // 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.
@@ -529,9 +543,9 @@ function _menu_tree($result = NULL, $dep
 /**
  * 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_text, $link->link_path, $link->options);
 }
 
 /**
@@ -582,78 +596,95 @@ 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;
+function menu_get_names($reset = FALSE) {
+  static $names;
+  // TODO - use cache system to save this
+  
+  if (!$reset || empty($names)) {
+    $names = module_invoke_all('menu_info');
+    $names['navigation'] = array(
+      'customizable' => TRUE, 
+      'title' => t('Navigation'), 
+      'title callback' => 'check_plain', 
+      'callback arguments' => array('$user->name'),
+      'description' => t('Main navigation menu'), // What's the existing t() string?
+    );
+  
+    foreach ($names as $key => $data) {
+      if (!isset($data['customizable'])) {
+        $names[$key]['customizable'] = TRUE;
+      }
+      if (!isset($data['title callback'])) {
+        $names[$key]['title callback'] = FALSE;
+      }
+      if (!isset($data['callback arguments'])) {
+        $names[$key]['callback arguments'] = array();
+      }
+    }
+  }
+  return $names;
+}
 
+
+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);
+}
+
+function _menu_router_build($callbacks) {
   // First pass: separate callbacks from pathes, making pathes ready for
   // matching. Calculate fitness, and fill some default values.
-  foreach ($menu as $path => $item) {
+  $menu = array();
+  foreach ($callbacks 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;
+
+    $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;
           }
-          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 (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;
+      if ($match) {
+        $parts[$k] = '%';
       }
       else {
-        // If there is no %, it fits maximally.
-        $fit = (1 << $number_parts) - 1;
+        $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(
@@ -663,8 +694,6 @@ function menu_rebuild() {
       '_number_parts' => $number_parts,
       '_parts' => $parts,
       '_fit' => $fit,
-      '_mid' => $mid++,
-      '_children' => array(),
     );
     $item += array(
       '_visible' => (bool)($item['type'] & MENU_VISIBLE_IN_TREE),
@@ -672,201 +701,269 @@ function menu_rebuild() {
     );
     if ($move) {
       $new_path = implode('/', $item['_parts']);
-      unset($menu[$path]);
+      $menu[$new_path] = $item;
     }
     else {
-      $new_path = $path;
+      $menu[$path] = $item;
     }
-    $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) {
+  // Apply inheritance rules.
+  foreach ($menu as $path => $v) {
     $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 (!isset($item['access callback']) && isset($item['access arguments'])) {
+      $item['access callback'] = 'user_access'; // Default callback
     }
-    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'];
+    if (!$item['_tab']) {
+      // Non-tab items 
+      $item['parent'] = '';
+    }
+    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 (0 && $parent_path == 'admin/logs') {
+          drupal_set_message($path ." ".print_r($parent,1));
         }
-        else {
-          $parent_path = substr($parent_path, 0, strrpos($parent_path, '/'));
+        if (empty($item['parent'])) {
+          // parent stores the parent of the path.
+          $item['parent'] = $parent_path;
+        }
+        // If a callback is not found, we try to find the first parent that
+        // has a callback. When found, its callback argument will also be
+        // copied.
+        if (!isset($item['access callback']) && isset($parent['access callback'])) {
+          $item['access callback'] = $parent['access callback'];
+          $item['access arguments'] = isset($parent['access arguments']) ? $parent['access arguments'] : array();
         }
-        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;
+        // Same for page callbacks, except preserve arguments.
+        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'];
           }
         }
       }
     }
-    $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,
+    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']);
+    }
+    
+    $insert_item = $item;
+    $item = NULL;
+    $item = $insert_item + array(
+      'access arguments' => array(),
+      'access callback' => '',
+      'page arguments' => array(),
+      'page callback' => '',
+      'block callback' => '',
+      'description' => '',
+      'position' => '',
     );
-    // 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);
+    db_query("INSERT INTO {menu} (
+      path, load_functions, to_arg_functions,
+      access_callback, access_arguments, page_callback, page_arguments, fit,
+      number_parts, parent,tab_depth, title, 
+      type, block_callback, description, position)
+      VALUES ('%s', '%s', '%s', 
+      '%s', '%s', '%s', '%s', %d, 
+      %d, '%s', %d, '%s',
+      %d, '%s', '%s', '%s')",
+      $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['parent'], $item['_tab'] ? $item['_number_parts'] : 0,
+      $item['title'],$item['type'], $item['block callback'], $item['description'], $item['position']);
   }
-  array_multisort($sort, $menu);
+  return $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);
+/**
+ * For the items in a given named menu, find the access callback and other properties for each item.
+ */
+function _menu_links_build(&$ml, $menu) {
+  static $mid = 1;
+  
+  foreach ($ml as $path => $v) {
+    $item = &$ml[$path];
 
-  // Apply inheritance rules.
-  foreach ($menu as $path => $item) {
+    $item['_mid'] = $mid++;
+    if (!isset($item['_external'])) {
+      $item['_external'] = menu_path_is_external($path);
+    }
     if ($item['_external']) {
-      $item['access callback'] = 1;
+      $item['path'] = '';
     }
     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;
-            }
-          }
+      $item['parts'] = explode('/', $path, 6);
+      $item['number_parts'] = count($item['parts']);
+      if (isset($menu[$path])) { // Perfect match, no need to search
+        $item['path'] = $path;
+      }
+      else {
+        list($ancestors, $placeholders) = menu_get_ancestors($item['parts']);
+        array_shift($ancestors);
 
-          // 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'];
-            }
+        while($path = array_shift($ancestors) && !isset($item['access callback'])) {
+          if (isset($menu[$path])) {
+            $item['path'] = $path;
           }
         }
       }
-      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;
-      }
     }
+    // Item is disabled if so set, or if no menu path is found
+    $item['disabled'] = (isset($item['disabled']) && $item['disabled']) || !isset($item['path']);
+  }
+}
 
-    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));
+
+/**
+ * For the items in a given named menu, find the parents and prepare to sort.
+ */
+function _menu_links_find_parents(&$ml) {
+
+  $sort = array();
+
+  foreach ($ml as $path => $v) {
+    $item = &$ml[$path];
+  // Second pass: prepare for sorting and find parents.
+
+    $parents = array($item['_mid']);
+    $depth = 1;
+    $p = $path;
+
+    while ($p) {
+      $parent_path = isset($ml[$p]['parent']) ? $ml[$p]['parent'] : '';
+      if (isset($ml[$parent_path])) {
+        $parent = $ml[$parent_path];
+        $parents[] = $parent['_mid'];
+        $depth++;
+        if (!isset($item['_pid'])) {
+          $item['_pid'] = $parent['_mid'];
+          $item['_visible_parent_path'] = $parent_path;
+        }
       }
+      $p = $parent_path;
     }
-    else {
-      // Non-tab items specified the parent for visible links, and it's
-      // stored in parents, parent stores the tab parent.
-      $item['parent'] = $path;
+    
+    $parents[] = 0;
+    $parents = implode(',', array_reverse($parents));
+    // Store variables and set defaults.
+    $item += array(
+      'weight' => 0,
+      'title' => '',
+      '_pid' => 0,
+      '_depth' => $depth,
+      '_parents' => $parents,
+      //'_slashes' => $slashes,
+      '_children' => array(),
+       'has_children' => 0,
+    );
+    // 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'];
+    
+  }
+  array_multisort($sort, $ml);
+}
+
+function _menu_links_build_tree(&$ml) {
+
+  $children = array();
+
+  // We are now sorted, so let's build the tree.  
+  foreach ($ml as $path => $item) {
+
+    if (!empty($item['_pid'])) {
+      $ml[$item['parent']]['_children'][] = $path;
+      $ml[$item['parent']]['has_children'] = TRUE;
     }
+  }
+  menu_renumber($ml);
+}
 
-    $insert_item = $item;
-    unset($item);
-    $item = $insert_item + array(
-      'access arguments' => array(),
-      'access callback' => '',
-      'page arguments' => array(),
-      'page callback' => '',
+function _menu_links_save($menu_name, $ml) {
+  
+  foreach ($ml as $path => $item) {
+    // set additional defaults 
+    $item += array(
       '_mleft' => 0,
       '_mright' => 0,
-      'block callback' => '',
-      'description' => '',
-      'position' => '',
-      'attributes' => '',
-      'query' => '',
-      'fragment' => '',
-      'absolute' => '',
-      'html' => '',
+      'title' => '',
+      'options' => array(),
+      'link_path' => '',
     );
-    $link_path = $item['to_arg_functions'] ? $path : drupal_get_path_alias($path);
-
-    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, 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', %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['_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']);
+    db_query("INSERT INTO {menu_links} (
+      menu_name, mid, pid, link_path, disabled,
+      external, path, parents, depth, has_children,
+      mleft, mright, link_text, options)
+      VALUES ('%s', %d, %d, '%s', %d,
+      %d, '%s', '%s', %d, %d, 
+      %d, %d, '%s', '%s')",
+      $menu_name, $item['_mid'], $item['_pid'], $path, $item['disabled'], 
+      $item['_external'], $item['path'], $item['_parents'],
+      $item['_depth'], $item['has_children'], 
+      $item['_mleft'], $item['_mright'], $item['link_text'], serialize($item['options']));    
   }
 }
 
+/**
+ * Populate the database representation of the menu.
+ */
+function menu_rebuild() {
+
+  $callbacks = module_invoke_all('menu');
+  $names = menu_get_names(TRUE);
+
+  foreach ($callbacks as $path => $item) {
+    if (!isset($item['menu name']) || !isset($names[$item['menu name']])) {
+      $callbacks[$path]['menu name'] = 'navigation'; // The default menu
+    }
+  }
+  
+  // Alter the menu as defined in modules, keys are like user/%user.
 
+  db_query('DELETE FROM {menu}');
+  db_query('DELETE FROM {menu_links}');
+  
+  drupal_alter('menu', $callbacks);
+  $menu = _menu_router_build($callbacks);
+
+  $ml = array(); // replace later ml = menu_links
+  foreach($names as $key => $n) {
+    $ml[$key] = array();
+  }
+  
+  // Add normal and suggested items
+  foreach($menu as $path => $item) {
+    if ($item['type'] & MENU_MODIFIABLE_BY_ADMIN) {
+      $item['disabled'] = !$item['_visible'];
+      $item['link_text'] = $item['title'];
+      $ml[$item['menu name']][$path] = $item;
+    }
+  }
+  //TODO build admin page here as separate sets of menu links?
+
+ // Alter the menu links derived from module-defined callbacks.
+ // This.is where menu.module will do its work adding and rearranging links
+  drupal_alter('menu_links', $ml, $menu);
+
+  foreach($names as $key => $n) {
+    _menu_links_build($ml[$key], $menu);
+    _menu_links_find_parents($ml[$key]);
+    // TODO mleft mright children
+    _menu_links_build_tree($ml[$key]);
+    _menu_links_save($key, $ml[$key]); 
+  }
+  return;  
+}
 
 function menu_renumber(&$tree) {
   foreach ($tree as $key => $element) {
@@ -914,7 +1011,7 @@ function menu_local_tasks($level = 0) {
       // 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;
+        $parents[] = $router_item->path;
         // Do not add the same item twice.
         $router_item->parent = '';
       }
@@ -924,20 +1021,20 @@ function menu_local_tasks($level = 0) {
         continue;
       }
       // This loads all the tabs.
-      $result = db_query("SELECT * FROM {menu} WHERE parent = '%s' AND tab = 1 ORDER BY mleft", $parent);
+      $result = db_query("SELECT * FROM {menu} WHERE parent = '%s' AND tab_depth != 0", $parent); // ORDER BY mleft
       $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);
+         _menu_translate($item, $map);
         if ($item->access) {
-          $depth = $item->depth;
-          $link = l($item->title, $item->link_path, (array)$item);
+          $depth = $item->tab_depth;
+          $link = l($item->title, $item->link_path); // 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 == $router_item->path || (!$router_item->tab_depth && $item->type == MENU_DEFAULT_LOCAL_TASK)) {
             $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));
+            $next_router_item = db_fetch_object(db_query("SELECT path, tab_depth, parent FROM {menu} WHERE path = '%s'", $item->parent));
             // We will need to inspect one level down.
             $parents[] = $item->path;
           }
@@ -979,7 +1076,7 @@ function menu_set_location() {
 }
 
 function menu_get_active_breadcrumb() {
-  $breadcrumb = array(l(t('Home'), ''));
+  return  $breadcrumb = array(l(t('Home'), '')); // TODO!
   $item = menu_get_item();
   if ($item && $item->access) {
     foreach ($item->active_trail as $parent) {
@@ -991,11 +1088,14 @@ function menu_get_active_breadcrumb() {
 
 function menu_get_active_title() {
   $item = menu_get_item();
+  return $item->title;
+  // TODO
+  /*
   foreach (array_reverse($item->active_trail) as $item) {
     if (!($item->type & MENU_IS_LOCAL_TASK)) {
       return $item->title;
     }
-  }
+  }*/
 }
 
 /**
@@ -1009,7 +1109,7 @@ function menu_get_active_title() {
  *   rendering.
  */
 function menu_get_item_by_mid($mid) {
-  if ($item = db_fetch_object(db_query('SELECT * FROM {menu} WHERE mid = %d', $mid))) {
+  if ($item = db_fetch_object(db_query('SELECT * FROM {menu_links} WHERE mid = %d', $mid))) {
     _menu_translate($item, arg(), MENU_RENDER_LINK);
     if ($item->access) {
       return $item;
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.99
diff -u -p -r1.99 system.install
--- modules/system/system.install	25 Apr 2007 21:34:32 -0000	1.99
+++ modules/system/system.install	29 Apr 2007 16:08:46 -0000
@@ -327,8 +327,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 '',
@@ -338,32 +336,36 @@ 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,
+        parent varchar(255) NOT NULL default '',
+        tab_depth int NOT NULL default 0,
         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),
-        KEY fit (fit),
-        KEY visible (visible),
-        KEY pid (pid),
-        KEY parent (parent)
+        KEY fit (fit)
+      ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
+
+      db_query("CREATE TABLE {menu_links} (
+        menu_name varchar(64) NOT NULL default '',
+        mid int NOT NULL default 0,
+        pid int NOT NULL default 0,
+        link_path varchar(255) NOT NULL default '',
+        path varchar(255) NOT NULL default '',
+        link_text varchar(255) NOT NULL default '',
+        disabled smallint NOT NULL default 0,
+        external smallint NOT NULL default 0,
+        mleft int NOT NULL default 0,
+        mright int NOT NULL default 0,
+        parents varchar(255) NOT NULL default '',
+        depth int NOT NULL default 0,
+        has_children int NOT NULL default 0,
+        options text,
+        PRIMARY KEY  (menu_name, path),
+        KEY pid (pid)
       ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
 
       db_query("CREATE TABLE {node} (
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.471
diff -u -p -r1.471 system.module
--- modules/system/system.module	24 Apr 2007 19:49:01 -0000	1.471
+++ modules/system/system.module	29 Apr 2007 16:08:47 -0000
@@ -374,6 +374,57 @@ function system_user($type, $edit, &$use
 }
 
 /**
+ * Implementation of hook_menu_info()
+ *
+ * Decalares menus for the administration overview page.
+ */
+function system_menu_info() {
+  $callbacks = module_invoke_all('menu');
+
+  $admin = array();
+  $exclude = array('compact', 'by-task', 'by-module');
+  
+  foreach ($callbacks as $path => $item) {
+    $parts = explode('/', $path);
+    if (count($parts) == 2 && $parts[0] == 'admin' && !in_array($parts[1], $exclude)) {
+      if (!isset($item['description'])) { 
+        $item['description']= ''; 
+      }
+      $admin[$path] = array('customizable' => FALSE, 'title' => $item['title'], 'description' => $item['description']);
+    }
+  }
+  
+  variable_set('system_admin_menus', $admin);
+  
+  return $admin;
+}
+
+/**
+ * Implementation of hook_menu_links_alter()
+ *
+ * Adds items to administration overview page menus.
+ */
+function system_menu_links_alter(&$menu_links, $menu) {
+  
+  $names = array_keys(variable_get('system_admin_menus', array()));
+  
+  foreach ($menu as $path => $item) {
+    $parts = $item['_parts'];
+    if ($parts[0] == 'admin' && count($parts) > 2 && $item['_visible']) {
+      $parent = $parts[0] .'/'. $parts[1];
+      if (in_array($parent, $names)) {
+        $menu_links[$parent][$path] = array(
+          'disabled' => FALSE,
+          'link_text' => $item['title'],
+          'options' => array('description' => $item['description']),
+        );
+      }
+    }
+  }
+}
+
+
+/**
  * Provide the administration overview page.
  */
 function system_main_admin_page($arg = NULL) {
@@ -388,21 +439,18 @@ 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');
   }
+  $names = variable_get('system_admin_menus', array());
 
-  $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");
-  while ($item = db_fetch_object($result)) {
-    _menu_translate($item, $map, MENU_RENDER_LINK);
-    if (!$item->access) {
-      continue;
-    }
-    $block = (array)$item;
+  foreach ($names as $n => $item) {
+    $block = $item;
     $block['content'] = '';
     if ($item->block_callback && function_exists($item->block_callback)) {
       $function = $item->block_callback;
       $block['content'] .= $function();
     }
-    $block['content'] .= theme('admin_block_content', system_admin_menu_block($item));
+    if ($menu = menu_tree($n)) {
+      $block['content'] .= $menu;
+    }
     $blocks[] = $block;
   }
   return theme('admin_page', $blocks);
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.775
diff -u -p -r1.775 user.module
--- modules/user/user.module	24 Apr 2007 13:53:15 -0000	1.775
+++ modules/user/user.module	29 Apr 2007 16:08:47 -0000
@@ -969,8 +969,14 @@ function user_current_load($arg) {
   return user_load($arg);
 }
 
-function user_current_to_arg() {
-  return $GLOBALS['user']->uid;
+function user_current_to_arg($param) {
+  
+  if (ctype_digit($param)) {
+    return $param;
+  }
+  else {
+    return $GLOBALS['user']->uid;
+  }
 }
 
 /**
