Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.166 diff -u -p -r1.166 menu.inc --- includes/menu.inc 16 May 2007 13:45:16 -0000 1.166 +++ includes/menu.inc 19 May 2007 02:43:06 -0000 @@ -149,11 +149,6 @@ define('MENU_SITE_OFFLINE', 4); * @} End of "Menu status codes". */ - -/** - * @} End of "Menu operations." - */ - /** * @Name Menu tree parameters * @{ @@ -898,6 +893,25 @@ function menu_secondary_local_tasks() { return menu_local_tasks(1); } +/** + * Returns the rendered local tasks. The default implementation renders + * them as tabs. + * + * @ingroup themeable + */ +function theme_menu_local_tasks() { + $output = ''; + + if ($primary = menu_primary_local_tasks()) { + $output .= "\n"; + } + if ($secondary = menu_secondary_local_tasks()) { + $output .= "\n"; + } + + return $output; +} + function menu_set_active_menu_name($menu_name = NULL) { static $active; @@ -1004,32 +1018,11 @@ function menu_get_active_title() { function menu_get_item_by_mlid($mlid) { if ($item = db_fetch_object(db_query("SELECT * FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE mlid = %d", $mlid))) { _menu_link_translate($item); - if ($item->access) { - return $item; - } + return $item; } return FALSE; } -/** - * Returns the rendered local tasks. The default implementation renders - * them as tabs. - * - * @ingroup themeable - */ -function theme_menu_local_tasks() { - $output = ''; - - if ($primary = menu_primary_local_tasks()) { - $output .= "\n"; - } - if ($secondary = menu_secondary_local_tasks()) { - $output .= "\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 @@ -1074,15 +1067,17 @@ function _menu_navigation_links_rebuild( if ($item['type'] == MENU_CALLBACK || $item['type'] == MENU_SUGGESTED_ITEM) { $item['hidden'] = $item['type']; } + // 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'; $item += array( - 'menu name' => 'navigation', + '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']))) { + 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']; } @@ -1106,7 +1101,7 @@ function _menu_navigation_links_rebuild( * @param $item * An array representing a menu link item. The only mandatory keys are href * and link_title. Possible keys are - * menu name default is navigation + * menu_name default is navigation * weight default is 0 * expanded whether the item is expanded. * options An array of options, @see l for more. @@ -1130,7 +1125,7 @@ function menu_link_save(&$item, $_menu = $item['_external'] = menu_path_is_external($item['href']); // Load defaults. $item += array( - 'menu name' => 'navigation', + 'menu_name' => 'navigation', 'weight' => 0, 'link_title' => '', 'hidden' => 0, @@ -1138,26 +1133,26 @@ function menu_link_save(&$item, $_menu = 'expanded' => 0, 'options' => empty($item['description']) ? array() : array('attributes' => array('title' => $item['description'])), ); - $existing_item = array(); + $menu_name = $item['menu_name']; + $existing_item = FALSE; 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'])); + $existing_item = db_fetch_array(db_query("SELECT * FROM {menu_links} WHERE menu_name = '%s' AND href = '%s'", $menu_name, $item['href'])); } - if (empty($existing_item)) { + if ($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 { // + else { $parent_path = $item['href']; do { $parent_path = substr($parent_path, 0, strrpos($parent_path, '/')); @@ -1180,18 +1175,21 @@ function menu_link_save(&$item, $_menu = } else { // Cannot add beyond the maximum depth. - if ($parent['depth'] >= (MENU_MAX_DEPTH)) { + if ($item['has_children'] && $existing_item) { + $limit = MENU_MAX_DEPTH - menu_children_relative_depth($existing_item) - 1; + } + else { + $limit = MENU_MAX_DEPTH - 1; + } + if ($parent['depth'] > $limit) { return FALSE; } $item['depth'] = $parent['depth'] + 1; - _menu_parents_copy($item, $parent); - $item['p'. $item['depth']] = $item['mlid']; + _menu_link_parents_set($item, $parent); } - - 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 + // Need to check both plid and menu_name, since plid can be 0 in any menu. + if ($existing_item && ($item['plid'] != $existing_item['plid'] || $menu_name != $existing_item['menu_name'])) { + _menu_link_move_children($item, $existing_item); } // Find the callback. if (empty($item['router_path']) || $new_path) { @@ -1213,13 +1211,13 @@ function menu_link_save(&$item, $_menu = } } } - if (!empty($existing_item)) { + if ($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['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'], @@ -1237,18 +1235,19 @@ function menu_link_save(&$item, $_menu = %d, %d, %d, %d, %d, %d, %d, %d, %d, '%s', '%s', '%s')", - $item['menu name'], $item['mlid'], $item['plid'], $item['href'], + $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']); + } + // Check the has_children status of the parent. + if ($item['plid']) { + $parent_has_children = (bool)db_result(db_query("SELECT COUNT(*) FROM {menu_links} WHERE plid = %d AND hidden = 0", $item['plid'])); + db_query("UPDATE {menu_links} SET has_children = %d WHERE mlid = %d", $parent_has_children, $item['plid']); } - // Keep track of which menus have expanded items + // Keep track of which menus have expanded items. $names = array(); $result = db_query("SELECT menu_name FROM {menu_links} WHERE expanded != 0 GROUP BY menu_name"); while ($n = db_fetch_array($result)) { @@ -1258,13 +1257,94 @@ function menu_link_save(&$item, $_menu = 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++; - } +/** + * Find the depth of an item's children relative to its depth. For example, if + * the item has a depth of 2, and the maximum of any child in the menu link tree + * is 5, the relative depth is 3. + * + * @param $item + * An array representing a menu link item. + * @return + * The relative depth, or zero. + * + */ +function menu_link_children_relative_depth($item) { + $i = 1; + $match = ''; + $args[] = $item['menu_name']; + $p = 'p1'; + while ($i <= MENU_MAX_DEPTH && $item[$p]) { + $match .= " AND $p = %d"; + $args[] = $item[$p]; + $p = 'p'. ++$i; + } + + $max_depth = db_result(db_query_range("SELECT depth FROM {menu_links} WHERE menu_name = '%s'". $match ." ORDER BY depth DESC", $args, 0, 1)); + + return ($max_depth > $item['depth']) ? $max_depth - $item['depth'] : 0; +} + +/** + * Update the menu name, parents (p1 - p6), and depth for the children of + * a menu link that's being moved in the tree and check the has_children status + * of the previous parent. + */ +function _menu_link_move_children($item, $existing_item) { + + $args[] = $item['menu_name']; + $set = ''; + $shift = $item['depth'] - $existing_item['depth']; + if ($shift < 0) { + $args[] = -$shift; + $set = ', depth = depth - %d'; + } + elseif ($shift > 0) { + $args[] = $shift; + $set = ', depth = depth + %d'; + } + $i = 1; + while ($i <= $item['depth']) { + $p = 'p'. $i++; + $set .= ", $p = %d"; + $args[] = $item[$p]; + } + $j = $existing_item['depth'] + 1; + while ($i <= MENU_MAX_DEPTH && $j <= MENU_MAX_DEPTH) { + $set .= ', p'. $i++ .' = p'. $j++; + } + while ($i <= MENU_MAX_DEPTH) { + $set .= ', p'. $i++ .' = 0'; + } + + $args[] = $existing_item['menu_name']; + $i = 1; + while ($i <= MENU_MAX_DEPTH && $existing_item[$p]) { + $p = 'p'. $i++; + $match .= " AND $p = %d"; + $args[] = $existing_item[$p]; + } + + db_query("UPDATE {menu_links} SET menu_name = '%s'". $set ." WHERE menu_name = '%s'". $match, $args); + + if ($existing_item['plid']) { + $parent_has_children = (bool)db_result(db_query("SELECT COUNT(*) FROM {menu_links} WHERE plid = %d AND hidden = 0 AND mlid != %d",$existing_item['plid'], $existing_item['mlid'])); + db_query("UPDATE {menu_links} SET has_children = %d WHERE mlid = %d", $parent_has_children, $existing_item['plid']); + } +} + +function _menu_link_parents_set(&$item, $parent){ + $i = 1; + while ($i < $item['depth']) { + $p = 'p'. $i++; + $item[$p] = $parent[$p]; + } + $p = 'p'. $i++; + // The parent (p1 - p6) corresponding to the depth always equals the mlid. + $item[$p] = $item['mlid']; + while ($i <= MENU_MAX_DEPTH) { + $p = 'p'. $i++; + $item[$p] = 0; + } } function _menu_router_build($callbacks) {