Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.328
diff -u -p -r1.328 menu.inc
--- includes/menu.inc	10 Jun 2009 21:52:36 -0000	1.328
+++ includes/menu.inc	23 Jun 2009 00:33:57 -0000
@@ -76,96 +76,20 @@
  * @{
  * Flags for use in the "type" attribute of menu items.
  */
-
-/**
- * Internal menu flag -- menu item is the root of the menu tree.
- */
-define('MENU_IS_ROOT', 0x0001);
-
-/**
- * Internal menu flag -- menu item is visible in the menu tree.
- */
-define('MENU_VISIBLE_IN_TREE', 0x0002);
-
-/**
- * Internal menu flag -- menu item is visible in the breadcrumb.
- */
-define('MENU_VISIBLE_IN_BREADCRUMB', 0x0004);
-
-/**
- * Internal menu flag -- menu item links back to its parnet.
- */
-define('MENU_LINKS_TO_PARENT', 0x0008);
-
-/**
- * Internal menu flag -- menu item can be modified by administrator.
- */
-define('MENU_MODIFIED_BY_ADMIN', 0x0020);
-
-/**
- * Internal menu flag -- menu item was created by administrator.
- */
-define('MENU_CREATED_BY_ADMIN', 0x0040);
-
-/**
- * Internal menu flag -- menu item is a local task.
- */
-define('MENU_IS_LOCAL_TASK', 0x0080);
-
 /**
- * @} End of "Menu flags".
+ * This link can not appear in a navigation tree.
  */
+define('MENU_LINK_DISABLED', -1);
 
 /**
- * @name Menu item types
- * @{
- * Menu item definitions provide one of these constants, which are shortcuts for
- * combinations of the above flags.
- */
-
-/**
- * Menu type -- A "normal" menu item that's shown in menu and breadcrumbs.
- *
- * Normal menu items show up in the menu tree and can be moved/hidden by
- * the administrator. Use this for most menu items. It is the default value if
- * no menu item type is specified.
- */
-define('MENU_NORMAL_ITEM', MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB);
-
-/**
- * Menu type -- A hidden, internal callback, typically used for API calls.
- *
- * Callbacks simply register a path so that the correct function is fired
- * when the URL is accessed. They are not shown in the menu.
- */
-define('MENU_CALLBACK', MENU_VISIBLE_IN_BREADCRUMB);
-
-/**
- * Menu type -- A normal menu item, hidden until enabled by an administrator.
- *
- * Modules may "suggest" menu items that the administrator may enable. They act
- * just as callbacks do until enabled, at which time they act like normal items.
- * Note for the value: 0x0010 was a flag which is no longer used, but this way
- * the values of MENU_CALLBACK and MENU_SUGGESTED_ITEM are separate.
+ * This link appears by default in a navigation tree.
  */
-define('MENU_SUGGESTED_ITEM', MENU_VISIBLE_IN_BREADCRUMB | 0x0010);
+define('MENU_LINK_SHOWN_BY_DEFAULT', 0);
 
 /**
- * Menu type -- A task specific to the parent item, usually rendered as a tab.
- *
- * Local tasks are menu items that describe actions to be performed on their
- * parent item. An example is the path "node/52/edit", which performs the
- * "edit" task on "node/52".
+ * This link can appear in a navigation tree but is hidden by default.
  */
-define('MENU_LOCAL_TASK', MENU_IS_LOCAL_TASK);
-
-/**
- * Menu type -- The "default" local task, which is initially active.
- *
- * Every set of local tasks should provide one "default" task, that links to the
- * same path as its parent when clicked.
- */
-define('MENU_DEFAULT_LOCAL_TASK', MENU_IS_LOCAL_TASK | MENU_LINKS_TO_PARENT);
+define('MENU_LINK_HIDDEN_BY_DEFAULT', 1);
 
 /**
  * @} End of "Menu item types".
@@ -376,6 +300,9 @@ function menu_get_item($path = NULL, $ro
       }
       if ($router_item['access']) {
         $router_item['map'] = $map;
+        $router_item['request_path'] = $path;
+        $router_item['original_path'] = $path;
+        $router_item['original_map'] = $original_map;
         $router_item['page_arguments'] = array_merge(menu_unserialize($router_item['page_arguments'], $map), array_slice($map, $router_item['number_parts']));
       }
     }
@@ -397,6 +324,7 @@ function menu_execute_active_handler($pa
     menu_rebuild();
   }
   if ($router_item = menu_get_item($path)) {
+  
     if ($router_item['access']) {
       if (drupal_function_exists($router_item['page_callback'])) {
         return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
@@ -626,6 +554,7 @@ function _menu_translate(&$router_item, 
 
   // Generate the link path for the page request or local tasks.
   $link_map = explode('/', $router_item['path']);
+  $router_item['link_map'] = $link_map;
   for ($i = 0; $i < $router_item['number_parts']; $i++) {
     if ($link_map[$i] == '%') {
       $link_map[$i] = $path_map[$i];
@@ -755,11 +684,12 @@ function _menu_link_translate(&$item) {
  *   The position of the object in the path, where the first path segment is 0.
  *   For node/%node, the position of %node is 1, but for comment/reply/%node,
  *   it's 2. Defaults to 1.
- * @param $path
- *   See menu_get_item() for more on this. Defaults to the current path.
+ * @param $item
+ *   A menu item got from menu_get_item(). Defaults to the item corresponding to
+ *   the current path.
  */
-function menu_get_object($type = 'node', $position = 1, $path = NULL) {
-  $router_item = menu_get_item($path);
+function menu_get_object($type = 'node', $position = 1, $item = NULL) {
+  $router_item = $item ? $item : menu_get_item();
   if (isset($router_item['load_functions'][$position]) && !empty($router_item['map'][$position]) && $router_item['load_functions'][$position] == $type . '_load') {
     return $router_item['map'][$position];
   }
@@ -878,7 +808,6 @@ function menu_tree_all_data($menu_name, 
         'title',
         'title_callback',
         'title_arguments',
-        'type',
         'description',
       ));
       for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) {
@@ -1048,7 +977,6 @@ function menu_tree_page_data($menu_name)
           'title',
           'title_callback',
           'title_arguments',
-          'type',
           'description',
         ));
         for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) {
@@ -1269,7 +1197,7 @@ function theme_menu_tree($tree) {
  * The menu item's LI element is given one of the following classes:
  * - expanded: The menu item is showing its submenu.
  * - collapsed: The menu item has a submenu which is not shown.
- * - leaf: The menu item has no submenu. 
+ * - leaf: The menu item has no submenu.
  *
  * @ingroup themeable
  *
@@ -1438,139 +1366,92 @@ function menu_navigation_links($menu_nam
   return $links;
 }
 
-/**
- * Collects the local tasks (tabs) for a given level.
- *
- * @param $level
- *   The level of tasks you ask for. Primary tasks are 0, secondary are 1.
- * @param $return_root
- *   Whether to return the root path for the current page.
- * @return
- *   Themed output corresponding to the tabs of the requested level, or
- *   router path if $return_root == TRUE. This router path corresponds to
- *   a parent tab, if the current page is a default local task.
- */
-function menu_local_tasks($level = 0, $return_root = FALSE) {
-  $tabs = &drupal_static(__FUNCTION__);
-  $root_path = &drupal_static(__FUNCTION__ . ':root_path');
-
-  if (!isset($tabs)) {
-    $tabs = array();
-
-    $router_item = menu_get_item();
-    if (!$router_item || !$router_item['access']) {
-      return '';
-    }
-    // Get all tabs and the root page.
-    $result = db_select('menu_router', NULL, array('fetch' => PDO::FETCH_ASSOC))
-      ->fields('menu_router')
-      ->condition('tab_root', $router_item['tab_root'])
-      ->orderBy('weight')
-      ->orderBy('title')
-      ->execute();
-    $map = arg();
-    $children = array();
-    $tasks = array();
-    $root_path = $router_item['path'];
 
-    foreach ($result as $item) {
-      _menu_translate($item, $map, TRUE);
-      if ($item['tab_parent']) {
-        // All tabs, but not the root page.
-        $children[$item['tab_parent']][$item['path']] = $item;
-      }
-      // Store the translated item for later use.
-      $tasks[$item['path']] = $item;
-    }
-
-    // Find all tabs below the current path.
-    $path = $router_item['path'];
-    // Tab parenting may skip levels, so the number of parts in the path may not
-    // equal the depth. Thus we use the $depth counter (offset by 1000 for ksort).
-    $depth = 1001;
-    while (isset($children[$path])) {
-      $tabs_current = '';
-      $next_path = '';
-      $count = 0;
-      foreach ($children[$path] as $item) {
-        if ($item['access']) {
-          $count++;
-          // The default task is always active.
-          if ($item['type'] == MENU_DEFAULT_LOCAL_TASK) {
-            // Find the first parent which is not a default local task.
-            for ($p = $item['tab_parent']; $tasks[$p]['type'] == MENU_DEFAULT_LOCAL_TASK; $p = $tasks[$p]['tab_parent']);
-            $link = theme('menu_item_link', array('href' => $tasks[$p]['href']) + $item);
-            $tabs_current .= theme('menu_local_task', $link, TRUE);
-            $next_path = $item['path'];
-          }
-          else {
-            $link = theme('menu_item_link', $item);
-            $tabs_current .= theme('menu_local_task', $link);
-          }
+define('MENU_LOCAL_TASK', 0);
+define('MENU_DEFAULT_LOCAL_TASK', 0);
+
+function menu_local_tasks($return_level) {
+  static $output;
+  if (!isset($output)) {
+    $output = array();
+    $tasks = array();
+    $item = menu_get_item();
+    // Collect local tasks.
+    $tasks = module_invoke_all('menu_local_task', $item);
+    drupal_alter('menu_local_task', $tasks);
+    if ($tasks) {
+      // Make sure no item comes before its parent.
+      asort($tasks);
+      // Mark the active tabs.
+      $path = $item['original_path'];
+      while ($path) {
+        if (isset($tasks[$path])) {
+          do {
+            $tasks[$path]['active'] = TRUE;
+            $path = isset($tasks[$path]['parent']) ? $tasks[$path]['parent'] : FALSE;
+          } while ($path);
+          break;
+        }
+        $path = substr($path, 0, strrpos($path, '/'));
+      }
+      // Put links on the appropriate level and mark defaults.
+      $links = array();
+      $default = array();
+      foreach ($tasks as $href => &$task) {
+        $level = isset($task['parent']) ? $tasks[$task['parent']]['level'] + 1 : 0;
+        $task['level'] = $level;
+        // The keys in menu_local_task is just what l() options are, so just
+        // pass the whole task simply to l() through theme('menu_item_link').
+        $links[$level][$href] = array(
+          'href' => $href,
+          'title' => $task['title'],
+          'localized_options' => $task,
+          'weight' => isset($task['weight']) ? $task['weight'] : 0,
+        );
+        // If there is an active task, set the default to empty to indicate
+        // there is no need for a default on this level.
+        if (!empty($task['active'])) {
+          $default[$level] = '';
         }
-      }
-      $path = $next_path;
-      $tabs[$depth]['count'] = $count;
-      $tabs[$depth]['output'] = $tabs_current;
-      $depth++;
-    }
-
-    // Find all tabs at the same level or above the current one.
-    $parent = $router_item['tab_parent'];
-    $path = $router_item['path'];
-    $current = $router_item;
-    $depth = 1000;
-    while (isset($children[$parent])) {
-      $tabs_current = '';
-      $next_path = '';
-      $next_parent = '';
-      $count = 0;
-      foreach ($children[$parent] as $item) {
-        if ($item['access']) {
-          $count++;
-          if ($item['type'] == MENU_DEFAULT_LOCAL_TASK) {
-            // Find the first parent which is not a default local task.
-            for ($p = $item['tab_parent']; $tasks[$p]['type'] == MENU_DEFAULT_LOCAL_TASK; $p = $tasks[$p]['tab_parent']);
-            $link = theme('menu_item_link', array('href' => $tasks[$p]['href']) + $item);
-            if ($item['path'] == $router_item['path']) {
-              $root_path = $tasks[$p]['path'];
-            }
-          }
-          else {
-            $link = theme('menu_item_link', $item);
-          }
-          // We check for the active tab.
-          if ($item['path'] == $path) {
-            $tabs_current .= theme('menu_local_task', $link, TRUE);
-            $next_path = $item['tab_parent'];
-            if (isset($tasks[$next_path])) {
-              $next_parent = $tasks[$next_path]['tab_parent'];
-            }
-          }
-          else {
-            $tabs_current .= theme('menu_local_task', $link);
-          }
+        // If this is a default task and there is no default yet, record the
+        // path.
+        if (!empty($task['default']) && !isset($default['level'])) {
+          $default[$level] = $href;
+        }
+      }      
+      // Create output.
+      foreach ($links as $level => $current_links) {
+        usort($current_links, '_menu_sort');
+        $output[$level] = '';
+        // Check we have enough links to display (less than two is pointless)
+        if (count($current_links) < 2) {
+          return $output[$level];
+        }
+        // If there was a default and there was no active task (which sets the
+        // default to empty) then mark the default task active.
+        if (!empty($default[$level])) {
+          $tasks[$default[$level]]['active'] = TRUE;
+        }
+        foreach ($current_links as $link) {
+          $output[$level] .= theme('menu_local_task', theme('menu_item_link', $link), !empty($tasks[$link['href']]['active']));
         }
       }
-      $path = $next_path;
-      $parent = $next_parent;
-      $tabs[$depth]['count'] = $count;
-      $tabs[$depth]['output'] = $tabs_current;
-      $depth--;
-    }
-    // Sort by depth.
-    ksort($tabs);
-    // Remove the depth, we are interested only in their relative placement.
-    $tabs = array_values($tabs);
+    }
   }
+  return isset($output[$return_level]) ? $output[$return_level] : '';
+}
 
-  if ($return_root) {
-    return $root_path;
+function _menu_sort($a, $b) {
+  if ($a['weight'] < $b['weight']) {
+    return -1;
   }
-  else {
-    // We do not display single tabs.
-    return (isset($tabs[$level]) && $tabs[$level]['count'] > 1) ? $tabs[$level]['output'] : '';
+  if ($a['weight'] > $b['weight']) {
+    return 1;
+  }
+  if ($a['title'] < $b['title']) {
+    return -1;
   }
+  return 1;
 }
 
 /**
@@ -1591,6 +1472,7 @@ function menu_secondary_local_tasks() {
  * Returns the router path, or the path of the parent tab of a default local task.
  */
 function menu_tab_root_path() {
+  return $_GET['q'];
   return menu_local_tasks(0, TRUE);
 }
 
@@ -1721,7 +1603,7 @@ function menu_set_active_trail($new_trai
     // Make sure the current page is in the trail (needed for the page title),
     // but exclude tabs and the front page.
     $last = count($trail) - 1;
-    if ($trail[$last]['href'] != $item['href'] && !(bool)($item['type'] & MENU_IS_LOCAL_TASK) && !drupal_is_front_page()) {
+    if ($trail[$last]['href'] != $item['href'] && !drupal_is_front_page()) {
       $trail[] = $item;
     }
   }
@@ -1768,12 +1650,7 @@ function menu_get_active_breadcrumb() {
  */
 function menu_get_active_title() {
   $active_trail = menu_get_active_trail();
-
-  foreach (array_reverse($active_trail) as $item) {
-    if (!(bool)($item['type'] & MENU_IS_LOCAL_TASK)) {
-      return $item['title'];
-    }
-  }
+  return $active_trail[0]['title'];
 }
 
 /**
@@ -1903,12 +1780,6 @@ function menu_get_router() {
  * Builds a link from a router item.
  */
 function _menu_link_build($item) {
-  if ($item['type'] == MENU_CALLBACK) {
-    $item['hidden'] = -1;
-  }
-  elseif ($item['type'] == MENU_SUGGESTED_ITEM) {
-    $item['hidden'] = 1;
-  }
   // Note, we set this as 'system', so that we can be sure to distinguish all
   // the menu links generated automatically from entries in {menu_router}.
   $item['module'] = 'system';
@@ -1916,7 +1787,6 @@ function _menu_link_build($item) {
     'menu_name' => 'navigation',
     'link_title' => $item['title'],
     'link_path' => $item['path'],
-    'hidden' => 0,
     'options' => empty($item['description']) ? array() : array('attributes' => array('title' => $item['description'])),
   );
   return $item;
@@ -1929,7 +1799,7 @@ function _menu_navigation_links_rebuild(
   // Add normal and suggested items as links.
   $menu_links = array();
   foreach ($menu as $path => $item) {
-    if ($item['_visible']) {
+    if ($item['hidden'] == MENU_LINK_SHOWN_BY_DEFAULT || isset($item['tab title'])) {
       $menu_links[$path] = $item;
       $sort[$path] = $item['_number_parts'];
     }
@@ -1967,7 +1837,15 @@ function _menu_navigation_links_rebuild(
       }
       if (!$existing_item || !$existing_item['customized']) {
         $item = _menu_link_build($item);
-        menu_link_save($item);
+        if ($item['hidden'] == MENU_LINK_SHOWN_BY_DEFAULT) {
+          menu_link_save($item);
+        }
+        if (isset($item['tab title'])) {
+          $tab_link = $item;
+          $tab_link['menu_name'] = 'system-tabs';
+          $tab_link['title'] = $item['tab title'];
+          menu_link_save($tab_link);
+        }
       }
     }
   }
@@ -2098,7 +1976,7 @@ function menu_link_save(&$item) {
     'menu_name' => 'navigation',
     'weight' => 0,
     'link_title' => '',
-    'hidden' => 0,
+    'hidden' => MENU_LINK_SHOWN_BY_DEFAULT,
     'has_children' => 0,
     'expanded' => 0,
     'options' => array(),
@@ -2564,14 +2442,10 @@ function _menu_router_build($callbacks) 
     $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),
+      'hidden' => isset($item['tab title']) && empty($item['equals']) ? MENU_LINK_DISABLED : MENU_LINK_SHOWN_BY_DEFAULT,
     );
     if ($move) {
       $new_path = implode('/', $item['_parts']);
@@ -2587,33 +2461,15 @@ function _menu_router_build($callbacks) 
   // Apply inheritance rules.
   foreach ($menu as $path => $v) {
     $item = &$menu[$path];
-    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 an access callback is not found for a default local task we use
         // the callback from the parent, since we expect them to be identical.
         // In all other cases, the access parameters must be specified.
-        if (($item['type'] == MENU_DEFAULT_LOCAL_TASK) && !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'];
@@ -2681,7 +2537,6 @@ function _menu_router_save($menu, $masks
       'title',
       'title_callback',
       'title_arguments',
-      'type',
       'block_callback',
       'description',
       'position',
@@ -2705,7 +2560,6 @@ function _menu_router_save($menu, $masks
       'title' => $item['title'],
       'title_callback' => $item['title callback'],
       'title_arguments' => ($item['title arguments'] ? serialize($item['title arguments']) : ''),
-      'type' => $item['type'],
       'block_callback' => $item['block callback'],
       'description' => $item['description'],
       'position' => $item['position'],
