menu.inc

  1. drupal
    1. 4.6 includes/menu.inc
    2. 4.7 includes/menu.inc
    3. 5 includes/menu.inc
    4. 6 includes/menu.inc
    5. 7 includes/menu.inc
    6. 8 core/includes/menu.inc

API for the Drupal menu system.

Constants

NameDescription
MENU_ACCESS_DENIED
MENU_CALLBACKCallbacks simply register a path so that the correct function is fired when the URL is accessed. They are not shown in the menu.
MENU_CREATED_BY_ADMIN
MENU_CUSTOM_ITEMCustom items are those defined by the administrator. Reserved for internal use; do not return from hook_menu() implementations.
MENU_CUSTOM_MENUCustom menus are those defined by the administrator. Reserved for internal use; do not return from hook_menu() implementations.
MENU_DEFAULT_LOCAL_TASKEvery set of local tasks should provide one "default" task, that links to the same path as its parent when clicked.
MENU_DYNAMIC_ITEMDynamic menu items change frequently, and so should not be stored in the database for administrative customization.
MENU_EXPANDED
MENU_FOUND
MENU_IS_LOCAL_TASK
MENU_IS_ROOT
MENU_ITEM_GROUPINGItem groupings are used for pages like "node/add" that simply list subpages to visit. They are distinguished from other pages in that they will disappear from the menu if no subpages exist.
MENU_LINKS_TO_PARENT
MENU_LOCAL_TASKLocal tasks are rendered as tabs by default. Use this for menu items that describe actions to be performed on their parent item. An example is the path "node/52/edit", which performs the "edit" task on "node/52".
MENU_MODIFIABLE_BY_ADMIN
MENU_MODIFIED_BY_ADMIN
MENU_NORMAL_ITEMNormal menu items show up in the menu tree and can be moved/hidden by the administrator. Use this for most menu items. It is the default value if no menu item type is specified.
MENU_NOT_FOUND
MENU_SITE_OFFLINE
MENU_SUGGESTED_ITEMModules may "suggest" menu items that the administrator may enable. They act just as callbacks do until enabled, at which time they act like normal items.
MENU_VISIBLE_IF_HAS_CHILDREN
MENU_VISIBLE_IN_BREADCRUMB
MENU_VISIBLE_IN_TREE

Functions & methods

NameDescription
menu_execute_active_handlerExecute the handler associated with the active menu item.
menu_get_active_breadcrumbReturns an array of rendered menu items in the active breadcrumb trail.
menu_get_active_helpReturns the help associated with the active menu item.
menu_get_active_itemReturns the ID of the active menu item.
menu_get_active_nontask_itemReturns the ID of the current menu item or, if the current item is a local task, the menu item to which this task is attached.
menu_get_active_titleReturns the title of the active menu item.
menu_get_itemRetrieves the menu item specified by $mid, or by $path if $mid is not given.
menu_get_local_tasksReturn the local task tree.
menu_get_menuReturn the menu data structure.
menu_get_root_menusRetrieves the menu ID and title of all root menus.
menu_in_active_trailReturns true when the menu item is in the active trail.
menu_in_active_trail_in_submenuReturns true when the menu item is in the active trail within a specific subsection of the menu tree.
menu_item_linkReturns the rendered link to a menu item.
menu_primary_linksReturns an array containing the primary links. Can optionally descend from the root of the Primary links menu towards the current node for a specified number of levels and return that submenu. Used to generate a primary/secondary menu from different…
menu_primary_local_tasksReturns the rendered HTML of the primary local tasks.
menu_rebuildPopulate the database representation of the menu.
menu_secondary_linksReturns an array containing the secondary links. Secondary links can be either a second level of the Primary links menu or generated from their own menu.
menu_secondary_local_tasksReturns the rendered HTML of the secondary local tasks.
menu_set_active_itemSets the path of the active menu item.
menu_set_locationChange the current menu location of the user.
menu_treeReturns a rendered menu tree.
theme_menu_itemGenerate the HTML output for a single menu item.
theme_menu_item_linkGenerate the HTML representing a given menu item ID.
theme_menu_linksReturns the themed HTML for primary and secondary links. Note that this function is overridden by most core themes because those themes display links in "link | link" format, not from a list. Also note that by default links rendered with…
theme_menu_local_taskGenerate the HTML representing a given menu item ID as a tab.
theme_menu_local_tasksReturns the rendered local tasks. The default implementation renders them as tabs.
theme_menu_treeGenerate the HTML for a menu tree.
_menu_append_contextual_itemsAccount for menu items that are only defined at certain paths, so will not be cached.
_menu_buildBuild the menu by querying both modules and the database.
_menu_build_local_tasksFind all the items in the current local task tree.
_menu_build_visible_treeFind all visible items in the menu tree, for ease in displaying to user.
_menu_find_parentsEstablish parent-child relationships.
_menu_get_active_trailReturns an array with the menu items that lead to the current menu item.
_menu_get_active_trail_in_submenuFind the active trail through a specific subsection of the menu tree.
_menu_item_is_accessibleDetermine whether the given menu item is accessible to the current user.
_menu_site_is_offlineReturns TRUE if the site is off-line for maintenance.
_menu_sortComparator routine for use in sorting menu items.

File

includes/menu.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * API for the Drupal menu system.
  5. */
  6. /**
  7. * @defgroup menu Menu system
  8. * @{
  9. * Define the navigation menus, and route page requests to code based on URLs.
  10. *
  11. * The Drupal menu system drives both the navigation system from a user
  12. * perspective and the callback system that Drupal uses to respond to URLs
  13. * passed from the browser. For this reason, a good understanding of the
  14. * menu system is fundamental to the creation of complex modules.
  15. *
  16. * Drupal's menu system follows a simple hierarchy defined by paths.
  17. * Implementations of hook_menu() define menu items and assign them to
  18. * paths (which should be unique). The menu system aggregates these items
  19. * and determines the menu hierarchy from the paths. For example, if the
  20. * paths defined were a, a/b, e, a/b/c/d, f/g, and a/b/h, the menu system
  21. * would form the structure:
  22. * - a
  23. * - a/b
  24. * - a/b/c/d
  25. * - a/b/h
  26. * - e
  27. * - f/g
  28. * Note that the number of elements in the path does not necessarily
  29. * determine the depth of the menu item in the tree.
  30. *
  31. * When responding to a page request, the menu system looks to see if the
  32. * path requested by the browser is registered as a menu item with a
  33. * callback. If not, the system searches up the menu tree for the most
  34. * complete match with a callback it can find. If the path a/b/i is
  35. * requested in the tree above, the callback for a/b would be used.
  36. *
  37. * The found callback function is called with any arguments specified
  38. * in the "callback arguments" attribute of its menu item. The
  39. * attribute must be an array. After these arguments, any remaining
  40. * components of the path are appended as further arguments. In this
  41. * way, the callback for a/b above could respond to a request for
  42. * a/b/i differently than a request for a/b/j.
  43. *
  44. * For an illustration of this process, see page_example.module.
  45. *
  46. * Access to the callback functions is also protected by the menu system.
  47. * The "access" attribute of each menu item is checked as the search for a
  48. * callback proceeds. If this attribute is TRUE, then access is granted; if
  49. * FALSE, then access is denied. The first found "access" attribute
  50. * determines the accessibility of the target. Menu items may omit this
  51. * attribute to use the value provided by an ancestor item.
  52. *
  53. * In the default Drupal interface, you will notice many links rendered as
  54. * tabs. These are known in the menu system as "local tasks", and they are
  55. * rendered as tabs by default, though other presentations are possible.
  56. * Local tasks function just as other menu items in most respects. It is
  57. * convention that the names of these tasks should be short verbs if
  58. * possible. In addition, a "default" local task should be provided for
  59. * each set. When visiting a local task's parent menu item, the default
  60. * local task will be rendered as if it is selected; this provides for a
  61. * normal tab user experience. This default task is special in that it
  62. * links not to its provided path, but to its parent item's path instead.
  63. * The default task's path is only used to place it appropriately in the
  64. * menu hierarchy.
  65. */
  66. /**
  67. * @name Menu flags
  68. * @{
  69. * Flags for use in the "type" attribute of menu items.
  70. */
  71. define('MENU_IS_ROOT', 0x0001);
  72. define('MENU_VISIBLE_IN_TREE', 0x0002);
  73. define('MENU_VISIBLE_IN_BREADCRUMB', 0x0004);
  74. define('MENU_VISIBLE_IF_HAS_CHILDREN', 0x0008);
  75. define('MENU_MODIFIABLE_BY_ADMIN', 0x0010);
  76. define('MENU_MODIFIED_BY_ADMIN', 0x0020);
  77. define('MENU_CREATED_BY_ADMIN', 0x0040);
  78. define('MENU_IS_LOCAL_TASK', 0x0080);
  79. define('MENU_EXPANDED', 0x0100);
  80. define('MENU_LINKS_TO_PARENT', 0x0200);
  81. /**
  82. * @} End of "Menu flags".
  83. */
  84. /**
  85. * @name Menu item types
  86. * @{
  87. * Menu item definitions provide one of these constants, which are shortcuts for
  88. * combinations of the above flags.
  89. */
  90. /**
  91. * Normal menu items show up in the menu tree and can be moved/hidden by
  92. * the administrator. Use this for most menu items. It is the default value if
  93. * no menu item type is specified.
  94. */
  95. define('MENU_NORMAL_ITEM', MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB | MENU_MODIFIABLE_BY_ADMIN);
  96. /**
  97. * Item groupings are used for pages like "node/add" that simply list
  98. * subpages to visit. They are distinguished from other pages in that they will
  99. * disappear from the menu if no subpages exist.
  100. */
  101. define('MENU_ITEM_GROUPING', MENU_VISIBLE_IF_HAS_CHILDREN | MENU_VISIBLE_IN_BREADCRUMB | MENU_MODIFIABLE_BY_ADMIN);
  102. /**
  103. * Callbacks simply register a path so that the correct function is fired
  104. * when the URL is accessed. They are not shown in the menu.
  105. */
  106. define('MENU_CALLBACK', MENU_VISIBLE_IN_BREADCRUMB);
  107. /**
  108. * Dynamic menu items change frequently, and so should not be stored in the
  109. * database for administrative customization.
  110. */
  111. define('MENU_DYNAMIC_ITEM', MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB);
  112. /**
  113. * Modules may "suggest" menu items that the administrator may enable. They act
  114. * just as callbacks do until enabled, at which time they act like normal items.
  115. */
  116. define('MENU_SUGGESTED_ITEM', MENU_MODIFIABLE_BY_ADMIN | MENU_VISIBLE_IN_BREADCRUMB);
  117. /**
  118. * Local tasks are rendered as tabs by default. Use this for menu items that
  119. * describe actions to be performed on their parent item. An example is the path
  120. * "node/52/edit", which performs the "edit" task on "node/52".
  121. */
  122. define('MENU_LOCAL_TASK', MENU_IS_LOCAL_TASK);
  123. /**
  124. * Every set of local tasks should provide one "default" task, that links to the
  125. * same path as its parent when clicked.
  126. */
  127. define('MENU_DEFAULT_LOCAL_TASK', MENU_IS_LOCAL_TASK | MENU_LINKS_TO_PARENT);
  128. /**
  129. * Custom items are those defined by the administrator. Reserved for internal
  130. * use; do not return from hook_menu() implementations.
  131. */
  132. define('MENU_CUSTOM_ITEM', MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB | MENU_CREATED_BY_ADMIN | MENU_MODIFIABLE_BY_ADMIN);
  133. /**
  134. * Custom menus are those defined by the administrator. Reserved for internal
  135. * use; do not return from hook_menu() implementations.
  136. */
  137. define('MENU_CUSTOM_MENU', MENU_IS_ROOT | MENU_VISIBLE_IN_TREE | MENU_CREATED_BY_ADMIN | MENU_MODIFIABLE_BY_ADMIN);
  138. /**
  139. * @} End of "Menu item types".
  140. */
  141. /**
  142. * @name Menu status codes
  143. * @{
  144. * Status codes for menu callbacks.
  145. */
  146. define('MENU_FOUND', 1);
  147. define('MENU_NOT_FOUND', 2);
  148. define('MENU_ACCESS_DENIED', 3);
  149. define('MENU_SITE_OFFLINE', 4);
  150. /**
  151. * @} End of "Menu status codes".
  152. */
  153. /**
  154. * Return the menu data structure.
  155. *
  156. * The returned structure contains much information that is useful only
  157. * internally in the menu system. External modules are likely to need only
  158. * the ['visible'] element of the returned array. All menu items that are
  159. * accessible to the current user and not hidden will be present here, so
  160. * modules and themes can use this structure to build their own representations
  161. * of the menu.
  162. *
  163. * $menu['visible'] will contain an associative array, the keys of which
  164. * are menu IDs. The values of this array are themselves associative arrays,
  165. * with the following key-value pairs defined:
  166. * - 'title' - The displayed title of the menu or menu item. It will already
  167. * have been translated by the locale system.
  168. * - 'description' - The description (link title attribute) of the menu item.
  169. * It will already have been translated by the locale system.
  170. * - 'path' - The Drupal path to the menu item. A link to a particular item
  171. * can thus be constructed with
  172. * l($item['title'], $item['path'], array('title' => $item['description'])).
  173. * - 'children' - A linear list of the menu ID's of this item's children.
  174. *
  175. * Menu ID 0 is the "root" of the menu. The children of this item are the
  176. * menus themselves (they will have no associated path). Menu ID 1 will
  177. * always be one of these children; it is the default "Navigation" menu.
  178. */
  179. function menu_get_menu() {
  180. global $_menu;
  181. global $user;
  182. global $locale;
  183. if (!isset($_menu['items'])) {
  184. // _menu_build() may indirectly call this function, so prevent infinite loops.
  185. $_menu['items'] = array();
  186. $cid = "menu:$user->uid:$locale";
  187. if ($cached = cache_get($cid)) {
  188. $_menu = unserialize($cached->data);
  189. }
  190. else {
  191. _menu_build();
  192. // Cache the menu structure for this user, to expire after one day.
  193. cache_set($cid, serialize($_menu), time() + (60 * 60 * 24));
  194. }
  195. // Make sure items that cannot be cached are added.
  196. _menu_append_contextual_items();
  197. // Reset the cached $menu in menu_get_item().
  198. menu_get_item(NULL, NULL, TRUE);
  199. }
  200. return $_menu;
  201. }
  202. /**
  203. * Return the local task tree.
  204. *
  205. * Unlike the rest of the menu structure, the local task tree cannot be cached
  206. * nor determined too early in the page request, because the user's current
  207. * location may be changed by a menu_set_location() call, and the tasks shown
  208. * (just as the breadcrumb trail) need to reflect the changed location.
  209. */
  210. function menu_get_local_tasks() {
  211. global $_menu;
  212. // Don't cache the local task tree, as it varies by location and tasks are
  213. // allowed to be dynamically determined.
  214. if (!isset($_menu['local tasks'])) {
  215. // _menu_build_local_tasks() may indirectly call this function, so prevent
  216. // infinite loops.
  217. $_menu['local tasks'] = array();
  218. $pid = menu_get_active_nontask_item();
  219. if (!_menu_build_local_tasks($pid)) {
  220. // If the build returned FALSE, the tasks need not be displayed.
  221. $_menu['local tasks'][$pid]['children'] = array();
  222. }
  223. }
  224. return $_menu['local tasks'];
  225. }
  226. /**
  227. * Retrieves the menu item specified by $mid, or by $path if $mid is not given.
  228. *
  229. * @param $mid
  230. * The menu ID of the menu item to retrieve.
  231. * @param $path
  232. * The internal path of the menu item to retrieve. Defaults to NULL. Only
  233. * used if $mid is not set.
  234. * @param $reset
  235. * Optional flag that resets the static variable cache of the menu tree, if
  236. * set to TRUE. Default is FALSE.
  237. *
  238. * @return
  239. * The menu item found in the site menu, or an empty array if none could be
  240. * found.
  241. */
  242. function menu_get_item($mid, $path = NULL, $reset = FALSE) {
  243. static $menu;
  244. if (!isset($menu) || $reset) {
  245. $menu = menu_get_menu();
  246. }
  247. if (isset($mid)) {
  248. return $menu['items'][$mid];
  249. }
  250. if (isset($path)) {
  251. return $menu['items'][$menu['path index'][$path]];
  252. }
  253. return array();
  254. }
  255. /**
  256. * Retrieves the menu ID and title of all root menus.
  257. *
  258. * @return
  259. * Array containing all menus (but not menu items), in the form mid => title.
  260. */
  261. function menu_get_root_menus() {
  262. $menu = menu_get_menu();
  263. $root_menus = array();
  264. foreach ($menu['items'][0]['children'] as $mid) {
  265. $root_menus[$mid] = $menu['items'][$mid]['title'];
  266. }
  267. return $root_menus;
  268. }
  269. /**
  270. * Change the current menu location of the user.
  271. *
  272. * Frequently, modules may want to make a page or node act as if it were
  273. * in the menu tree somewhere, even though it was not registered in a
  274. * hook_menu() implementation. If the administrator has rearranged the menu,
  275. * the newly set location should respect this in the breadcrumb trail and
  276. * expanded/collapsed status of menu items in the tree. This function
  277. * allows this behavior.
  278. *
  279. * @param $location
  280. * An array specifying a complete or partial breadcrumb trail for the
  281. * new location, in the same format as the return value of hook_menu().
  282. * The last element of this array should be the new location itself.
  283. *
  284. * This function will set the new breadcrumb trail to the passed-in value,
  285. * but if any elements of this trail are visible in the site tree, the
  286. * trail will be "spliced in" to the existing site navigation at that point.
  287. */
  288. function menu_set_location($location) {
  289. global $_menu;
  290. $temp_id = min(array_keys($_menu['items'])) - 1;
  291. $prev_id = 0;
  292. // Don't allow this function to change the actual current path, just the
  293. // position in the menu tree.
  294. $location[count($location) - 1]['path'] = $_GET['q'];
  295. foreach (array_reverse($location) as $item) {
  296. if (isset($_menu['path index'][$item['path']])) {
  297. $mid = $_menu['path index'][$item['path']];
  298. if (isset($_menu['visible'][$mid])) {
  299. // Splice in the breadcrumb at this location.
  300. if ($prev_id) {
  301. $_menu['items'][$prev_id]['pid'] = $mid;
  302. }
  303. $prev_id = 0;
  304. break;
  305. }
  306. else {
  307. // A hidden item; show it, but only temporarily.
  308. $_menu['items'][$mid]['type'] |= MENU_VISIBLE_IN_BREADCRUMB;
  309. if ($prev_id) {
  310. $_menu['items'][$prev_id]['pid'] = $mid;
  311. }
  312. $prev_id = $mid;
  313. }
  314. }
  315. else {
  316. $item['type'] |= MENU_VISIBLE_IN_BREADCRUMB;
  317. if ($prev_id) {
  318. $_menu['items'][$prev_id]['pid'] = $temp_id;
  319. }
  320. $_menu['items'][$temp_id] = $item;
  321. $_menu['path index'][$item['path']] = $temp_id;
  322. $prev_id = $temp_id;
  323. $temp_id--;
  324. }
  325. }
  326. if ($prev_id) {
  327. // Didn't find a home, so attach this to the main navigation menu.
  328. $_menu['items'][$prev_id]['pid'] = 1;
  329. }
  330. $final_item = array_pop($location);
  331. menu_set_active_item($final_item['path']);
  332. }
  333. /**
  334. * Execute the handler associated with the active menu item.
  335. *
  336. * This is called early in the page request. The active menu item is at
  337. * this point determined exclusively by the URL. The handler that is called
  338. * here may, as a side effect, change the active menu item so that later
  339. * menu functions (that display the menus and breadcrumbs, for example)
  340. * act as if the user were in a different location on the site.
  341. */
  342. function menu_execute_active_handler() {
  343. if (_menu_site_is_offline()) {
  344. return MENU_SITE_OFFLINE;
  345. }
  346. $menu = menu_get_menu();
  347. // Determine the menu item containing the callback.
  348. $path = $_GET['q'];
  349. while ($path && !isset($menu['callbacks'][$path])) {
  350. $path = substr($path, 0, strrpos($path, '/'));
  351. }
  352. if (!isset($menu['callbacks'][$path])) {
  353. return MENU_NOT_FOUND;
  354. }
  355. if (!function_exists($menu['callbacks'][$path]['callback'])) {
  356. return MENU_NOT_FOUND;
  357. }
  358. if (!_menu_item_is_accessible(menu_get_active_item())) {
  359. return MENU_ACCESS_DENIED;
  360. }
  361. // We found one, and are allowed to execute it.
  362. $arguments = isset($menu['callbacks'][$path]['callback arguments']) ? $menu['callbacks'][$path]['callback arguments'] : array();
  363. $arg = substr($_GET['q'], strlen($path) + 1);
  364. if (strlen($arg)) {
  365. $arguments = array_merge($arguments, explode('/', $arg));
  366. }
  367. return call_user_func_array($menu['callbacks'][$path]['callback'], $arguments);
  368. }
  369. /**
  370. * Returns the ID of the active menu item.
  371. */
  372. function menu_get_active_item() {
  373. return menu_set_active_item();
  374. }
  375. /**
  376. * Sets the path of the active menu item.
  377. */
  378. function menu_set_active_item($path = NULL) {
  379. static $stored_mid;
  380. if (!isset($stored_mid) || isset($path)) {
  381. $menu = menu_get_menu();
  382. if (!isset($path)) {
  383. $path = $_GET['q'];
  384. }
  385. else {
  386. $_GET['q'] = $path;
  387. }
  388. while ($path && !isset($menu['path index'][$path])) {
  389. $path = substr($path, 0, strrpos($path, '/'));
  390. }
  391. $stored_mid = isset($menu['path index'][$path]) ? $menu['path index'][$path] : 0;
  392. // Search for default local tasks to activate instead of this item.
  393. $continue = TRUE;
  394. while ($continue) {
  395. $continue = FALSE;
  396. if (isset($menu['items'][$stored_mid]['children'])) {
  397. foreach ($menu['items'][$stored_mid]['children'] as $cid) {
  398. if ($menu['items'][$cid]['type'] & MENU_LINKS_TO_PARENT) {
  399. $stored_mid = $cid;
  400. $continue = TRUE;
  401. }
  402. }
  403. }
  404. }
  405. // Reset the cached $menu in menu_get_item().
  406. menu_get_item(NULL, NULL, TRUE);
  407. }
  408. return $stored_mid;
  409. }
  410. /**
  411. * Returns the ID of the current menu item or, if the current item is a
  412. * local task, the menu item to which this task is attached.
  413. */
  414. function menu_get_active_nontask_item() {
  415. $mid = menu_get_active_item();
  416. // Find the first non-task item:
  417. while ($mid) {
  418. $item = menu_get_item($mid);
  419. if (!($item['type'] & MENU_IS_LOCAL_TASK)) {
  420. return $mid;
  421. }
  422. $mid = $item['pid'];
  423. }
  424. }
  425. /**
  426. * Returns the title of the active menu item.
  427. */
  428. function menu_get_active_title() {
  429. if ($mid = menu_get_active_nontask_item()) {
  430. $item = menu_get_item($mid);
  431. return $item['title'];
  432. }
  433. }
  434. /**
  435. * Returns the help associated with the active menu item.
  436. */
  437. function menu_get_active_help() {
  438. $path = $_GET['q'];
  439. $output = '';
  440. if (!_menu_item_is_accessible(menu_get_active_item())) {
  441. // Don't return help text for areas the user cannot access.
  442. return;
  443. }
  444. foreach (module_list() as $name) {
  445. if (module_hook($name, 'help')) {
  446. if ($temp = module_invoke($name, 'help', $path)) {
  447. $output .= $temp . "\n";
  448. }
  449. if (module_hook('help', 'page')) {
  450. if (substr($path, 0, 6) == "admin/") {
  451. if (module_invoke($name, 'help', 'admin/help#' . substr($path, 6))) {
  452. $output .= theme("more_help_link", url('admin/help/' . substr($path, 6)));
  453. }
  454. }
  455. }
  456. }
  457. }
  458. return $output;
  459. }
  460. /**
  461. * Returns an array of rendered menu items in the active breadcrumb trail.
  462. */
  463. function menu_get_active_breadcrumb() {
  464. // No breadcrumb for the front page.
  465. if (drupal_is_front_page()) {
  466. return array();
  467. }
  468. // We do *not* want to use "variable_get('site_frontpage', 'node)" here
  469. // as that will create the link '/node'. This is unsightly and creates
  470. // a second URL for the homepage ('/' *and* '/node').
  471. $links[] = l(t('Home'), '');
  472. $trail = _menu_get_active_trail();
  473. foreach ($trail as $mid) {
  474. $item = menu_get_item($mid);
  475. if ($item['type'] & MENU_VISIBLE_IN_BREADCRUMB) {
  476. $links[] = menu_item_link($mid);
  477. }
  478. }
  479. // The last item in the trail is the page title; don't display it here.
  480. array_pop($links);
  481. return $links;
  482. }
  483. /**
  484. * Returns true when the menu item is in the active trail.
  485. */
  486. function menu_in_active_trail($mid) {
  487. $trail = _menu_get_active_trail();
  488. return in_array($mid, $trail);
  489. }
  490. /**
  491. * Returns true when the menu item is in the active trail within a
  492. * specific subsection of the menu tree.
  493. *
  494. * @param $mid
  495. * The menu item being considered.
  496. * @param $pid
  497. * The root of the subsection of the menu tree in which to look.
  498. */
  499. function menu_in_active_trail_in_submenu($mid, $pid) {
  500. $trail = _menu_get_active_trail_in_submenu($pid);
  501. if (!$trail) {
  502. return FALSE;
  503. }
  504. return in_array($mid, $trail);
  505. }
  506. /**
  507. * Populate the database representation of the menu.
  508. *
  509. * This need only be called at the start of pages that modify the menu.
  510. */
  511. function menu_rebuild() {
  512. // Clear the page cache, so that changed menus are reflected for anonymous users.
  513. cache_clear_all();
  514. // Also clear the menu cache.
  515. cache_clear_all('menu:', TRUE);
  516. _menu_build();
  517. if (module_exist('menu')) {
  518. $menu = menu_get_menu();
  519. // Fill a queue of new menu items which are modifiable.
  520. $new_items = array();
  521. foreach ($menu['items'] as $mid => $item) {
  522. if ($mid < 0 && ($item['type'] & MENU_MODIFIABLE_BY_ADMIN)) {
  523. $new_items[$mid] = $item;
  524. }
  525. }
  526. $old_count = -1;
  527. // Save the new items updating the pids in each iteration
  528. while (($c = count($new_items)) && ($c != $old_count)) {
  529. $old_count = count($new_items);
  530. foreach($new_items as $mid => $item) {
  531. // If the item has a valid parent, save it
  532. if ($item['pid'] >= 0) {
  533. // The new menu ID gets passed back by reference as $item['mid']
  534. menu_save_item($item);
  535. // Fix parent IDs for the children of the menu item just saved
  536. if ($item['children']) {
  537. foreach ($item['children'] as $child) {
  538. if (isset($new_items[$child])) {
  539. $new_items[$child]['pid'] = $item['mid'];
  540. }
  541. }
  542. }
  543. // remove the item
  544. unset($new_items[$mid]);
  545. }
  546. }
  547. }
  548. // Rebuild the menu to account for the changes.
  549. _menu_build();
  550. }
  551. // Reset the cached $menu in menu_get_item().
  552. menu_get_item(NULL, NULL, TRUE);
  553. }
  554. /**
  555. * Generate the HTML for a menu tree.
  556. *
  557. * @param $pid
  558. * The parent id of the menu.
  559. *
  560. * @ingroup themeable
  561. */
  562. function theme_menu_tree($pid = 1) {
  563. if ($tree = menu_tree($pid)) {
  564. return "\n<ul class=\"menu\">\n". $tree ."\n</ul>\n";
  565. }
  566. }
  567. /**
  568. * Returns a rendered menu tree.
  569. *
  570. * @param $pid
  571. * The parent id of the menu.
  572. */
  573. function menu_tree($pid = 1) {
  574. $menu = menu_get_menu();
  575. $output = '';
  576. if (isset($menu['visible'][$pid]) && $menu['visible'][$pid]['children']) {
  577. foreach ($menu['visible'][$pid]['children'] as $mid) {
  578. $type = isset($menu['visible'][$mid]['type']) ? $menu['visible'][$mid]['type'] : NULL;
  579. $children = isset($menu['visible'][$mid]['children']) ? $menu['visible'][$mid]['children'] : NULL;
  580. $output .= theme('menu_item', $mid, menu_in_active_trail($mid) || ($type & MENU_EXPANDED) ? theme('menu_tree', $mid) : '', count($children) == 0);
  581. }
  582. }
  583. return $output;
  584. }
  585. /**
  586. * Generate the HTML output for a single menu item.
  587. *
  588. * @param $mid
  589. * The menu id of the item.
  590. * @param $children
  591. * A string containing any rendered child items of this menu.
  592. * @param $leaf
  593. * A boolean indicating whether this menu item is a leaf.
  594. *
  595. * @ingroup themeable
  596. */
  597. function theme_menu_item($mid, $children = '', $leaf = TRUE) {
  598. return '<li class="'. ($leaf ? 'leaf' : ($children ? 'expanded' : 'collapsed')) .'">'. menu_item_link($mid) . $children ."</li>\n";
  599. }
  600. /**
  601. * Generate the HTML representing a given menu item ID.
  602. *
  603. * @param $item
  604. * The menu item to render.
  605. * @param $link_item
  606. * The menu item which should be used to find the correct path.
  607. *
  608. * @ingroup themeable
  609. */
  610. function theme_menu_item_link($item, $link_item) {
  611. return l($item['title'], $link_item['path'], !empty($item['description']) ? array('title' => $item['description']) : array());
  612. }
  613. /**
  614. * Returns the rendered link to a menu item.
  615. *
  616. * @param $mid
  617. * The menu item id to render.
  618. */
  619. function menu_item_link($mid) {
  620. $item = menu_get_item($mid);
  621. $link_item = $item;
  622. while ($link_item['type'] & MENU_LINKS_TO_PARENT) {
  623. $link_item = menu_get_item($link_item['pid']);
  624. }
  625. return theme('menu_item_link', $item, $link_item);
  626. }
  627. /**
  628. * Returns the rendered local tasks. The default implementation renders
  629. * them as tabs.
  630. *
  631. * @ingroup themeable
  632. */
  633. function theme_menu_local_tasks() {
  634. $output = '';
  635. if ($primary = menu_primary_local_tasks()) {
  636. $output .= "<ul class=\"tabs primary\">\n". $primary ."</ul>\n";
  637. }
  638. if ($secondary = menu_secondary_local_tasks()) {
  639. $output .= "<ul class=\"tabs secondary\">\n". $secondary ."</ul>\n";
  640. }
  641. return $output;
  642. }
  643. /**
  644. * Returns the rendered HTML of the primary local tasks.
  645. */
  646. function menu_primary_local_tasks() {
  647. $local_tasks = menu_get_local_tasks();
  648. $pid = menu_get_active_nontask_item();
  649. $output = '';
  650. if (count($local_tasks[$pid]['children'])) {
  651. foreach ($local_tasks[$pid]['children'] as $mid) {
  652. $output .= theme('menu_local_task', $mid, menu_in_active_trail($mid), TRUE);
  653. }
  654. }
  655. return $output;
  656. }
  657. /**
  658. * Returns the rendered HTML of the secondary local tasks.
  659. */
  660. function menu_secondary_local_tasks() {
  661. $local_tasks = menu_get_local_tasks();
  662. $pid = menu_get_active_nontask_item();
  663. $output = '';
  664. if (count($local_tasks[$pid]['children'])) {
  665. foreach ($local_tasks[$pid]['children'] as $mid) {
  666. if (menu_in_active_trail($mid) && count($local_tasks[$mid]['children']) > 1) {
  667. foreach ($local_tasks[$mid]['children'] as $cid) {
  668. $output .= theme('menu_local_task', $cid, menu_in_active_trail($cid), FALSE);
  669. }
  670. }
  671. }
  672. }
  673. return $output;
  674. }
  675. /**
  676. * Generate the HTML representing a given menu item ID as a tab.
  677. *
  678. * @param $mid
  679. * The menu ID to render.
  680. * @param $active
  681. * Whether this tab or a subtab is the active menu item.
  682. * @param $primary
  683. * Whether this tab is a primary tab or a subtab.
  684. *
  685. * @ingroup themeable
  686. */
  687. function theme_menu_local_task($mid, $active, $primary) {
  688. if ($active) {
  689. return '<li class="active">'. menu_item_link($mid) ."</li>\n";
  690. }
  691. else {
  692. return '<li>'. menu_item_link($mid) ."</li>\n";
  693. }
  694. }
  695. /**
  696. * Returns an array containing the primary links.
  697. * Can optionally descend from the root of the Primary links menu towards the
  698. * current node for a specified number of levels and return that submenu.
  699. * Used to generate a primary/secondary menu from different levels of one menu.
  700. *
  701. * @param $start_level
  702. * This optional parameter can be used to retrieve a context-sensitive array
  703. * of links at $start_level levels deep into the Primary links menu.
  704. * The default is to return the top-level links.
  705. * @param $pid
  706. * The parent menu ID from which to search for children. Defaults to the
  707. * menu_primary_menu setting.
  708. * @return An array containing the themed links as the values. The keys of
  709. * the array contain some extra encoded information about the results.
  710. * The format of the key is {level}-{num}{-active}.
  711. * level is the depth within the menu tree of this list.
  712. * num is the number within this array, used only to make the key unique.
  713. * -active is appended if this element is in the active trail.
  714. */
  715. function menu_primary_links($start_level = 1, $pid = 0) {
  716. if (!module_exist('menu')) {
  717. return NULL;
  718. }
  719. if (!$pid) {
  720. $pid = variable_get('menu_primary_menu', 0);
  721. }
  722. if (!$pid) {
  723. return NULL;
  724. }
  725. if ($start_level < 1) {
  726. $start_level = 1;
  727. }
  728. if ($start_level > 1) {
  729. $trail = _menu_get_active_trail_in_submenu($pid);
  730. if (!$trail) {
  731. return NULL;
  732. }
  733. else {
  734. $pid = $trail[$start_level - 1];
  735. }
  736. }
  737. $menu = menu_get_menu();
  738. $links = array();
  739. if ($pid && is_array($menu['visible'][$pid]) && isset($menu['visible'][$pid]['children'])) {
  740. $count = 1;
  741. foreach ($menu['visible'][$pid]['children'] as $cid) {
  742. $index = "menu-$start_level-$count";
  743. if (menu_in_active_trail_in_submenu($cid, $pid)) {
  744. $index .= "-active";
  745. }
  746. $links[$index] = menu_item_link($cid);
  747. $count++;
  748. }
  749. }
  750. // Special case - provide link to admin/menu if primary links is empty.
  751. if (empty($links) && $start_level == 1 && $pid == variable_get('menu_primary_menu', 0) && user_access('administer menu')) {
  752. $links['1-1'] = l(t('edit primary links'),'admin/menu');
  753. }
  754. return $links;
  755. }
  756. /**
  757. * Returns an array containing the secondary links.
  758. * Secondary links can be either a second level of the Primary links
  759. * menu or generated from their own menu.
  760. */
  761. function menu_secondary_links() {
  762. $msm = variable_get('menu_secondary_menu', 0);
  763. if ($msm == 0) {
  764. return NULL;
  765. }
  766. if ($msm == variable_get('menu_primary_menu', 0)) {
  767. return menu_primary_links(2, $msm);
  768. }
  769. return menu_primary_links(1, $msm);
  770. }
  771. /**
  772. * Returns the themed HTML for primary and secondary links.
  773. * Note that this function is overridden by most core themes because
  774. * those themes display links in "link | link" format, not from a list.
  775. * Also note that by default links rendered with this function are
  776. * displayed with the same CSS as is used for the local tasks.
  777. * If a theme wishes to render links from a ul it is expected that
  778. * the theme will provide suitable CSS.
  779. *
  780. * @param $links
  781. * An array containing links to render.
  782. * @return
  783. * A string containing the themed links.
  784. *
  785. * @ingroup themeable
  786. */
  787. function theme_menu_links($links) {
  788. if (!count($links)) {
  789. return '';
  790. }
  791. $level_tmp = explode('-', key($links));
  792. $level = $level_tmp[0];
  793. $output = "<ul class=\"links-$level\">\n";
  794. foreach ($links as $index => $link) {
  795. $output .= '<li';
  796. if (stristr($index, 'active')) {
  797. $output .= ' class="active"';
  798. }
  799. $output .= ">$link</li>\n";
  800. }
  801. $output .= '</ul>';
  802. return $output;
  803. }
  804. /**
  805. * @} End of "defgroup menu".
  806. */
  807. /**
  808. * Returns an array with the menu items that lead to the current menu item.
  809. */
  810. function _menu_get_active_trail() {
  811. static $trail;
  812. if (!isset($trail)) {
  813. $trail = array();
  814. $mid = menu_get_active_item();
  815. // Follow the parents up the chain to get the trail.
  816. while ($mid && ($item = menu_get_item($mid))) {
  817. array_unshift($trail, $mid);
  818. $mid = $item['pid'];
  819. }
  820. }
  821. return $trail;
  822. }
  823. /**
  824. * Find the active trail through a specific subsection of the menu tree.
  825. *
  826. * @param $pid
  827. * The root item from which the active trail must descend.
  828. */
  829. function _menu_get_active_trail_in_submenu($pid) {
  830. static $trails;
  831. if (!isset($trails)) {
  832. // Find all menu items which point to the current node and for each
  833. // follow the parents up the chain to build an active trail.
  834. $trails = array();
  835. $menu = menu_get_menu();
  836. $path = $_GET['q'];
  837. $count = 0;
  838. while ($path && !$count) {
  839. foreach ($menu['items'] as $key => $item) {
  840. if (isset($item['path']) && $item['path'] == $path) {
  841. $trails[$count] = array();
  842. $mid = $key;
  843. while ($mid && $menu['items'][$mid]) {
  844. array_unshift($trails[$count], $mid);
  845. $mid = $menu['items'][$mid]['pid'];
  846. }
  847. $count ++;
  848. }
  849. }
  850. $path = substr($path, 0, strrpos($path, '/'));
  851. }
  852. }
  853. if ($trails) {
  854. foreach ($trails as $trail) {
  855. $count_trail = count($trail);
  856. for ($i = 0; $i < $count_trail; $i++) {
  857. if ($trail[$i] == $pid) {
  858. // Return a trail from $pid down to the current page inclusive.
  859. for ( ; $i < $count_trail; $i++) {
  860. $subtrail[] = $trail[$i];
  861. }
  862. return $subtrail;
  863. }
  864. }
  865. }
  866. }
  867. return NULL;
  868. }
  869. /**
  870. * Comparator routine for use in sorting menu items.
  871. */
  872. function _menu_sort($a, $b) {
  873. $menu = menu_get_menu();
  874. $a = &$menu['items'][$a];
  875. $b = &$menu['items'][$b];
  876. if ($a['weight'] < $b['weight']) {
  877. return -1;
  878. }
  879. elseif ($a['weight'] > $b['weight']) {
  880. return 1;
  881. }
  882. elseif (isset($a['title']) && isset($b['title'])) {
  883. return strnatcasecmp($a['title'], $b['title']);
  884. }
  885. else {
  886. return 1;
  887. }
  888. }
  889. /**
  890. * Build the menu by querying both modules and the database.
  891. */
  892. function _menu_build() {
  893. global $_menu;
  894. global $user;
  895. // Start from a clean slate.
  896. $_menu = array();
  897. $_menu['path index'] = array();
  898. // Set up items array, including default "Navigation" menu.
  899. $_menu['items'] = array(
  900. 0 => array('path' => '', 'title' => '', 'type' => MENU_IS_ROOT),
  901. 1 => array('pid' => 0, 'path' => '', 'title' => t('Navigation'), 'weight' => -50, 'access' => TRUE, 'type' => MENU_IS_ROOT | MENU_VISIBLE_IN_TREE)
  902. );
  903. $_menu['callbacks'] = array();
  904. // Build a sequential list of all menu items.
  905. $menu_item_list = module_invoke_all('menu', TRUE);
  906. // Menu items not in the DB get temporary negative IDs.
  907. $temp_mid = -1;
  908. foreach ($menu_item_list as $item) {
  909. if (!isset($item['path'])) {
  910. $item['path'] = '';
  911. }
  912. if (!isset($item['type'])) {
  913. $item['type'] = MENU_NORMAL_ITEM;
  914. }
  915. if (!isset($item['weight'])) {
  916. $item['weight'] = 0;
  917. }
  918. $mid = $temp_mid;
  919. if (isset($_menu['path index'][$item['path']])) {
  920. // Newer menu items overwrite older ones.
  921. unset($_menu['items'][$_menu['path index'][$item['path']]]);
  922. }
  923. if (isset($item['callback'])) {
  924. $_menu['callbacks'][$item['path']] = array('callback' => $item['callback']);
  925. if (isset($item['callback arguments'])) {
  926. $_menu['callbacks'][$item['path']]['callback arguments'] = $item['callback arguments'];
  927. }
  928. }
  929. unset($item['callback']);
  930. unset($item['callback arguments']);
  931. $_menu['items'][$mid] = $item;
  932. $_menu['path index'][$item['path']] = $mid;
  933. $temp_mid--;
  934. }
  935. // Now fetch items from the DB, reassigning menu IDs as needed.
  936. if (module_exist('menu')) {
  937. $result = db_query(db_rewrite_sql('SELECT m.mid, m.* FROM {menu} m ORDER BY m.mid ASC', 'm', 'mid'));
  938. while ($item = db_fetch_object($result)) {
  939. // Handle URL aliases if entered in menu administration.
  940. if (!isset($_menu['path index'][$item->path])) {
  941. $item->path = drupal_get_normal_path($item->path);
  942. }
  943. if (isset($_menu['path index'][$item->path])) {
  944. // The path is already declared.
  945. $old_mid = $_menu['path index'][$item->path];
  946. if ($old_mid < 0) {
  947. // It had a temporary ID, so use a permanent one.
  948. $_menu['items'][$item->mid] = $_menu['items'][$old_mid];
  949. unset($_menu['items'][$old_mid]);
  950. $_menu['path index'][$item->path] = $item->mid;
  951. }
  952. else {
  953. // It has a permanent ID. Only replace with non-custom menu items.
  954. if ($item->type & MENU_CREATED_BY_ADMIN) {
  955. $_menu['items'][$item->mid] = array('path' => $item->path);
  956. }
  957. else {
  958. // Leave the old item around as a shortcut to this one.
  959. $_menu['items'][$item->mid] = $_menu['items'][$old_mid];
  960. $_menu['path index'][$item->path] = $item->mid;
  961. }
  962. }
  963. }
  964. else {
  965. // The path was not declared, so this is a custom item or an orphaned one.
  966. if ($item->type & MENU_CREATED_BY_ADMIN) {
  967. $_menu['items'][$item->mid] = array('path' => $item->path);
  968. if (!empty($item->path)) {
  969. $_menu['path index'][$item->path] = $item->mid;
  970. }
  971. }
  972. }
  973. // If the administrator has changed the item, reflect the change.
  974. if ($item->type & MENU_MODIFIED_BY_ADMIN) {
  975. $_menu['items'][$item->mid]['title'] = $item->title;
  976. $_menu['items'][$item->mid]['description'] = $item->description;
  977. $_menu['items'][$item->mid]['pid'] = $item->pid;
  978. $_menu['items'][$item->mid]['weight'] = $item->weight;
  979. $_menu['items'][$item->mid]['type'] = $item->type;
  980. }
  981. }
  982. }
  983. // Associate parent and child menu items.
  984. _menu_find_parents($_menu['items']);
  985. // Prepare to display trees to the user as required.
  986. _menu_build_visible_tree();
  987. }
  988. /**
  989. * Determine whether the given menu item is accessible to the current user.
  990. *
  991. * Use this instead of just checking the "access" property of a menu item
  992. * to properly handle items with fall-through semantics.
  993. */
  994. function _menu_item_is_accessible($mid) {
  995. $menu = menu_get_menu();
  996. // Follow the path up to find the first "access" attribute.
  997. $path = isset($menu['items'][$mid]['path']) ? $menu['items'][$mid]['path'] : NULL;
  998. while ($path && (!isset($menu['path index'][$path]) || !isset($menu['items'][$menu['path index'][$path]]['access']))) {
  999. $path = substr($path, 0, strrpos($path, '/'));
  1000. }
  1001. if (empty($path)) {
  1002. // Items without any access attribute up the chain are denied, unless they
  1003. // were created by the admin. They most likely point to non-Drupal directories
  1004. // or to an external URL and should be allowed.
  1005. return $menu['items'][$mid]['type'] & MENU_CREATED_BY_ADMIN;
  1006. }
  1007. return $menu['items'][$menu['path index'][$path]]['access'];
  1008. }
  1009. /**
  1010. * Find all visible items in the menu tree, for ease in displaying to user.
  1011. *
  1012. * Since this is only for display, we only need title, path, and children
  1013. * for each item.
  1014. */
  1015. function _menu_build_visible_tree($pid = 0) {
  1016. global $_menu;
  1017. if (isset($_menu['items'][$pid])) {
  1018. $parent = $_menu['items'][$pid];
  1019. $children = array();
  1020. if (isset($parent['children'])) {
  1021. usort($parent['children'], '_menu_sort');
  1022. foreach ($parent['children'] as $mid) {
  1023. $children = array_merge($children, _menu_build_visible_tree($mid));
  1024. }
  1025. }
  1026. $visible = ($parent['type'] & MENU_VISIBLE_IN_TREE) ||
  1027. ($parent['type'] & MENU_VISIBLE_IF_HAS_CHILDREN && count($children) > 0);
  1028. $allowed = _menu_item_is_accessible($pid);
  1029. if (($parent['type'] & MENU_IS_ROOT) || ($visible && $allowed)) {
  1030. $_menu['visible'][$pid] = array('title' => $parent['title'], 'path' => $parent['path'], 'children' => $children, 'type' => $parent['type']);
  1031. foreach ($children as $mid) {
  1032. $_menu['visible'][$mid]['pid'] = $pid;
  1033. }
  1034. return array($pid);
  1035. }
  1036. else {
  1037. return $children;
  1038. }
  1039. }
  1040. return array();
  1041. }
  1042. /**
  1043. * Account for menu items that are only defined at certain paths, so will not
  1044. * be cached.
  1045. *
  1046. * We don't support the full range of menu item options for these menu items. We
  1047. * don't support MENU_VISIBLE_IF_HAS_CHILDREN, and we require parent items to be
  1048. * declared before their children.
  1049. */
  1050. function _menu_append_contextual_items() {
  1051. global $_menu;
  1052. // Build a sequential list of all menu items.
  1053. $menu_item_list = module_invoke_all('menu', FALSE);
  1054. // Menu items not in the DB get temporary negative IDs.
  1055. $temp_mid = min(array_keys($_menu['items'])) - 1;
  1056. $new_items = array();
  1057. foreach ($menu_item_list as $item) {
  1058. if (isset($item['callback'])) {
  1059. $_menu['callbacks'][$item['path']] = array('callback' => $item['callback']);
  1060. if (isset($item['callback arguments'])) {
  1061. $_menu['callbacks'][$item['path']]['callback arguments'] = $item['callback arguments'];
  1062. }
  1063. }
  1064. unset($item['callback']);
  1065. unset($item['callback arguments']);
  1066. if (!isset($_menu['path index'][$item['path']])) {
  1067. if (!isset($item['path'])) {
  1068. $item['path'] = '';
  1069. }
  1070. if (!isset($item['type'])) {
  1071. $item['type'] = MENU_NORMAL_ITEM;
  1072. }
  1073. if (!isset($item['weight'])) {
  1074. $item['weight'] = 0;
  1075. }
  1076. $_menu['items'][$temp_mid] = $item;
  1077. $_menu['path index'][$item['path']] = $temp_mid;
  1078. $new_items[$temp_mid] = $item;
  1079. $temp_mid--;
  1080. }
  1081. else {
  1082. $mid = $_menu['path index'][$item['path']];
  1083. if ($_menu['items'][$mid]['type'] & MENU_CREATED_BY_ADMIN) {
  1084. $_menu['items'][$mid]['access'] = $item['access'];
  1085. if (isset($_menu['items'][$mid]['callback'])) {
  1086. $_menu['items'][$mid]['callback'] = $item['callback'];
  1087. }
  1088. if (isset($_menu['items'][$mid]['callback arguments'])) {
  1089. $_menu['items'][$mid]['callback arguments'] = $item['callback arguments'];
  1090. }
  1091. }
  1092. }
  1093. }
  1094. // Establish parent-child relationships.
  1095. _menu_find_parents($new_items);
  1096. // Add new items to the visible tree if necessary.
  1097. foreach ($new_items as $mid => $item) {
  1098. $item = $_menu['items'][$mid];
  1099. if (($item['type'] & MENU_VISIBLE_IN_TREE) && _menu_item_is_accessible($mid)) {
  1100. $pid = $item['pid'];
  1101. while ($pid && !isset($_menu['visible'][$pid])) {
  1102. $pid = $_menu['items'][$pid]['pid'];
  1103. }
  1104. $_menu['visible'][$mid] = array('title' => $item['title'], 'path' => $item['path'], 'pid' => $pid);
  1105. $_menu['visible'][$pid]['children'][] = $mid;
  1106. usort($_menu['visible'][$pid]['children'], '_menu_sort');
  1107. }
  1108. }
  1109. }
  1110. /**
  1111. * Establish parent-child relationships.
  1112. */
  1113. function _menu_find_parents(&$items) {
  1114. global $_menu;
  1115. foreach ($items as $mid => $item) {
  1116. if (!isset($item['pid'])) {
  1117. // Parent's location has not been customized, so figure it out using the path.
  1118. $parent = $item['path'];
  1119. if ($parent) {
  1120. do {
  1121. $parent = substr($parent, 0, strrpos($parent, '/'));
  1122. }
  1123. while ($parent && !isset($_menu['path index'][$parent]));
  1124. }
  1125. $pid = $parent ? $_menu['path index'][$parent] : 1;
  1126. $_menu['items'][$mid]['pid'] = $pid;
  1127. }
  1128. else {
  1129. $pid = $item['pid'];
  1130. }
  1131. // Don't make root a child of itself.
  1132. if ($mid) {
  1133. if (isset ($_menu['items'][$pid])) {
  1134. $_menu['items'][$pid]['children'][] = $mid;
  1135. }
  1136. else {
  1137. // If parent is missing, it is a menu item that used to be defined
  1138. // but is no longer. Default to a root-level "Navigation" menu item.
  1139. $_menu['items'][1]['children'][] = $mid;
  1140. }
  1141. }
  1142. }
  1143. }
  1144. /**
  1145. * Find all the items in the current local task tree.
  1146. *
  1147. * Since this is only for display, we only need title, path, and children
  1148. * for each item.
  1149. *
  1150. * At the close of this function, $_menu['local tasks'] is populated with the
  1151. * menu items in the local task tree.
  1152. *
  1153. * @return
  1154. * TRUE if the local task tree is forked. It does not need to be displayed
  1155. * otherwise.
  1156. */
  1157. function _menu_build_local_tasks($pid) {
  1158. global $_menu;
  1159. $forked = FALSE;
  1160. if (isset($_menu['items'][$pid])) {
  1161. $parent = $_menu['items'][$pid];
  1162. $children = array();
  1163. if (isset($parent['children'])) {
  1164. foreach ($parent['children'] as $mid) {
  1165. if (($_menu['items'][$mid]['type'] & MENU_IS_LOCAL_TASK) && _menu_item_is_accessible($mid)) {
  1166. $children[] = $mid;
  1167. // Beware short-circuiting || operator!
  1168. $forked = _menu_build_local_tasks($mid) || $forked;
  1169. }
  1170. }
  1171. }
  1172. usort($children, '_menu_sort');
  1173. $forked = $forked || count($children) > 1;
  1174. $_menu['local tasks'][$pid] = array('title' => $parent['title'], 'path' => $parent['path'], 'children' => $children);
  1175. foreach ($children as $mid) {
  1176. $_menu['local tasks'][$mid]['pid'] = $pid;
  1177. }
  1178. }
  1179. return $forked;
  1180. }
  1181. /**
  1182. * Returns TRUE if the site is off-line for maintenance.
  1183. */
  1184. function _menu_site_is_offline() {
  1185. // Check if site is set to off-line mode
  1186. if (variable_get('site_offline', 0)) {
  1187. // Check if the user has administration privileges
  1188. if (!user_access('administer site configuration')) {
  1189. // Check if this is an attempt to login
  1190. if (drupal_get_normal_path($_GET['q']) != 'user') {
  1191. return TRUE;
  1192. }
  1193. }
  1194. else {
  1195. $offline_message = t('Operating in off-line mode.');
  1196. $messages = drupal_set_message();
  1197. // Ensure that the off-line message is displayed only once [allowing for page redirects].
  1198. if (!isset($messages) || !isset($messages['status']) || !in_array($offline_message, $messages['status'])) {
  1199. drupal_set_message($offline_message);
  1200. }
  1201. }
  1202. }
  1203. return FALSE;
  1204. }
Login or register to post comments