- Patch #151583 by pwolanin, Shakur, webernet, Arancaytar et al: various menu module fixes.

6.x
Dries Buytaert 2007-07-04 15:49:44 +00:00
parent 8cd661b82b
commit e0fe1ae8eb
5 changed files with 505 additions and 316 deletions

View File

@ -67,7 +67,7 @@
* *
* Everything described so far is stored in the menu_router table. The * Everything described so far is stored in the menu_router table. The
* menu_links table holds the visible menu links. By default these are * menu_links table holds the visible menu links. By default these are
* derived from the same hook_menu definitions, however you are free to * derived from the same hook_menu definitons, however you are free to
* add more with menu_link_save(). * add more with menu_link_save().
*/ */
@ -297,7 +297,6 @@ function menu_get_item($path = NULL) {
/** /**
* Execute the page callback associated with the current path * Execute the page callback associated with the current path
*/ */
function menu_execute_active_handler($path = NULL) { function menu_execute_active_handler($path = NULL) {
if (_menu_site_is_offline()) { if (_menu_site_is_offline()) {
return MENU_SITE_OFFLINE; return MENU_SITE_OFFLINE;
@ -378,6 +377,9 @@ function _menu_check_access(&$item, $map) {
} }
} }
/**
* Localize the item title using t() or another callback.
*/
function _menu_item_localize(&$item) { function _menu_item_localize(&$item) {
// Translate the title to allow storage of English title strings // Translate the title to allow storage of English title strings
// in the database, yet display of them in the language required // in the database, yet display of them in the language required
@ -514,15 +516,15 @@ function _menu_link_translate(&$item) {
_menu_link_map_translate($map, $item['to_arg_functions']); _menu_link_map_translate($map, $item['to_arg_functions']);
$item['href'] = implode('/', $map); $item['href'] = implode('/', $map);
// Note- skip callbacks without real values for their arguments // Note- skip callbacks without real values for their arguments.
if (strpos($item['href'], '%') !== FALSE) { if (strpos($item['href'], '%') !== FALSE) {
$item['access'] = FALSE; $item['access'] = FALSE;
return FALSE; return FALSE;
} }
// TODO: menu_tree_data may set this ahead of time for links to nodes // menu_tree_check_access() may set this ahead of time for links to nodes.
if (!isset($item['access'])) { if (!isset($item['access'])) {
if (!_menu_load_objects($item, $map)) { if (!_menu_load_objects($item, $map)) {
// An error occurred loading an object // An error occured loading an object.
$item['access'] = FALSE; $item['access'] = FALSE;
return FALSE; return FALSE;
} }
@ -606,16 +608,22 @@ function menu_tree_output($tree) {
function menu_tree_all_data($menu_name = 'navigation', $item = NULL, $show_hidden = FALSE) { function menu_tree_all_data($menu_name = 'navigation', $item = NULL, $show_hidden = FALSE) {
static $tree = array(); static $tree = array();
// Use $mlid as a flag for whether the data being loaded is for the whole tree.
$mlid = isset($item['mlid']) ? $item['mlid'] : 0; $mlid = isset($item['mlid']) ? $item['mlid'] : 0;
// Generate the cache ID.
$cid = 'links:'. $menu_name .':all:'. $mlid .':'. (int)$show_hidden; $cid = 'links:'. $menu_name .':all:'. $mlid .':'. (int)$show_hidden;
if (!isset($tree[$cid])) { if (!isset($tree[$cid])) {
// If the static variable doesn't have the data, check {cache_menu}.
$cache = cache_get($cid, 'cache_menu'); $cache = cache_get($cid, 'cache_menu');
if ($cache && isset($cache->data)) { if ($cache && isset($cache->data)) {
$tree[$cid] = $cache->data; $tree[$cid] = $cache->data;
} }
else { else {
// Build and run the query, and build the tree.
if ($mlid) { if ($mlid) {
// The tree is for a single item, so we need to match the values in its
// p columns and 0 (the top level) with the plid values of other links.
$args = array(0, $item['p1'], $item['p2'], $item['p3'], $item['p4'], $item['p5']); $args = array(0, $item['p1'], $item['p2'], $item['p3'], $item['p4'], $item['p5']);
$args = array_unique($args); $args = array_unique($args);
$placeholders = implode(', ', array_fill(0, count($args), '%d')); $placeholders = implode(', ', array_fill(0, count($args), '%d'));
@ -624,26 +632,28 @@ function menu_tree_all_data($menu_name = 'navigation', $item = NULL, $show_hidde
$parents[] = $item['mlid']; $parents[] = $item['mlid'];
} }
else { else {
// Get all links in this menu.
$where = ''; $where = '';
$args = array(); $args = array();
$parents = array(); $parents = array();
} }
if (!$show_hidden) {
$where .= ' AND ml.hidden = 0';
}
else {
$where .= ' AND ml.hidden > 0';
}
array_unshift($args, $menu_name); array_unshift($args, $menu_name);
list(, $tree[$cid]) = _menu_tree_data(db_query(" // Select the links from the table, and recursively build the tree. We
// LEFT JOIN since there is no match in {menu_router} for an external link.
// We need to select links that are visible or hidden (ml.hidden >= 0), but
// not callbacks (ml.hidden < 0), so that we can later exclude all the
// children of a hidden item.
// No need to order by p6 - there is a sort by weight later.
$tree[$cid] = menu_tree_data(db_query("
SELECT m.*, ml.menu_name, ml.mlid, ml.plid, ml.link_path, ml.router_path, ml.hidden, ml.external, ml.has_children, ml.expanded, ml.weight + 50000 AS weight, ml.depth, ml.p1, ml.p2, ml.p3, ml.p4, ml.p5, ml.p6, ml.module, ml.link_title, ml.options SELECT m.*, ml.menu_name, ml.mlid, ml.plid, ml.link_path, ml.router_path, ml.hidden, ml.external, ml.has_children, ml.expanded, ml.weight + 50000 AS weight, ml.depth, ml.p1, ml.p2, ml.p3, ml.p4, ml.p5, ml.p6, ml.module, ml.link_title, ml.options
FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
WHERE ml.menu_name = '%s'". $where ." WHERE ml.menu_name = '%s'". $where ." AND ml.hidden >= 0
ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC", $args), $parents); ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC", $args), $parents);
// Cache the data.
cache_set($cid, $tree[$cid], 'cache_menu'); cache_set($cid, $tree[$cid], 'cache_menu');
} }
// TODO: special case node links and access check via db_rewite_sql() // Check access for the current user to each item in the tree.
_menu_tree_check_access($tree[$cid]); menu_tree_check_access($tree[$cid], $show_hidden);
} }
return $tree[$cid]; return $tree[$cid];
@ -651,7 +661,7 @@ function menu_tree_all_data($menu_name = 'navigation', $item = NULL, $show_hidde
/** /**
* Get the data structure representing a named menu tree, based on the current * Get the data structure representing a named menu tree, based on the current
* page. The tree order is maintained by storing each parent in an individual * page. The tree order is maintained by storing each parent in an invidual
* field, see http://drupal.org/node/141866 for more. * field, see http://drupal.org/node/141866 for more.
* *
* @param $menu_name * @param $menu_name
@ -666,28 +676,38 @@ function menu_tree_all_data($menu_name = 'navigation', $item = NULL, $show_hidde
function menu_tree_page_data($menu_name = 'navigation') { function menu_tree_page_data($menu_name = 'navigation') {
static $tree = array(); static $tree = array();
// Load the menu item corresponding to the current page.
if ($item = menu_get_item()) { if ($item = menu_get_item()) {
// Generate the cache ID.
$cid = 'links:'. $menu_name .':page:'. $item['href'] .':'. (int)$item['access']; $cid = 'links:'. $menu_name .':page:'. $item['href'] .':'. (int)$item['access'];
if (!isset($tree[$cid])) { if (!isset($tree[$cid])) {
// If the static variable doesn't have the data, check {cache_menu}.
$cache = cache_get($cid, 'cache_menu'); $cache = cache_get($cid, 'cache_menu');
if ($cache && isset($cache->data)) { if ($cache && isset($cache->data)) {
$tree[$cid] = $cache->data; $tree[$cid] = $cache->data;
} }
else { else {
// Build and run the query, and build the tree.
if ($item['access']) { if ($item['access']) {
// Check whether a menu link exists that corresponds to the current path.
$parents = db_fetch_array(db_query("SELECT p1, p2, p3, p4, p5, p6 FROM {menu_links} WHERE menu_name = '%s' AND link_path = '%s'", $menu_name, $item['href'])); $parents = db_fetch_array(db_query("SELECT p1, p2, p3, p4, p5, p6 FROM {menu_links} WHERE menu_name = '%s' AND link_path = '%s'", $menu_name, $item['href']));
// We may be on a local task that's not in the links
// TODO how do we handle the case like a local task on a specific node in the menu?
if (empty($parents)) { if (empty($parents)) {
// If no link exists, we may be on a local task that's not in the links.
// TODO: handle the case like a local task on a specific node in the menu.
$parents = db_fetch_array(db_query("SELECT p1, p2, p3, p4, p5, p6 FROM {menu_links} WHERE menu_name = '%s' AND link_path = '%s'", $menu_name, $item['tab_root'])); $parents = db_fetch_array(db_query("SELECT p1, p2, p3, p4, p5, p6 FROM {menu_links} WHERE menu_name = '%s' AND link_path = '%s'", $menu_name, $item['tab_root']));
} }
// We always want all the top-level links with plid == 0.
$parents[] = '0'; $parents[] = '0';
$args = $parents = array_unique($parents); $args = $parents = array_unique($parents);
$placeholders = implode(', ', array_fill(0, count($args), '%d')); $placeholders = implode(', ', array_fill(0, count($args), '%d'));
$expanded = variable_get('menu_expanded', array()); $expanded = variable_get('menu_expanded', array());
// Check whether the current menu has any links set to be expanded.
if (in_array($menu_name, $expanded)) { if (in_array($menu_name, $expanded)) {
// Collect all the links set to be expanded, and then add all their
// children to the list also.
do { do {
$result = db_query("SELECT mlid FROM {menu_links} WHERE expanded != 0 AND has_children != 0 AND menu_name = '%s' AND plid IN (". $placeholders .') AND mlid NOT IN ('. $placeholders .')', array_merge(array($menu_name), $args, $args)); $result = db_query("SELECT mlid FROM {menu_links} WHERE expanded != 0 AND has_children != 0 AND menu_name = '%s' AND plid IN (". $placeholders .') AND mlid NOT IN ('. $placeholders .')', array_merge(array($menu_name), $args, $args));
while ($item = db_fetch_array($result)) { while ($item = db_fetch_array($result)) {
@ -698,23 +718,28 @@ function menu_tree_page_data($menu_name = 'navigation') {
} }
array_unshift($args, $menu_name); array_unshift($args, $menu_name);
} }
// Show the root menu for access denied.
else { else {
$args = array('navigation', '0'); // Show only the top-level menu items when access is denied.
$args = array($menu_name, '0');
$placeholders = '%d'; $placeholders = '%d';
$parents = array(); $parents = array();
} }
// Select the links from the table, and recursively build the tree. We
// LEFT JOIN since there is no match in {menu_router} for an external link. // LEFT JOIN since there is no match in {menu_router} for an external link.
// We need to select links that are visible or hidden (ml.hidden >= 0), but
// not callbacks (ml.hidden < 0), so that we can later exclude all the
// children of a hidden item.
// No need to order by p6 - there is a sort by weight later. // No need to order by p6 - there is a sort by weight later.
list(, $tree[$cid]) = _menu_tree_data(db_query(" $tree[$cid] = menu_tree_data(db_query("
SELECT m.*, ml.menu_name, ml.mlid, ml.plid, ml.link_path, ml.router_path, ml.hidden, ml.external, ml.has_children, ml.expanded, ml.weight + 50000 AS weight, ml.depth, ml.p1, ml.p2, ml.p3, ml.p4, ml.p5, ml.p6, ml.module, ml.link_title, ml.options SELECT m.*, ml.menu_name, ml.mlid, ml.plid, ml.link_path, ml.router_path, ml.hidden, ml.external, ml.has_children, ml.expanded, ml.weight + 50000 AS weight, ml.depth, ml.p1, ml.p2, ml.p3, ml.p4, ml.p5, ml.p6, ml.module, ml.link_title, ml.options
FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
WHERE ml.menu_name = '%s' AND ml.plid IN (". $placeholders .") AND ml.hidden = 0 WHERE ml.menu_name = '%s' AND ml.plid IN (". $placeholders .") AND ml.hidden >= 0
ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC", $args), $parents); ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC", $args), $parents);
// Cache the data.
cache_set($cid, $tree[$cid], 'cache_menu'); cache_set($cid, $tree[$cid], 'cache_menu');
} }
// TODO: special case node links and access check via db_rewite_sql() // Check access for the current user to each item in the tree.
_menu_tree_check_access($tree[$cid]); menu_tree_check_access($tree[$cid]);
} }
return $tree[$cid]; return $tree[$cid];
} }
@ -722,15 +747,34 @@ function menu_tree_page_data($menu_name = 'navigation') {
return array(); return array();
} }
function _menu_tree_check_access(&$tree) { /**
* Check access and perform other dynamic operations for each link in the tree.
*/
function menu_tree_check_access(&$tree, $show_hidden = FALSE) {
// TODO: special case node links and access check via db_rewite_sql().
// TODO: move sorting of siblings in the tree here so that we can sort based on
// the localized title of each link. Currently the dorting is done in
// function _menu_tree_data().
_menu_tree_check_access($tree, $show_hidden);
}
/**
* Recursive helper function for menu_tree_check_access()
*/
function _menu_tree_check_access(&$tree, $show_hidden) {
foreach ($tree as $key => $v) { foreach ($tree as $key => $v) {
$item = &$tree[$key]['link']; $item = &$tree[$key]['link'];
_menu_link_translate($item); if (!$item['hidden'] || $show_hidden) {
_menu_link_translate($item);
}
else {
$item['access'] = FALSE;
}
if (!$item['access']) { if (!$item['access']) {
unset($tree[$key]); unset($tree[$key]);
} }
elseif ($tree[$key]['below']) { elseif ($tree[$key]['below']) {
_menu_tree_check_access($tree[$key]['below']); _menu_tree_check_access($tree[$key]['below'], $show_hidden);
} }
} }
} }
@ -738,10 +782,6 @@ function _menu_tree_check_access(&$tree) {
/** /**
* Build the data representing a menu tree. * Build the data representing a menu tree.
* *
* The function is a bit complex because the rendering of an item depends on
* the next menu item. So we are always rendering the element previously
* processed not the current one.
*
* @param $result * @param $result
* The database result. * The database result.
* @param $parents * @param $parents
@ -749,12 +789,23 @@ function _menu_tree_check_access(&$tree) {
* to the root of the menu tree. * to the root of the menu tree.
* @param $depth * @param $depth
* The depth of the current menu tree. * The depth of the current menu tree.
* @param $previous_element
* The previous menu link in the current menu tree.
* @return * @return
* See menu_tree_data for a description of the data structure. * See menu_tree_page_data for a description of the data structure.
*/ */
function _menu_tree_data($result = NULL, $parents = array(), $depth = 1, $previous_element = '') { function menu_tree_data($result = NULL, $parents = array(), $depth = 1) {
list(, $tree) = _menu_tree_data($result, $parents, $depth);
return $tree;
}
/**
* Recursive helper function to build the data representing a menu tree.
*
* The function is a bit complex because the rendering of an item depends on
* the next menu item. So we are always rendering the element previously
* processed not the current one.
*/
function _menu_tree_data($result, $parents, $depth, $previous_element = '') {
$remnant = NULL; $remnant = NULL;
$tree = array(); $tree = array();
while ($item = db_fetch_array($result)) { while ($item = db_fetch_array($result)) {
@ -763,7 +814,7 @@ function _menu_tree_data($result = NULL, $parents = array(), $depth = 1, $previo
$item['in_active_trail'] = in_array($item['mlid'], $parents); $item['in_active_trail'] = in_array($item['mlid'], $parents);
// The weights are uniform 5 digits because of the 50000 offset in the // The weights are uniform 5 digits because of the 50000 offset in the
// query. We add mlid at the end of the index to insure uniqueness. // query. We add mlid at the end of the index to insure uniqueness.
$index = $previous_element ? ($previous_element['weight'] .' '. $previous_element['title'] . $previous_element['mlid']) : ''; $index = $previous_element ? ($previous_element['weight'] .' '. drupal_strtolower($previous_element['link_title']) . $previous_element['mlid']) : '';
// The current item is the first in a new submenu. // The current item is the first in a new submenu.
if ($item['depth'] > $depth) { if ($item['depth'] > $depth) {
// _menu_tree returns an item and the menu tree structure. // _menu_tree returns an item and the menu tree structure.
@ -798,8 +849,8 @@ function _menu_tree_data($result = NULL, $parents = array(), $depth = 1, $previo
} }
} }
if ($previous_element) { if ($previous_element) {
// We have one more link dangling. // We have one more link dangling
$tree[$previous_element['weight'] .' '. $previous_element['title'] .' '. $previous_element['mlid']] = array( $tree[$previous_element['weight'] .' '. drupal_strtolower($previous_element['link_title']) .' '. $previous_element['mlid']] = array(
'link' => $previous_element, 'link' => $previous_element,
'below' => '', 'below' => '',
); );
@ -833,6 +884,9 @@ function theme_menu_item($link, $has_children, $menu = '', $in_active_trail = FA
return '<li class="'. $class .'">'. $link . $menu .'</li>'."\n"; return '<li class="'. $class .'">'. $link . $menu .'</li>'."\n";
} }
/**
* Generate the HTML output for a single local task link.
*/
function theme_menu_local_task($link, $active = FALSE) { function theme_menu_local_task($link, $active = FALSE) {
return '<li '. ($active ? 'class="active" ' : '') .'>'. $link .'</li>'; return '<li '. ($active ? 'class="active" ' : '') .'>'. $link .'</li>';
} }
@ -886,6 +940,9 @@ function menu_get_names($reset = FALSE) {
return $names; return $names;
} }
/**
* Return an array of links to be rendered as the Primary links.
*/
function menu_primary_links() { function menu_primary_links() {
$tree = menu_tree_page_data('primary-links'); $tree = menu_tree_page_data('primary-links');
$links = array(); $links = array();
@ -898,6 +955,9 @@ function menu_primary_links() {
return $links; return $links;
} }
/**
* Return an array of links to be rendered as the Secondary links.
*/
function menu_secondary_links() { function menu_secondary_links() {
$tree = menu_tree_page_data('secondary-links'); $tree = menu_tree_page_data('secondary-links');
$links = array(); $links = array();
@ -941,7 +1001,7 @@ function menu_local_tasks($level = 0, $return_root = FALSE) {
while ($item = db_fetch_array($result)) { while ($item = db_fetch_array($result)) {
_menu_translate($item, $map, TRUE); _menu_translate($item, $map, TRUE);
if ($item['tab_parent']) { if ($item['tab_parent']) {
// All tabs, but not the root page. // All tabs, but not the root page
$children[$item['tab_parent']][$item['path']] = $item; $children[$item['tab_parent']][$item['path']] = $item;
} }
// Store the translated item for later use. // Store the translated item for later use.
@ -976,7 +1036,7 @@ function menu_local_tasks($level = 0, $return_root = FALSE) {
$tabs[$item['number_parts']]['output'] = $tabs_current; $tabs[$item['number_parts']]['output'] = $tabs_current;
} }
// Find all tabs at the same level or above the current one // Find all tabs at the same level or above the current one.
$parent = $router_item['tab_parent']; $parent = $router_item['tab_parent'];
$path = $router_item['path']; $path = $router_item['path'];
$current = $router_item; $current = $router_item;
@ -1032,10 +1092,16 @@ function menu_local_tasks($level = 0, $return_root = FALSE) {
} }
} }
/**
* Returns the rendered local tasks at the top level.
*/
function menu_primary_local_tasks() { function menu_primary_local_tasks() {
return menu_local_tasks(0); return menu_local_tasks(0);
} }
/**
* Returns the rendered local tasks at the second level.
*/
function menu_secondary_local_tasks() { function menu_secondary_local_tasks() {
return menu_local_tasks(1); return menu_local_tasks(1);
} }
@ -1065,6 +1131,9 @@ function theme_menu_local_tasks() {
return $output; return $output;
} }
/**
* Set (or get) the active menu for the current page - determines the active trail.
*/
function menu_set_active_menu_name($menu_name = NULL) { function menu_set_active_menu_name($menu_name = NULL) {
static $active; static $active;
@ -1077,6 +1146,9 @@ function menu_set_active_menu_name($menu_name = NULL) {
return $active; return $active;
} }
/**
* Get the active menu for the current page - determines the active trail.
*/
function menu_get_active_menu_name() { function menu_get_active_menu_name() {
return menu_set_active_menu_name(); return menu_set_active_menu_name();
} }
@ -1084,6 +1156,9 @@ function menu_get_active_menu_name() {
function menu_set_active_item() { function menu_set_active_item() {
} }
/**
* Set (or get) the active trail for the current page - the path to root in the menu tree..
*/
function menu_set_active_trail($new_trail = NULL) { function menu_set_active_trail($new_trail = NULL) {
static $trail; static $trail;
@ -1121,6 +1196,9 @@ function menu_set_active_trail($new_trail = NULL) {
return $trail; return $trail;
} }
/**
* Get the active trail for the current page - the path to root in the menu tree..
*/
function menu_get_active_trail() { function menu_get_active_trail() {
return menu_set_active_trail(); return menu_set_active_trail();
} }
@ -1128,6 +1206,9 @@ function menu_get_active_trail() {
function menu_set_location() { function menu_set_location() {
} }
/**
* Get the breadcrumb for the current page, as determined by the active trail.
*/
function menu_get_active_breadcrumb() { function menu_get_active_breadcrumb() {
$breadcrumb = array(); $breadcrumb = array();
$item = menu_get_item(); $item = menu_get_item();
@ -1147,6 +1228,9 @@ function menu_get_active_breadcrumb() {
return $breadcrumb; return $breadcrumb;
} }
/**
* Get the title of the current page, as determined by the active trail.
*/
function menu_get_active_title() { function menu_get_active_title() {
$active_trail = menu_get_active_trail(); $active_trail = menu_get_active_trail();
@ -1168,20 +1252,23 @@ function menu_get_active_title() {
* rendering. * rendering.
*/ */
function menu_link_load($mlid) { function menu_link_load($mlid) {
if ($item = db_fetch_array(db_query("SELECT * FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = %d", $mlid))) { if (is_numeric($mlid) && $item = db_fetch_array(db_query("SELECT m.*, ml.* FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = %d", $mlid))) {
_menu_link_translate($item); _menu_link_translate($item);
return $item; return $item;
} }
return FALSE; return FALSE;
} }
/**
* Clears the cached cached data for a single named menu.
*/
function menu_cache_clear($menu_name = 'navigation') { function menu_cache_clear($menu_name = 'navigation') {
cache_clear_all('links:'. $menu_name .':', 'cache_menu', TRUE); cache_clear_all('links:'. $menu_name .':', 'cache_menu', TRUE);
} }
/** /**
* This should be called any time broad changes might have been made to the * Clears all cached menu data. This should be called any time broad changes
* router items or menu links. * might have been made to the router items or menu links.
*/ */
function menu_cache_clear_all() { function menu_cache_clear_all() {
cache_clear_all('*', 'cache_menu', TRUE); cache_clear_all('*', 'cache_menu', TRUE);
@ -1253,13 +1340,15 @@ function _menu_link_build($item) {
return $item; return $item;
} }
/**
* Helper function to build menu links for the items in the menu router.
*/
function _menu_navigation_links_rebuild($menu) { function _menu_navigation_links_rebuild($menu) {
// Add normal and suggested items as links. // Add normal and suggested items as links.
$menu_links = array(); $menu_links = array();
foreach ($menu as $path => $item) { foreach ($menu as $path => $item) {
$item = _menu_link_build($item); if ($item['_visible']) {
// We add nonexisting items. $item = _menu_link_build($item);
if ($item['_visible'] && !db_result(db_query("SELECT COUNT(*) FROM {menu_links} WHERE menu_name = '%s' AND link_path = '%s'", $item['menu_name'], $item['link_path']))) {
$menu_links[$path] = $item; $menu_links[$path] = $item;
$sort[$path] = $item['_number_parts']; $sort[$path] = $item['_number_parts'];
} }
@ -1269,7 +1358,13 @@ function _menu_navigation_links_rebuild($menu) {
array_multisort($sort, SORT_NUMERIC, $menu_links); array_multisort($sort, SORT_NUMERIC, $menu_links);
foreach ($menu_links as $item) { foreach ($menu_links as $item) {
menu_link_save($item); $existing_item = db_fetch_array(db_query("SELECT mlid, customized FROM {menu_links} WHERE menu_name = '%s' AND link_path = '%s' AND module = 'system'", $item['menu_name'], $item['link_path']));
if ($existing_item) {
$item['mlid'] = $existing_item['mlid'];
}
if (!$existing_item || !$existing_item['customized']) {
menu_link_save($item);
}
} }
} }
$placeholders = implode(', ', array_fill(0, count($menu), "'%s'")); $placeholders = implode(', ', array_fill(0, count($menu), "'%s'"));
@ -1297,6 +1392,9 @@ function menu_link_delete($mlid, $path = NULL) {
} }
} }
/**
* Helper function for menu_link_delete; deletes a single menu link.
*/
function _menu_delete_item($item) { function _menu_delete_item($item) {
// System-created items get automatically deleted, but only on menu rebuild. // System-created items get automatically deleted, but only on menu rebuild.
if ($item && $item['module'] != 'system') { if ($item && $item['module'] != 'system') {
@ -1331,8 +1429,7 @@ function _menu_delete_item($item) {
* weight default is 0 * weight default is 0
* expanded whether the item is expanded. * expanded whether the item is expanded.
* options An array of options, @see l for more. * options An array of options, @see l for more.
* mlid If it's an existing item, this comes from the database. * mlid Set to an existing value, or 0 or NULL to insert a new link.
* Never set by hand.
* plid The mlid of the parent. * plid The mlid of the parent.
* router_path The path of the relevant router item. * router_path The path of the relevant router item.
*/ */
@ -1341,8 +1438,10 @@ function menu_link_save(&$item) {
drupal_alter('menu_link', $item, $menu); drupal_alter('menu_link', $item, $menu);
$item['_external'] = menu_path_is_external($item['link_path']); // This is the easiest way to handle the unique internal path '<front>',
// Load defaults. // since a path marked as external does not need to match a router path.
$item['_external'] = menu_path_is_external($item['link_path']) || $item['link_path'] == '<front>';
// Load defaults
$item += array( $item += array(
'menu_name' => 'navigation', 'menu_name' => 'navigation',
'weight' => 0, 'weight' => 0,
@ -1352,19 +1451,13 @@ function menu_link_save(&$item) {
'expanded' => 0, 'expanded' => 0,
'options' => array(), 'options' => array(),
'module' => 'menu', 'module' => 'menu',
'customized' => 0,
); );
$menu_name = $item['menu_name']; $menu_name = $item['menu_name'];
$existing_item = FALSE; $existing_item = FALSE;
if (isset($item['mlid'])) { if (isset($item['mlid'])) {
$existing_item = db_fetch_array(db_query("SELECT * FROM {menu_links} WHERE mlid = %d", $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 link_path = '%s'", $menu_name, $item['link_path']));
}
if ($existing_item) {
$item['mlid'] = $existing_item['mlid'];
}
// Find the parent - it must be in the same menu. // Find the parent - it must be in the same menu.
if (isset($item['plid'])) { if (isset($item['plid'])) {
@ -1378,7 +1471,7 @@ function menu_link_save(&$item) {
} while ($parent === FALSE && $parent_path); } while ($parent === FALSE && $parent_path);
} }
// Menu callbacks need to be in the links table for breadcrumbs, but can't // Menu callbacks need to be in the links table for breadcrumbs, but can't
// be parents if they are generated directly from a router item // be parents if they are generated directly from a router item.
if (empty($parent['mlid']) || $parent['hidden'] < 0) { if (empty($parent['mlid']) || $parent['hidden'] < 0) {
$item['plid'] = 0; $item['plid'] = 0;
} }
@ -1391,15 +1484,15 @@ function menu_link_save(&$item) {
menu_name, plid, link_path, menu_name, plid, link_path,
hidden, external, has_children, hidden, external, has_children,
expanded, weight, expanded, weight,
module, link_title, options) VALUES ( module, link_title, options, customized) VALUES (
'%s', %d, '%s', '%s', %d, '%s',
%d, %d, %d, %d, %d, %d,
%d, %d, %d, %d,
'%s', '%s', '%s')", '%s', '%s', '%s', %d)",
$item['menu_name'], $item['plid'], $item['link_path'], $item['menu_name'], $item['plid'], $item['link_path'],
$item['hidden'], $item['_external'], $item['has_children'], $item['hidden'], $item['_external'], $item['has_children'],
$item['expanded'], $item['weight'], $item['expanded'], $item['weight'],
$item['module'], $item['link_title'], serialize($item['options'])); $item['module'], $item['link_title'], serialize($item['options']), $item['customized']);
$item['mlid'] = db_last_insert_id('menu_links', 'mlid'); $item['mlid'] = db_last_insert_id('menu_links', 'mlid');
} }
@ -1409,7 +1502,7 @@ function menu_link_save(&$item) {
$item['depth'] = 1; $item['depth'] = 1;
} }
else { else {
// Cannot add beyond the maximum depth. // Cannot add beyond the maximum depth
if ($item['has_children'] && $existing_item) { if ($item['has_children'] && $existing_item) {
$limit = MENU_MAX_DEPTH - menu_link_children_relative_depth($existing_item) - 1; $limit = MENU_MAX_DEPTH - menu_link_children_relative_depth($existing_item) - 1;
} }
@ -1450,12 +1543,12 @@ function menu_link_save(&$item) {
router_path = '%s', hidden = %d, external = %d, has_children = %d, router_path = '%s', hidden = %d, external = %d, has_children = %d,
expanded = %d, weight = %d, depth = %d, expanded = %d, weight = %d, depth = %d,
p1 = %d, p2 = %d, p3 = %d, p4 = %d, p5 = %d, p6 = %d, p1 = %d, p2 = %d, p3 = %d, p4 = %d, p5 = %d, p6 = %d,
module = '%s', link_title = '%s', options = '%s' WHERE mlid = %d", module = '%s', link_title = '%s', options = '%s', customized = %d WHERE mlid = %d",
$item['menu_name'], $item['plid'], $item['link_path'], $item['menu_name'], $item['plid'], $item['link_path'],
$item['router_path'], $item['hidden'], $item['_external'], $item['has_children'], $item['router_path'], $item['hidden'], $item['_external'], $item['has_children'],
$item['expanded'], $item['weight'], $item['depth'], $item['expanded'], $item['weight'], $item['depth'],
$item['p1'], $item['p2'], $item['p3'], $item['p4'], $item['p5'], $item['p6'], $item['p1'], $item['p2'], $item['p3'], $item['p4'], $item['p5'], $item['p6'],
$item['module'], $item['link_title'], serialize($item['options']), $item['mlid']); $item['module'], $item['link_title'], serialize($item['options']), $item['customized'], $item['mlid']);
// Check the has_children status of the parent. // Check the has_children status of the parent.
if ($item['plid']) { if ($item['plid']) {
$parent_has_children = (bool)db_result(db_query("SELECT COUNT(*) FROM {menu_links} WHERE plid = %d AND hidden = 0", $item['plid'])); $parent_has_children = (bool)db_result(db_query("SELECT COUNT(*) FROM {menu_links} WHERE plid = %d AND hidden = 0", $item['plid']));
@ -1552,6 +1645,9 @@ function _menu_link_move_children($item, $existing_item) {
} }
} }
/**
* Helper function that sets the p1..p6 values for a menu link being saved.
*/
function _menu_link_parents_set(&$item, $parent) { function _menu_link_parents_set(&$item, $parent) {
$i = 1; $i = 1;
while ($i < $item['depth']) { while ($i < $item['depth']) {
@ -1567,6 +1663,9 @@ function _menu_link_parents_set(&$item, $parent) {
} }
} }
/**
* Helper function to build the router table based on the data from hook_menu.
*/
function _menu_router_build($callbacks) { function _menu_router_build($callbacks) {
// First pass: separate callbacks from paths, making paths ready for // First pass: separate callbacks from paths, making paths ready for
// matching. Calculate fitness, and fill some default values. // matching. Calculate fitness, and fill some default values.
@ -1582,7 +1681,7 @@ function _menu_router_build($callbacks) {
// We store the highest index of parts here to save some work in the fit // We store the highest index of parts here to save some work in the fit
// calculation loop. // calculation loop.
$slashes = $number_parts - 1; $slashes = $number_parts - 1;
// extract functions // Extract load and to_arg functions.
foreach ($parts as $k => $part) { foreach ($parts as $k => $part) {
$match = FALSE; $match = FALSE;
if (preg_match('/^%([a-z_]*)$/', $part, $matches)) { if (preg_match('/^%([a-z_]*)$/', $part, $matches)) {
@ -1646,7 +1745,7 @@ function _menu_router_build($callbacks) {
foreach ($menu as $path => $v) { foreach ($menu as $path => $v) {
$item = &$menu[$path]; $item = &$menu[$path];
if (!isset($item['access callback']) && isset($item['access arguments'])) { if (!isset($item['access callback']) && isset($item['access arguments'])) {
$item['access callback'] = 'user_access'; // Default callback $item['access callback'] = 'user_access'; // Default callback.
} }
if (!$item['_tab']) { if (!$item['_tab']) {
// Non-tab items // Non-tab items
@ -1740,6 +1839,9 @@ function _menu_router_build($callbacks) {
return $menu; return $menu;
} }
/**
* Returns TRUE if a path is external (e.g. http://example.com).
*/
function menu_path_is_external($path) { function menu_path_is_external($path) {
$colonpos = strpos($path, ':'); $colonpos = strpos($path, ':');
return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) == check_plain($path); return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) == check_plain($path);
@ -1749,11 +1851,11 @@ function menu_path_is_external($path) {
* Returns TRUE if the site is off-line for maintenance. * Returns TRUE if the site is off-line for maintenance.
*/ */
function _menu_site_is_offline() { function _menu_site_is_offline() {
// Check if site is set to off-line mode // Check if site is set to off-line mode.
if (variable_get('site_offline', 0)) { if (variable_get('site_offline', 0)) {
// Check if the user has administration privileges // Check if the user has administration privileges.
if (!user_access('administer site configuration')) { if (!user_access('administer site configuration')) {
// Check if this is an attempt to login // Check if this is an attempt to login.
if (drupal_get_normal_path($_GET['q']) != 'user') { if (drupal_get_normal_path($_GET['q']) != 'user') {
return TRUE; return TRUE;
} }

View File

@ -24,7 +24,7 @@ Menu administration tabs:
return $output; return $output;
case 'admin/build/menu': case 'admin/build/menu':
return '<p>'. t('Menus are a collection of links (menu items) used to navigate a website. The list(s) below display the currently available menus along with their menu items. Select an operation from the list to manage each menu or menu item.', array('@admin-settings-menus' => url('admin/build/menu/settings'), '@admin-block' => url('admin/build/block'))) .'</p>'; return '<p>'. t('Menus are a collection of links (menu items) used to navigate a website. The list(s) below display the currently available menus along with their menu items. Select an operation from the list to manage each menu or menu item.', array('@admin-settings-menus' => url('admin/build/menu/settings'), '@admin-block' => url('admin/build/block'))) .'</p>';
case 'admin/build/menu/menu/add': case 'admin/build/menu/add':
return '<p>'. t('Enter the name for your new menu. Remember to enable the newly created block in the <a href="@blocks">blocks administration page</a>.', array('@blocks' => url('admin/build/block'))) .'</p>'; return '<p>'. t('Enter the name for your new menu. Remember to enable the newly created block in the <a href="@blocks">blocks administration page</a>.', array('@blocks' => url('admin/build/block'))) .'</p>';
case 'admin/build/menu/item/add': case 'admin/build/menu/item/add':
return '<p>'. t('Enter the title, path, position and the weight for your new menu item.') .'</p>'; return '<p>'. t('Enter the title, path, position and the weight for your new menu item.') .'</p>';
@ -45,150 +45,169 @@ function menu_menu() {
$items['admin/build/menu'] = array( $items['admin/build/menu'] = array(
'title' => 'Menus', 'title' => 'Menus',
'description' => "Control your site's navigation menu, primary links and secondary links. as well as rename and reorganize menu items.", 'description' => "Control your site's navigation menu, primary links and secondary links. as well as rename and reorganize menu items.",
'page callback' => 'system_admin_menu_block_page', 'page callback' => 'menu_overview_page',
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
'access callback' => 'user_access', 'access callback' => 'user_access',
'access arguments' => array('administer menu'), 'access arguments' => array('administer menu'),
); );
$result = db_query('SELECT * FROM {menu_custom} ORDER BY title');
while ($menu = db_fetch_object($result)) {
$items['admin/build/menu/'. $menu->menu_name] = array(
'title' => $menu->title,
'page callback' => 'menu_overview',
'page arguments' => array($menu->menu_name),
'description' => $menu->description,
);
$items['admin/build/menu/'. $menu->menu_name .'/list'] = array(
'title' => 'List items',
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/build/menu/'. $menu->menu_name .'/add'] = array(
'title' => 'Add item',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_edit_item', 'add', $menu->menu_name),
'type' => MENU_LOCAL_TASK);
$items['admin/build/menu/'. $menu->menu_name .'/edit'] = array(
'title' => 'Edit menu',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_edit_menu', 'edit', $menu->menu_name),
'type' => MENU_LOCAL_TASK);
}
$items['admin/build/menu/list'] = array( $items['admin/build/menu/list'] = array(
'title' => 'List menus', 'title' => 'List menus',
'type' => MENU_DEFAULT_LOCAL_TASK, 'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10, 'weight' => -10);
); $items['admin/build/menu/add'] = array(
$items['admin/build/menu/menu/add'] = array(
'title' => 'Add menu', 'title' => 'Add menu',
'page callback' => 'drupal_get_form', 'page callback' => 'drupal_get_form',
'page arguments' => array('menu_edit_menu', 'add'), 'page arguments' => array('menu_edit_menu', 'add'),
'type' => MENU_LOCAL_TASK); 'type' => MENU_LOCAL_TASK);
$items['admin/build/menu/item/disable'] = array(
'title' => 'Disable menu item',
'page callback' => 'menu_flip_item',
'page arguments' => array(TRUE),
'type' => MENU_CALLBACK,
);
$items['admin/build/menu/item/enable'] = array(
'title' => 'Enable menu item',
'page callback' => 'menu_flip_item',
'page arguments' => array(FALSE),
'type' => MENU_CALLBACK,
);
$items['admin/build/menu/item/edit'] = array(
'title' => 'Edit menu item',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_edit_item', 'edit', 5),
'type' => MENU_CALLBACK);
$items['admin/build/menu/item/reset'] = array(
'title' => 'Reset menu item',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_reset_item'),
'type' => MENU_CALLBACK);
$items['admin/build/menu/item/delete'] = array(
'title' => 'Delete menu item',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_item_delete_form'),
'type' => MENU_CALLBACK);
$items['admin/build/menu/menu/edit'] = array(
'title' => 'Edit menu',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_edit_menu', 'edit'),
'type' => MENU_CALLBACK);
$items['admin/build/menu/menu/delete'] = array(
'title' => 'Delete menu',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_item_delete_form'),
'type' => MENU_CALLBACK);
$items['admin/build/menu/settings'] = array( $items['admin/build/menu/settings'] = array(
'title' => 'Settings', 'title' => 'Settings',
'page callback' => 'drupal_get_form', 'page callback' => 'drupal_get_form',
'page arguments' => array('menu_configure'), 'page arguments' => array('menu_configure'),
'type' => MENU_LOCAL_TASK, 'type' => MENU_LOCAL_TASK,
'weight' => 5, 'weight' => 5);
);
$items['admin/build/menu-customize/%menu'] = array(
'title' => 'Customize menu',
'page callback' => 'menu_overview',
'page arguments' => array(3),
'access arguments' => array('administer menu'),
'type' => MENU_CALLBACK);
$items['admin/build/menu-customize/%menu/list'] = array(
'title' => 'List items',
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK);
$items['admin/build/menu-customize/%menu/add'] = array(
'title' => 'Add item',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_edit_item', 'add', NULL, 3),
'type' => MENU_LOCAL_TASK);
$items['admin/build/menu-customize/%menu/edit'] = array(
'title' => 'Edit menu',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_edit_menu', 'edit', 3),
'type' => MENU_LOCAL_TASK);
$items['admin/build/menu/item/%menu_link/disable'] = array(
'title' => 'Disable menu item',
'page callback' => 'menu_flip_item',
'page arguments' => array(TRUE, 4),
'type' => MENU_CALLBACK);
$items['admin/build/menu/item/%menu_link/enable'] = array(
'title' => 'Enable menu item',
'page callback' => 'menu_flip_item',
'page arguments' => array(FALSE, 4),
'type' => MENU_CALLBACK);
$items['admin/build/menu/item/%menu_link/edit'] = array(
'title' => 'Edit menu item',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_edit_item', 'edit', 4, NULL),
'type' => MENU_CALLBACK);
$items['admin/build/menu/item/%menu_link/reset'] = array(
'title' => 'Reset menu item',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_reset_item', 4),
'type' => MENU_CALLBACK);
$items['admin/build/menu/item/%menu_link/delete'] = array(
'title' => 'Delete menu item',
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_item_delete_form', 4),
'type' => MENU_CALLBACK);
return $items; return $items;
} }
/**
* Implementation of hook_enable()
*
* Add a link for each custom menu.
*/
function menu_enable() {
menu_rebuild();
$result = db_query("SELECT * FROM {menu_custom}");
$link['module'] = 'menu';
$link['plid'] = db_result(db_query("SELECT mlid from {menu_links} WHERE menu_name = 'navigation' AND link_path = 'admin/build/menu'"));
$link['router_path'] = 'admin/build/menu-customize/%';
while ($menu = db_fetch_array($result)) {
$link['mlid'] = 0;
$link['link_title'] = $menu['title'];
$link['link_path'] = 'admin/build/menu-customize/'. $menu['menu_name'];
menu_link_save($link);
}
}
/**
* Load the data for a single custom menu.
*/
function menu_load($menu_name) {
return db_fetch_array(db_query("SELECT * FROM {menu_custom} WHERE menu_name = '%s'", $menu_name));
}
/**
* Menu callback which shows an overview page of all the custom menus and their descriptions.
*/
function menu_overview_page() {
$result = db_query("SELECT * FROM {menu_custom}");
$content = array();
while ($menu = db_fetch_array($result)) {
$menu['href'] = 'admin/build/menu-customize/'. $menu['menu_name'];
$menu['options'] = array();
$content[] = $menu;
}
return theme('admin_block_content', $content);
}
/** /**
* Menu callback which displays every menu element accessible to the current * Menu callback which displays every menu element accessible to the current
* user and the relevant operations. * user and the relevant operations.
*/ */
function menu_overview($menu_name) { function menu_overview($menu) {
$header = array(t('Menu item'), t('Expanded'), array('data' => t('Operations'), 'colspan' => '3')); $header = array(t('Menu item'), t('Expanded'), array('data' => t('Operations'), 'colspan' => '3'));
$sql =" $sql ="
SELECT *, ml.weight + 50000 AS weight FROM {menu_links} ml SELECT m.*, ml.menu_name, ml.mlid, ml.plid, ml.link_path, ml.router_path, ml.hidden, ml.external, ml.has_children, ml.expanded, ml.weight + 50000 AS weight, ml.depth, ml.customized, ml.module, ml.link_title, ml.options
LEFT JOIN {menu_router} m ON m.path = ml.router_path FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
WHERE menu_name = '%s' AND hidden >= 0 WHERE ml.menu_name = '%s' AND ml.hidden >= 0
ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC"; ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC";
$sql_count = "SELECT COUNT(*) FROM {menu_links} ml WHERE menu_name = '%s' AND hidden >= 0"; $sql_count = "SELECT COUNT(*) FROM {menu_links} ml WHERE menu_name = '%s' AND hidden >= 0";
$result = pager_query($sql, 200, 0, $sql_count, $menu_name); $result = pager_query($sql, 200, 0, $sql_count, $menu['menu_name']);
list(, $tree) = _menu_tree_data($result); $tree = menu_tree_data($result);
menu_tree_check_access($tree, TRUE);
$rows = _menu_overview_tree($tree); $rows = _menu_overview_tree($tree);
$output = theme('table', $header, $rows); $output = theme('table', $header, $rows);
$output .= theme('pager', NULL, 200, 0); $output .= theme('pager', NULL, 200, 0);
return $output; return $output;
} }
/**
* Recursive helper function for menu_overview().
*/
function _menu_overview_tree($tree) { function _menu_overview_tree($tree) {
static $rows = array(); static $rows = array();
foreach ($tree as $data) { foreach ($tree as $data) {
$title = ''; $title = '';
if ($item = $data['link']) { if ($item = $data['link']) {
_menu_link_translate($item);
if (!$item['access']) {
continue;
}
$title = str_repeat('&nbsp;&nbsp;', $item['depth'] - 1) . ($item['depth'] > 1 ? '-&nbsp;' : ''); $title = str_repeat('&nbsp;&nbsp;', $item['depth'] - 1) . ($item['depth'] > 1 ? '-&nbsp;' : '');
$title .= l($item['link_title'], $item['href'], $item['options']); $title .= l($item['link_title'], $item['href'], $item['options']);
// Populate the operations field. // Populate the operations field.
$operations = array(); $operations = array();
// Set the edit column. // Set the edit column.
$operations[] = array('data' => l(t('edit'), 'admin/build/menu/item/edit/'. $item['mlid'])); $operations[] = array('data' => l(t('edit'), 'admin/build/menu/item/'. $item['mlid'] .'/edit'));
if ($item['hidden']) { if ($item['hidden']) {
$title .= ' ('. t('disabled') .')'; $title .= ' ('. t('disabled') .')';
$class = 'menu-disabled'; $class = 'menu-disabled';
$operations[] = array('data' => l(t('enable'), 'admin/build/menu/item/enable/'. $item['mlid'])); $operations[] = array('data' => l(t('enable'), 'admin/build/menu/item/'. $item['mlid'] .'/enable'));
} }
else { else {
$class = 'menu-enabled'; $class = 'menu-enabled';
$operations[] = array('data' => l(t('disable'), 'admin/build/menu/item/disable/'. $item['mlid'])); $operations[] = array('data' => l(t('disable'), 'admin/build/menu/item/'. $item['mlid'] .'/disable'));
} }
// Only items created by the menu module can be deleted. // Only items created by the menu module can be deleted.
if ($item['module'] == 'menu') { if ($item['module'] == 'menu') {
$operations[] = array('data' => l(t('delete'), 'admin/build/menu/item/delete/'. $item['mlid'])); $operations[] = array('data' => l(t('delete'), 'admin/build/menu/item/'. $item['mlid'] .'/delete'));
} }
// Set the reset column. // Set the reset column.
else if ($item['module'] == 'system') { elseif ($item['module'] == 'system' && $item['customized']) {
$operations[] = array('data' => l(t('reset'), 'admin/build/menu/item/reset/'. $item['mlid'])); $operations[] = array('data' => l(t('reset'), 'admin/build/menu/item/'. $item['mlid'] .'/reset'));
} }
else { else {
$operations[] = array('data' => ''); $operations[] = array('data' => '');
@ -211,55 +230,39 @@ function _menu_overview_tree($tree) {
} }
/** /**
* Menu callback; enable/disable a menu item. * Menu callback; enable/disable a menu link.
* *
* @param $hide * @param $hide
* TRUE to not show in the menu tree. FALSE to make the item and its children * TRUE to not show in the menu tree. FALSE to make the item and its children
* reappear in menu tree. * reappear in menu tree.
* @param $mlid * @param $item
* mlid of the menu item. * The menu item.
*/ */
function menu_flip_item($hide, $mlid) { function menu_flip_item($hide, $item) {
if (!($item = menu_link_load($mlid))) {
drupal_not_found();
return;
}
$item['hidden'] = (bool)$hide; $item['hidden'] = (bool)$hide;
$item['customized'] = 1;
menu_link_save($item); menu_link_save($item);
drupal_set_message($hide ? t('The menu item has been disabled.') : t('The menu item has been enabled.')); drupal_set_message($hide ? t('The menu item has been disabled.') : t('The menu item has been enabled.'));
drupal_goto('admin/build/menu/'. $item['menu_name']); drupal_goto('admin/build/menu-customize/'. $item['menu_name']);
} }
/** /**
* Menu callback; present the menu item editing form. * Menu callback; Build the menu link editing form.
*/ */
function menu_edit_item(&$form_state, $type, $id = 0) { function menu_edit_item(&$form_state, $type, $item, $menu) {
if ($type == 'edit') {
if (!($item = menu_link_load($id))) { if ($type == 'add' || empty($item)) {
drupal_not_found(); // This is an add form, initialize the menu link.
return; $item = array('link_title' => '', 'mlid' => 0, 'plid' => 0, 'menu_name' => $menu['menu_name'], 'weight' => 0, 'link_path' => '', 'options' => array(), 'module' => 'menu', 'expanded' => 0, 'hidden' => 0, 'has_children' => 0);
}
} }
else { foreach (array('link_path', 'mlid', 'module', 'hidden', 'menu_name', 'has_children', 'options') as $key) {
// This is an add form.
// The mlid argument (if set) will be the default pid to use.
$item = array('mlid' => 0, 'plid' => 0, 'menu_name' => $id, 'weight' => 0, 'link_title' => '', 'link_path' => '', 'options' => array(), 'module' => 'menu', 'expanded' => 0, 'hidden' => 0, 'has_children' => 0);
}
foreach (array('link_path', 'mlid', 'module', 'hidden', 'menu_name', 'has_children') as $key) {
$form[$key] = array('#type' => 'value', '#value' => $item[$key]); $form[$key] = array('#type' => 'value', '#value' => $item[$key]);
} }
// Any item created or edited via this interface is considered "customized".
$form['customized'] = array('#type' => 'value', '#value' => 1);
$form['original_item'] = array('#type' => 'value', '#value' => $item);
$form['link_title'] = array('#type' => 'textfield',
'#title' => t('Title'),
'#default_value' => $item['link_title'],
'#description' => t('The name of the menu item.'),
'#required' => TRUE,
);
$form['description'] = array(
'#type' => 'textarea',
'#title' => t('Description'),
'#default_value' => isset($item['options']['attributes']['title']) ? $item['options']['attributes']['title'] : '',
);
if ($item['module'] == 'menu') { if ($item['module'] == 'menu') {
$form['link_path'] = array( $form['link_path'] = array(
'#type' => 'textfield', '#type' => 'textfield',
@ -276,8 +279,19 @@ function menu_edit_item(&$form_state, $type, $id = 0) {
'#description' => l($item['link_title'], $item['href'], $item['options']), '#description' => l($item['link_title'], $item['href'], $item['options']),
); );
} }
$form['original_item'] = array('#type' => 'value', '#value' => $item); $form['link_title'] = array('#type' => 'textfield',
'#title' => t('Menu link title'),
'#default_value' => $item['link_title'],
'#description' => t('The link text corresponding to this item that should appear in the menu.'),
'#required' => TRUE,
);
$form['description'] = array(
'#type' => 'textarea',
'#title' => t('Description'),
'#default_value' => isset($item['options']['attributes']['title']) ? $item['options']['attributes']['title'] : '',
'#rows' => 1,
'#description' => t('The description displayed when hovering over a menu item.'),
);
$form['expanded'] = array( $form['expanded'] = array(
'#type' => 'checkbox', '#type' => 'checkbox',
'#title' => t('Expanded'), '#title' => t('Expanded'),
@ -286,7 +300,7 @@ function menu_edit_item(&$form_state, $type, $id = 0) {
); );
// Generate a list of possible parents (not including this item or descendants). // Generate a list of possible parents (not including this item or descendants).
$options = menu_parent_options($item['mlid'], $item['menu_name']); $options = menu_parent_options($item['menu_name'], $item);
$form['plid'] = array( $form['plid'] = array(
'#type' => 'select', '#type' => 'select',
'#title' => t('Parent item'), '#title' => t('Parent item'),
@ -296,7 +310,7 @@ function menu_edit_item(&$form_state, $type, $id = 0) {
$form['weight'] = array( $form['weight'] = array(
'#type' => 'weight', '#type' => 'weight',
'#title' => t('Weight'), '#title' => t('Weight'),
'#default_value' => isset($item['weight']) ? $item['weight'] : 0, '#default_value' => $item['weight'],
'#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'), '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
); );
$form['submit'] = array('#type' => 'submit', '#value' => t('Submit')); $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
@ -304,14 +318,13 @@ function menu_edit_item(&$form_state, $type, $id = 0) {
return $form; return $form;
} }
/**
* Validate form values for a menu link being added or edited.
*/
function menu_edit_item_validate($form, &$form_state) { function menu_edit_item_validate($form, &$form_state) {
$item = $form_state['values']; $item = $form_state['values'];
if (isset($item['link_path']) && !menu_path_is_external($item['link_path'])) { if (!trim($item['link_path']) || !menu_valid_path($item)) {
$path = $item['link_path']; form_set_error('link_path', t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $item['link_path'])));
$item = menu_get_item($path);
if (!$item || !$item['access']) {
form_set_error('path', t('This path is either invalid or you do not have access to it'));
}
} }
} }
@ -319,83 +332,56 @@ function menu_edit_item_validate($form, &$form_state) {
* Process menu and menu item add/edit form submissions. * Process menu and menu item add/edit form submissions.
*/ */
function menu_edit_item_submit($form, &$form_state) { function menu_edit_item_submit($form, &$form_state) {
$form_state['values']['options']['attributes']['title'] = $form_state['values']['description'];
menu_link_save($form_state['values']); menu_link_save($form_state['values']);
$form_state['redirect'] = 'admin/build/menu/'. $form_state['values']['menu_name']; $form_state['redirect'] = 'admin/build/menu-customize/'. $form_state['values']['menu_name'];
} }
/** /**
* Return a list of menu items that are valid possible parents for the * Return a list of menu items that are valid possible parents for the
* given menu item. The list excludes the given item and its children. * given menu item. The list excludes the given item and its children.
* *
* @param $mlid
* The menu item id for which to generate a list of parents.
* If $mlid == 0 then the complete tree is returned.
* @param $menu_name * @param $menu_name
* The name of the menu. * The name of the menu.
* @param $plid * @param $item
* The menu link item id of the menu item at which to start the tree. * The menu item for which to generate a list of parents.
* If $pid > 0 then this item will be included in the tree. * If $item['mlid'] == 0 or NULL then the complete tree is returned.
* @param $depth
* The current depth in the tree - used when recursing to indent the tree.
* @return * @return
* An array of menu titles keyed on the mlid. * An array of menu link titles keyed on the mlid.
*/ */
function menu_parent_options($mlid, $menu_name, $plid = 0, $depth = 0) { function menu_parent_options($menu_name, $item) {
$options = array(0 => t('Root'));
// Exclude $mlid and its children from the list unless $mlid is 0.
if ($mlid && $mlid == $plid) {
return $options;
}
$sql = "SELECT * FROM {menu_links} ml LEFT JOIN {menu_router} mr ON ml.router_path = mr.path WHERE menu_name = '%s' AND hidden >= 0"; $tree = menu_tree_all_data($item['menu_name'], NULL, TRUE);
$params = array($menu_name); $options = array(0 => '<'. t('root') .'>');
if ($mlid && ($item = menu_link_load($mlid))) { _menu_parents_recurse($tree, '--', $options, $item['mlid']);
$parents = array();
for ($i = 1; $i <= 6; $i++) {
$key = "p$i";
$value = $item[$key];
if ($value) {
$parents[]= "$key != %d";
$params[] = $value;
}
else {
break;
}
}
$sql .= ' AND (' . implode(' OR ', $parents) .')';
}
$sql .= ' ORDER BY p1, p2, p3, p4, p5';
$result = db_query($sql, $params);
while ($item = db_fetch_array($result)) {
_menu_link_translate($item);
if (!$item['access']) {
continue;
}
$title = str_repeat('--', $item['depth']) .' '. $item['link_title'];
if ($item['hidden']) {
$title .= ' ('. t('disabled') .')';
}
$options[$item['mlid']] = $title;
}
return $options; return $options;
} }
/** /**
* Remove the menu item. * Recursive helper function for menu_parent_options().
*/ */
function menu_node_form_delete($node) { function _menu_parents_recurse($tree, $indent, &$options, $exclude) {
menu_link_delete(NULL, 'node/'. $node->nid); foreach ($tree as $data) {
if ($data['link']['mlid'] != $exclude) {
$title = $indent .' '. truncate_utf8($data['link']['title'], 30, TRUE, FALSE);
if ($data['link']['hidden']) {
$title .= ' ('. t('disabled') .')';
}
$options[$data['link']['mlid']] = $title;
if ($data['below'] && $data['link']['depth'] < MENU_MAX_DEPTH - 1) {
_menu_parents_recurse($data['below'], $indent .'--', $options, $exclude);
}
}
}
} }
/** /**
* Menu callback; handle the adding/editing of a new menu. * Menu callback; Build the form that handles the adding/editing of a custom menu.
*/ */
function menu_edit_menu(&$form_state, $type, $menu_name = '') { function menu_edit_menu(&$form_state, $type, $menu = array()) {
if ($type == 'edit') { if ($type == 'edit') {
$menu = db_fetch_array(db_query("SELECT * FROM {menu_custom} WHERE menu_name = '%s'", $menu_name)); $form['menu_name'] = array('#type' => 'value', '#value' => $menu['menu_name']);
$form['menu_name'] = array('#type' => 'value', '#value' => $menu_name);
$form['#insert'] = FALSE; $form['#insert'] = FALSE;
} }
else { else {
@ -403,7 +389,7 @@ function menu_edit_menu(&$form_state, $type, $menu_name = '') {
$form['menu_name'] = array( $form['menu_name'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Menu name'), '#title' => t('Menu name'),
'#description' => t('The machine-readable name of this menu. This text will be used for constructing the URL of the <em>menu overwrite</em> page for this menu. This name may consist of only of lowercase letters, numbers and hyphens and must be unique.'), '#description' => t('The machine-readable name of this menu. This text will be used for constructing the URL of the <em>menu overview</em> page for this menu. This name may consist of only of lowercase letters, numbers and hypens and must be unique.'),
'#required' => TRUE, '#required' => TRUE,
); );
$form['#insert'] = TRUE; $form['#insert'] = TRUE;
@ -428,10 +414,13 @@ function menu_edit_menu(&$form_state, $type, $menu_name = '') {
return $form; return $form;
} }
/**
* Validates the human and machine-readable names when adding or editing a menu.
*/
function menu_edit_menu_validate($form, &$form_state) { function menu_edit_menu_validate($form, &$form_state) {
$item = $form_state['values']; $item = $form_state['values'];
if (preg_match('/[^a-z0-9-]/', $item['menu_name'])) { if (preg_match('/[^a-z0-9-]/', $item['menu_name'])) {
form_set_error('menu_name', t('Menu name may consist of only of lowercase letters, numbers and hyphens.')); form_set_error('menu_name', t('Menu name may consist only of lowercase letters, numbers and hypens.'));
} }
if ($form['#insert'] && if ($form['#insert'] &&
(db_result(db_query("SELECT menu_name FROM {menu_custom} WHERE menu_name = '%s'", $item['menu_name'])) || (db_result(db_query("SELECT menu_name FROM {menu_custom} WHERE menu_name = '%s'", $item['menu_name'])) ||
@ -440,31 +429,44 @@ function menu_edit_menu_validate($form, &$form_state) {
} }
} }
/**
* Submit function for adding or editing a custom menu.
*/
function menu_edit_menu_submit($form, &$form_state) { function menu_edit_menu_submit($form, &$form_state) {
$menu = $form_state['values']; $menu = $form_state['values'];
$redirect = 'admin/build/menu/'. $menu['menu_name']; // Append 'menu' to the menu name to help avoid name-space conflicts.
$menu['menu_name'] = 'menu-'. $menu['menu_name'];
$redirect = 'admin/build/menu-customize/'. $menu['menu_name'];
$link['link_title'] = $menu['title'];
$link['router_path'] = 'admin/build/menu-customize/%';
$link['module'] = 'menu';
$link['link_path'] = $redirect;
if ($form['#insert']) { if ($form['#insert']) {
$link['plid'] = db_result(db_query("SELECT mlid from {menu_links} WHERE menu_name = 'navigation' AND link_path = 'admin/build/menu'"));
menu_link_save($link);
db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('%s', '%s', '%s')", $menu['menu_name'], $menu['title'], $menu['description']); db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('%s', '%s', '%s')", $menu['menu_name'], $menu['title'], $menu['description']);
} }
else { else {
db_query("UPDATE {menu_custom} SET title = '%s', description = '%s' WHERE menu_name = '%s'", $menu['title'], $menu['description'], $menu['menu_name']); db_query("UPDATE {menu_custom} SET title = '%s', description = '%s' WHERE menu_name = '%s'", $menu['title'], $menu['description'], $menu['menu_name']);
db_query("UPDATE {menu_links} SET link_title = '%s' WHERE link_title = '%s' AND router_path = '%s'", $menu['title'], $form['#title'], $redirect); $result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = '%s'", $link['link_path']);
while ($m = db_fetch_array($result)) {
$link = menu_link_load($m['mlid']);
$link['link_title'] = $menu['title'];
menu_link_save($link);
}
} }
menu_rebuild();
$form_state['redirect'] = $redirect; $form_state['redirect'] = $redirect;
} }
/** /**
* Menu callback; delete a single custom item. * Menu callback; Build a confirm form for deletion of a single menu link.
*/ */
function menu_item_delete_form(&$form_state, $mlid) { function menu_item_delete_form(&$form_state, $item) {
if (!$item = menu_link_load($mlid)) { if ($item['module'] == 'system') {
drupal_not_found(); drupal_access_denied();
return;
} }
$form['#item'] = $item; $form['#item'] = $item;
return confirm_form($form, t('Are you sure you want to delete the custom menu item %item?', array('%item' => $item['link_title'])), 'admin/build/menu-customize/'. $item['menu_name']);
return confirm_form($form, t('Are you sure you want to delete the custom menu item %item?', array('%item' => $item['link_title'])), 'admin/build/menu/'. $item['menu_name'], t('This action cannot be undone.'), t('Delete'));
} }
/** /**
@ -476,40 +478,37 @@ function menu_item_delete_form_submit($form, &$form_state) {
$t_args = array('%title' => $item['link_title']); $t_args = array('%title' => $item['link_title']);
drupal_set_message(t('The menu item %title has been deleted.', $t_args)); drupal_set_message(t('The menu item %title has been deleted.', $t_args));
watchdog('menu', 'Deleted menu item %title.', $t_args, WATCHDOG_NOTICE); watchdog('menu', 'Deleted menu item %title.', $t_args, WATCHDOG_NOTICE);
$form_state['redirect'] = 'admin/build/menu/'. $item['menu_name']; $form_state['redirect'] = 'admin/build/menu-customize/'. $item['menu_name'];
} }
/** /**
* Menu callback; reset a single modified item. * Menu callback; reset a single modified item.
*/ */
function menu_reset_item(&$form_state, $mlid) { function menu_reset_item(&$form_state, $item) {
if (isset($mlid) && $item = db_fetch_array(db_query('SELECT router_path, link_title FROM {menu_links} WHERE mlid = %d', $mlid))) { $form['item'] = array('#type' => 'value', '#value' => $item);
$form['#router_path'] = $item['router_path'];
$options = array( $options = array(
'description' => t('Any customizations will be lost. This action cannot be undone.'), 'description' => t('Any customizations will be lost. This action cannot be undone.'),
'yes' => t('Reset') 'yes' => t('Reset')
); );
return confirm_form($form, t('Are you sure you want to reset the item %item to its default values?', array('%item' => $item['link_title'])), 'admin/build/menu-customize/'. $item['menu_name'], $options);
return confirm_form($form, t('Are you sure you want to reset the item %item to its default values?', array('%item' => $item['link_title'])), 'admin/build/menu', $options);
}
else {
drupal_not_found();
}
} }
/** /**
* Process menu reset item form submissions. * Process menu reset item form submissions.
*/ */
function menu_reset_item_submit($form, &$form_state) { function menu_reset_item_submit($form, &$form_state) {
$new_item = db_fetch_array(db_query("SELECT * FROM {menu_router} WHERE path = '%s'", $form['#router_path'])); $item = $form_state['values']['item'];
menu_link_save(_menu_link_build($new_item)); $router = menu_router_build();
$new_item = _menu_link_build($router[$item['router_path']]);
foreach (array('mlid', 'has_children') as $key) {
$new_item[$key] = $item[$key];
}
menu_link_save($new_item);
drupal_set_message(t('The menu item was reset to its default settings.')); drupal_set_message(t('The menu item was reset to its default settings.'));
$form_state['redirect'] = 'admin/build/menu/navigation'; $form_state['redirect'] = 'admin/build/menu-customize/'. $new_item['menu_name'];
} }
// Conversion ends here.
/** /**
* Implementation of hook_block(). * Implementation of hook_block().
*/ */
@ -533,24 +532,47 @@ function menu_block($op = 'list', $delta = 0) {
/** /**
* Implementation of hook_nodeapi(). * Implementation of hook_nodeapi().
*/ */
function menu_nodeapi($node, $op) { function menu_nodeapi(&$node, $op) {
if (user_access('administer menu') && isset($node->menu)) { switch ($op) {
$item = $node->menu; case 'insert':
switch ($op) { case 'update':
case 'delete': if (isset($node->menu)) {
$item['delete'] = 1; $item = $node->menu;
// Deliberate no break. if (!empty($item['delete'])) {
case 'insert': menu_link_delete($item['mlid']);
case 'update':
if ($item['delete']) {
_menu_delete_item($item);
} }
else { elseif (trim($item['link_title'])) {
$item['link_title'] = trim($item['link_title']);
$item['link_path'] = "node/$node->nid"; $item['link_path'] = "node/$node->nid";
if (!$item['customized']) {
$item['options']['attributes']['title'] = trim($node->title);
}
menu_link_save($item); menu_link_save($item);
} }
break; }
} break;
case 'delete':
// Delete all menu module links that point to this node.
$result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = 'node/%d' AND module = 'menu'", $node->nid);
while ($m = db_fetch_array($result)) {
menu_link_delete($m['mlid']);
}
break;
case 'prepare':
if (empty($node->menu)) {
// Prepare the node for the edit form so that $node->menu always exists.
$menu_name = variable_get('menu_parent_items', 'navigation');
$item = array();
if (isset($node->nid)) {
$mlid = db_result(db_query("SELECT mlid FROM {menu_links} WHERE link_path = 'node/%d' AND menu_name = '%s' AND module = 'menu' ORDER BY mlid ASC", $node->nid, $menu_name));
if ($mlid) {
$item = menu_link_load($mlid);
}
}
// Set default values.
$node->menu = $item + array('link_title' => '', 'mlid' => 0, 'plid' => 0, 'menu_name' => $menu_name, 'weight' => 0, 'options' => array(), 'module' => 'menu', 'expanded' => 0, 'hidden' => 0, 'has_children' => 0, 'customized' => 0);
}
break;
} }
} }
@ -559,7 +581,10 @@ function menu_nodeapi($node, $op) {
* Add menu item fields to the node form. * Add menu item fields to the node form.
*/ */
function menu_form_alter(&$form, $form_state, $form_id) { function menu_form_alter(&$form, $form_state, $form_id) {
if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) { if (isset($form['#node']) && $form['#node']->type .'_node_form' == $form_id) {
// Note - doing this to make sure the delete checkbox stays in the form.
$form['#cache'] = TRUE;
$form['menu'] = array( $form['menu'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('Menu settings'), '#title' => t('Menu settings'),
@ -567,27 +592,58 @@ function menu_form_alter(&$form, $form_state, $form_id) {
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => FALSE, '#collapsed' => FALSE,
'#tree' => TRUE, '#tree' => TRUE,
'#weight' => 30, '#weight' => -2,
); );
$form_state = array(); $item = $form['#node']->menu;
if ($mlid = db_result(db_query("SELECT mlid FROM {menu_links} WHERE link_path = 'node/%d' AND menu_name = '%s'", $form['nid']['#value'], variable_get('menu_parent_items', 'navigation')))) {
$menu_form = drupal_retrieve_form('menu_edit_item', $form_state, 'edit', $mlid); if ($item['mlid']) {
$menu_form['delete'] = array( // There is an existing link
$form['menu']['delete'] = array(
'#type' => 'checkbox', '#type' => 'checkbox',
'#title' => t('Check to delete this menu item.'), '#title' => t('Check to remove this item from the menu.'),
); );
} }
else { if (!$item['link_title']) {
$menu_form = drupal_retrieve_form('menu_edit_item', $form_state, 'add', variable_get('menu_parent_items', 'navigation'));
unset($menu_form['link_path']);
$menu_form['link_title']['#required'] = FALSE;
$form['menu']['#collapsed'] = TRUE; $form['menu']['#collapsed'] = TRUE;
} }
unset($menu_form['submit']);
$form['menu'] += $menu_form; foreach (array('mlid', 'module', 'hidden', 'menu_name', 'has_children', 'customized', 'options', 'expanded', 'hidden') as $key) {
$form['menu'][$key] = array('#type' => 'value', '#value' => $item[$key]);
}
$form['menu']['link_title'] = array('#type' => 'textfield',
'#title' => t('Menu link title'),
'#default_value' => $item['link_title'],
'#description' => t('The link text corresponding to this item that should appear in the menu. Leave blank if you do not wish to add this post to the menu.'),
'#required' => FALSE,
);
// Generate a list of possible parents (not including this item or descendants).
$options = menu_parent_options($item['menu_name'], $item);
$form['menu']['plid'] = array(
'#type' => 'select',
'#title' => t('Parent item'),
'#default_value' => $item['plid'],
'#options' => $options,
);
$form['menu']['weight'] = array(
'#type' => 'weight',
'#title' => t('Weight'),
'#default_value' => $item['weight'],
'#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
);
} }
} }
/**
* Return an associative array of the custom menus names.
*
* @param $all
* If FALSE return only user-added menus, or if TRUE also include
* the menus defined by the system.
* @return
* An array with the machine-readable names as the keys, and human-readable
* titles as the values.
*/
function menu_get_menus($all = FALSE) { function menu_get_menus($all = FALSE) {
$sql = 'SELECT * FROM {menu_custom}'. ($all ? '' : " WHERE menu_name NOT IN ('navigation', 'primary-links', 'secondary-links')") . ' ORDER BY title'; $sql = 'SELECT * FROM {menu_custom}'. ($all ? '' : " WHERE menu_name NOT IN ('navigation', 'primary-links', 'secondary-links')") . ' ORDER BY title';
$result = db_query($sql); $result = db_query($sql);
@ -600,7 +656,7 @@ function menu_get_menus($all = FALSE) {
} }
/** /**
* Menu callback; presents menu configuration options. * Menu callback; Build the form presenting menu configuration options.
*/ */
function menu_configure() { function menu_configure() {
$form['intro'] = array( $form['intro'] = array(
@ -619,3 +675,32 @@ function menu_configure() {
return system_settings_form($form); return system_settings_form($form);
} }
/**
* Validates the path of a menu link being created or edited.
*
* @return
* TRUE if it is a valid path AND the current user has access permission,
* FALSE otherwise.
*/
function menu_valid_path($form_item) {
$item = array();
$path = $form_item['link_path'];
if ($path == '<front>' || menu_path_is_external($path)) {
$item = array('access' => TRUE);
}
elseif (preg_match('/\/\%/', $path)) {
// Path is dynamic (ie 'user/%'), so check directly against menu_router table.
if ($item = db_fetch_array(db_query("SELECT * FROM {menu_router} where path = '%s' ", $path))) {
$item['link_path'] = $form_item['link_path'];
$item['link_title'] = $form_item['link_title'];
$item['external'] = FALSE;
$item['options'] = '';
_menu_link_translate($item);
}
}
else {
$item = menu_get_item($path);
}
return $item && $item['access'];
}

View File

@ -3339,6 +3339,7 @@ function system_update_6020() {
'expanded' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'small'), 'expanded' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'small'),
'weight' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), 'weight' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
'depth' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'small'), 'depth' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'small'),
'customized' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'small'),
'p1' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'p1' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
'p2' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'p2' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
'p3' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'p3' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),

View File

@ -110,6 +110,7 @@ function system_schema() {
'expanded' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'small'), 'expanded' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'small'),
'weight' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), 'weight' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
'depth' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'small'), 'depth' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'small'),
'customized' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'small'),
'p1' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'p1' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
'p2' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'p2' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
'p3' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'p3' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),

View File

@ -8,7 +8,7 @@
* An array of modules to be enabled. * An array of modules to be enabled.
*/ */
function default_profile_modules() { function default_profile_modules() {
return array('color', 'comment', 'help', 'taxonomy', 'dblog'); return array('color', 'comment', 'help', 'menu', 'taxonomy', 'dblog');
} }
/** /**