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 25 Apr 2007 04:22:07 -0000 @@ -313,7 +313,7 @@ function menu_get_item($path = NULL, $it // 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); + $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); @@ -582,6 +582,30 @@ function menu_get_active_help() { return $output; } + +/** + * Build a list of named menus. + */ + +function menu_get_names($reset = FALSE) { + $names = module_invoke_all('menu_info'); + $names['navigation'] = array('customize' => TRUE, 'title' => t('Navigation'), 'title callback' => 'check_plain', 'callback arguments' => array('$user->name')); + + foreach ($names as $key => $data) { + if (!isset($data['customize'])) { + $names[$key]['customize'] = 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); @@ -591,12 +615,20 @@ function menu_path_is_external($path) { * Populate the database representation of the menu. */ function menu_rebuild() { - // TODO: split menu and menu links storage. + $menu = module_invoke_all('menu'); + $names = menu_get_names(TRUE); + foreach ($menu as $path => $item) { + if (!isset($item['menu name']) || !isset($names[$item['menu name']])) { + $menu[$path]['menu name'] = 'navigation'; // The default 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}'); + db_query('DELETE FROM {menu_links}'); $mid = 1; // First pass: separate callbacks from pathes, making pathes ready for @@ -680,11 +712,82 @@ function menu_rebuild() { $menu_path_map[$path] = $new_path; $menu[$new_path] = $item; } + $menu_path_map[''] = ''; + // Apply inheritance rules. + foreach ($menu as $path => $foo) { + $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['parent'] = $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['parent'])) { + // parent stores the tab parent. + $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(); + } + // 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']; + } + } + } + } + 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' => '', + ); + 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']); + } + + return; // 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]; @@ -747,64 +850,8 @@ function menu_rebuild() { $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; - } } - 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)); - } - } - 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); @@ -843,31 +890,27 @@ function menu_rebuild() { 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, + + + db_query("INSERT INTO {menu_links} ( + menu_name, mid, pid, path, visible, + access_callback, access_arguments, parents, depth, has_children, + mleft, mright, description, 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', + VALUES ('%s', %d, %d, '%s', %d, + '%s', '%s', '%s', %d, %d, + %d, %d, '%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['menu name'], $item['_mid'], $item['_pid'], $path, $item['_visible'], + $item['access callback'], serialize($item['access arguments']), $item['_parents'], + $item['_depth'], $has_children, + $item['_mleft'], $item['_mright'], $item['description'], + $link_path, $item['attributes'], $item['query'], $item['fragment'], $item['absolute'], $item['html']); } } - - function menu_renumber(&$tree) { foreach ($tree as $key => $element) { if (!isset($tree[$key]['_mleft'])) { @@ -924,20 +967,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); if ($item->access) { - $depth = $item->depth; + $depth = $item->tab_depth; $link = l($item->title, $item->link_path, (array)$item); // 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; } @@ -1009,7 +1052,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.97 diff -u -p -r1.97 system.install --- modules/system/system.install 24 Apr 2007 13:55:36 -0000 1.97 +++ modules/system/system.install 25 Apr 2007 04:22:07 -0000 @@ -324,8 +324,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 '', @@ -335,32 +333,42 @@ 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 '', + PRIMARY KEY (path), + 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, + path varchar(255) NOT NULL default '', + visible int NOT NULL default 0, + access_callback varchar(255) NOT NULL default '', + access_arguments text, + 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, + description 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), + PRIMARY KEY (menu_name, path), KEY visible (visible), - KEY pid (pid), - KEY parent (parent) + KEY pid (pid) ) /*!40100 DEFAULT CHARACTER SET UTF8 */ "); db_query("CREATE TABLE {node} (