Index: includes/install.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/install.inc,v
retrieving revision 1.43
diff -u -p -r1.43 install.inc
--- includes/install.inc	22 Aug 2007 08:36:34 -0000	1.43
+++ includes/install.inc	26 Aug 2007 22:00:57 -0000
@@ -354,6 +354,32 @@ function drupal_install_modules($module_
 function drupal_uninstall_module($module) {
   module_load_install($module);
   module_invoke($module, 'uninstall');
+  
+  // Remove menu links for paths declared by this module
+  drupal_load('module', $module);
+  $paths = module_invoke($module, 'menu');
+  if (!empty($paths)) {
+    $paths = array_keys($paths);
+    // Clean out the names of load functions.
+    foreach($paths as $index => $path) {
+      $parts = explode('/', $path, MENU_MAX_PARTS);
+      foreach ($parts as $k => $part) {
+        if (preg_match('/^%[a-z_]*$/', $part)) {
+          $parts[$k] = '%';
+        }
+      }
+      $paths[$index] = implode('/', $parts);
+    }
+    $placeholders = implode(', ', array_fill(0, count($paths), "'%s'"));
+    
+    $result = db_query('SELECT * FROM {menu_links} WHERE router_path IN ('. $placeholders .') AND external = 0 ORDER BY depth DESC', $paths);
+    // Remove all such items. Starting from those with the greatest depth will
+    // minimize the amount of re-parenting done by menu_link_delete().
+    while ($item = db_fetch_array($result)) {
+      _menu_delete_item($item, TRUE);
+    }
+  }
+
   drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
 }
 
Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.201
diff -u -p -r1.201 menu.inc
--- includes/menu.inc	25 Aug 2007 10:29:18 -0000	1.201
+++ includes/menu.inc	26 Aug 2007 22:00:58 -0000
@@ -363,10 +363,10 @@ function _menu_load_objects($item, &$map
 function _menu_check_access(&$item, $map) {
   // Determine access callback, which will decide whether or not the current
   // user has access to this path.
-  $callback = trim($item['access_callback']);
+  $callback = isset($item['access_callback']) ? trim($item['access_callback']) : 0;
   // Check for a TRUE or FALSE value.
   if (is_numeric($callback)) {
-    $item['access'] = $callback;
+    $item['access'] = (bool)$callback;
   }
   else {
     $arguments = menu_unserialize($item['access_arguments'], $map);
@@ -1457,13 +1457,14 @@ function _menu_navigation_links_rebuild(
       }
     }
   }
-  $placeholders = implode(', ', array_fill(0, count($menu), "'%s'"));
-  // Find any items where their router path does not exist any more.
-  $result = db_query('SELECT * FROM {menu_links} WHERE router_path NOT IN ('. $placeholders .') AND external = 0 ORDER BY depth DESC', array_keys($menu));
-  // Remove all such items. Starting from those with the greatest depth will
-  // minimize the amount of re-parenting done by menu_link_delete().
+  // During the menu update we might have added links that lead to nowhere
+  // If these exist, let's try fixing their router_path.
+  $result = db_query("SELECT ml.link_path, ml.mlid, ml.router_path FROM {menu_links} ml WHERE ml.router_path = '%s'", 'system_update_6021');
   while ($item = db_fetch_array($result)) {
-    _menu_delete_item($item, TRUE);
+    $router_path = _menu_find_router_path($menu, $item['link_path']);
+    if (!empty($router_path)) {
+      db_query("UPDATE {menu_links} SET router_path = '%s' WHERE mlid = %d", $router_path, $item['mlid']);
+    }
   }
 }
 
@@ -1623,15 +1624,8 @@ function menu_link_save(&$item) {
       $item['router_path'] = '';
     }
     else {
-      // Find the router path which will serve this path.
-      $item['parts'] = explode('/', $item['link_path'], MENU_MAX_PARTS);
-      $item['router_path'] = $item['link_path'];
-      if (!isset($menu[$item['router_path']])) {
-        list($ancestors) = menu_get_ancestors($item['parts']);
-        while ($ancestors && (empty($menu[$item['router_path']]))) {
-          $item['router_path'] = array_shift($ancestors);
-        }
-      }
+      $item['router_path'] = _menu_find_router_path($menu, $item['link_path']);
+
       if (empty($item['router_path'])) {
         return FALSE;
       }
@@ -1663,6 +1657,20 @@ function menu_link_save(&$item) {
   return $item['mlid'];
 }
 
+function _menu_find_router_path($menu, $link_path) {
+  // Find the router path which will serve this path.
+  $parts = explode('/', $link_path, MENU_MAX_PARTS);
+  $router_path = $link_path;
+  if (!isset($menu[$router_path])) {
+    list($ancestors) = menu_get_ancestors($parts);
+    $ancestors[] = '';
+    while ($ancestors && (empty($menu[$router_path]))) {
+      $router_path = array_shift($ancestors);
+    }
+  }
+  return $router_path;
+}
+
 /**
  * Find the depth of an item's children relative to its depth.
  *
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.140
diff -u -p -r1.140 system.install
--- modules/system/system.install	26 Aug 2007 08:27:09 -0000	1.140
+++ modules/system/system.install	26 Aug 2007 22:00:58 -0000
@@ -3322,10 +3322,123 @@ function system_update_6020() {
 
 function system_update_6021() {
   $ret = array();
-  // TODO - menu module updates. These need to happen before we do the menu_rebuild
+  // Multi-part update
+  if (!isset($_SESSION['system_update_6021'])) {
+    $_SESSION['system_update_6021'] = 0;
+    $_SESSION['system_update_6021_max'] = db_result(db_query('SELECT COUNT(*) FROM {menu}'));
+    $_SESSION['menu_menu_map'] = array(1 => 'navigation');
+    // 0 => FALSE is for new menus, 1 => FALSE is for the navigation.
+    $_SESSION['menu_item_map'] = array(0 => FALSE, 1 => FALSE);
+    if ($secondary = variable_get('menu_secondary_menu', 0)) {
+      $_SESSION['menu_menu_map'][$secondary] = 'secondary-links';
+      $_SESSION['menu_item_map'][$secondary] = FALSE;
+    }
+    if ($primary = variable_get('menu_primary_menu', 0)) {
+      $_SESSION['menu_menu_map'][$primary] = 'primary-links';
+      $_SESSION['menu_item_map'][$primary] = FALSE;
+      if ($primary == $secondary) {
+        variable_set('menu_secondary_links_source', 'primary-links');
+      }
+    }
+    switch ($GLOBALS['db_type']) {
+      case 'mysql': case 'mysqli':
+        update_sql('ALTER TABLE {menu} ADD converted tinyint unsigned default 0 NOT NULL');
+        break;
+      case 'pgsql':
+        db_add_column($ret, 'menu', 'converted', 'smallint', array('default' => 0, 'not null' => TRUE));
+        break;
+    }
+    $table = array(
+      'fields' => array(
+        'menu_name'   => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
+        'title'       => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
+        'description' => array('type' => 'text', 'not null' => FALSE),
+      ),
+      'primary key' => array('menu_name'),
+    );
+    db_create_table($ret, 'menu_custom', $table);
+    db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('%s', '%s', '%s')", 'navigation', 'Navigation', 'The navigation menu is provided by Drupal and is the main interactive menu for any site. It is usually the only menu that contains personalized links for authenticated users, and is often not even visible to anonymous users.');
+    db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('%s', '%s', '%s')", 'primary-links', 'Primary links', 'Primary links are often used at the theme layer to show the major sections of a site. A typical representation for primary links would be tabs along the top.');
+    db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('%s', '%s', '%s')", 'secondary-links', 'Secondary links', 'Secondary links are often used for pages like legal notices, contact details, and other secondary navigation items that play a lesser role than primary links');
+    menu_rebuild();
+    // Force a reload.
+    return array('#finished' => FALSE);
+  }
 
-  menu_rebuild();
-  return $ret;
+  $limit = 50;
+  while ($limit-- && ($item = db_fetch_array(db_query_range('SELECT * FROM {menu} WHERE converted = 0', 0, 1)))) {
+    // If it's not a menu...
+    if ($item['pid']) {
+      // Let's climb up until we find an item with a converted parent.
+      while (!isset($_SESSION['menu_item_map'][$item['pid']])) {
+        $item = db_fetch_array(db_query('SELECT * FROM {menu} WHERE mid = %d', $item['pid']));
+      }
+    }
+    // We need to recheck because item might have changed.
+    if ($item['pid']) {
+      // Fill the new fields.
+      $item['link_title'] = $item['title'];
+      $item['link_path'] = $item['path'];
+      // We know the parent is already set. If it's not FALSE then it's an item.
+      if ($_SESSION['menu_item_map'][$item['pid']]) {
+        // The new menu system parent link id.
+        $item['plid'] = $_SESSION['menu_item_map'][$item['pid']]['mlid'];
+        // The new menu system menu name.
+        $item['menu_name'] = $_SESSION['menu_item_map'][$item['pid']]['menu_name'];
+      }
+      else {
+        // This a top level element.
+        $item['plid'] = 0;
+        // The menu name is stored among the menus.
+        $item['menu_name'] = $_SESSION['menu_menu_map'][$item['pid']];
+      }
+      // Is the element visible in the menu block?
+      $item['hidden'] = !($item['type'] & MENU_VISIBLE_IN_TREE);
+      // Is it a custom(ized) element?
+      if ($item['type' ] & (MENU_CREATED_BY_ADMIN | MENU_MODIFIED_BY_ADMIN)) {
+        $item['module'] = 'menu';
+        $item['customized'] = TRUE;
+      }
+      else {
+        $item['module'] = 'system';
+        $item['customized'] = FALSE;
+      }
+      // Skip over external links or links to the front page.
+      if (!menu_path_is_external($item['link_path']) && $item['link_path'] != '<front>') {
+        $item['router_path'] = _menu_find_router_path(menu_router_build(), $item['link_path']);
+        // Use a special a callback path to serve as a placeholder for links
+        // whose path is defined by a module that is not enabled.
+        if (empty($item['router_path'])) {
+          $item['router_path'] = 'system_update_6021';
+        }
+      }
+      // Save the link.
+      menu_link_save($item);
+      $_SESSION['menu_item_map'][$item['mid']] = $item;
+    }
+    elseif (!isset($_SESSION['menu_menu_map'][$item['mid']])) {
+      $item['menu_name'] = preg_replace('/[^a-zA-Z0-9]/' , '-', strtolower($item['title']));
+      $i = 0;
+      while (db_result(db_query("SELECT menu_name FROM {menu_custom} WHERE menu_name = 'menu-%s'", $item['menu_name']))) {
+        $item['menu_name'] .= $i++;
+      }
+      $form_state = array('values' => $item);
+      // We have a batch running, drupal_execute won't work. And we do not
+      // validate anyways.
+      menu_edit_menu_submit(array('#insert' => TRUE), $form_state);
+      $_SESSION['menu_menu_map'][$item['mid']] = 'menu-'. $item['menu_name'];
+      $_SESSION['menu_item_map'][$item['mid']] = FALSE;
+    }
+    db_query('UPDATE {menu} SET converted = %d WHERE mid  = %d', 1 + (!empty($item['customized'])), $item['mid']);
+    $_SESSION['system_update_6021']++;
+  }
+  if ($_SESSION['system_update_6021'] >= $_SESSION['system_update_6021_max']) {
+    unset($_SESSION['system_update_6021'], $_SESSION['system_update_6021_max'], $_SESSION['menu_menu_map'], $_SESSION['menu_item_map']);
+    menu_rebuild();
+    db_query("DROP TABLE {menu}");
+    return array();
+  }
+  return array('#finished' => FALSE);
 }
 
 /**
@@ -3521,7 +3634,7 @@ function system_update_6028() {
   return $ret;
 }
 
-/*
+/**
  * Enable the dblog module on sites that upgrade, since otherwise
  * watchdog logging will stop unexpectedly.
  */
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.524
diff -u -p -r1.524 system.module
--- modules/system/system.module	26 Aug 2007 16:41:02 -0000	1.524
+++ modules/system/system.module	26 Aug 2007 22:00:59 -0000
@@ -384,6 +384,14 @@ function system_menu() {
     'type' => MENU_CALLBACK,
     'file' => 'system.admin.inc',
   );
+  // No-op callback for orphaned link during the menu update.
+  $items['system_update_6021'] = array(
+    'page callback' => 't',
+    'page arguments' => array('Orphan link.'),
+    'access callback' => FALSE,
+    'type' => MENU_CALLBACK,
+  );
+
   return $items;
 }
 
