admin_menu.module

Tracking 5.x-3.x branch
  1. drupal
    1. 5 contributions/admin_menu/admin_menu.module
    2. 6 contributions/admin_menu/admin_menu.module
    3. 7 contributions/admin_menu/admin_menu.module

Render an administrative menu as a dropdown menu at the top of the window.

Note: Most theme-functions in Administration Menu are not invoked via theme(), because we try to keep this module as fast as possible and chances are very small that someone wants to override those functions.

Functions & methods

NameDescription
admin_menu_add_itemAdd a custom menu item.
admin_menu_admin_menuImplementation of hook_admin_menu().
admin_menu_admin_menu_replacementsImplementation of hook_admin_menu_replacements().
admin_menu_cache_getRetrieve a client-side cache hash from cache.
admin_menu_cache_setStore a client-side cache hash in persistent cache.
admin_menu_clear_cacheClear the cached admin menu tree.
admin_menu_footerImplementation of hook_footer().
admin_menu_get_menuReturn administration menu from cache or rebuild it.
admin_menu_get_user_countReturn count of online anonymous/authenticated users.
admin_menu_helpImplementation of hook_help().
admin_menu_item_urlAdjust the menu item path.
admin_menu_jsImplementation of hook_js().
admin_menu_js_cacheMenu callback; Output administration menu for HTTP caching via AJAX request.
admin_menu_menuImplementation of hook_menu().
admin_menu_outputBuild the administration menu output.
admin_menu_panels_cacheImplementation of hook_panels_cache().
admin_menu_permImplementation of hook_perm().
admin_menu_suppressSuppress display of administration menu.
admin_menu_toggle_modulesMenu callback; Enable/disable developer modules.
theme_admin_menu_itemHigh-performance implementation of theme_menu_item().
theme_admin_menu_treeReturn a rendered menu tree.
_admin_menu_sortComparator routine for use in sorting menu items.

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * Render an administrative menu as a dropdown menu at the top of the window.
  5. *
  6. * Note: Most theme-functions in Administration Menu are not invoked via theme(),
  7. * because we try to keep this module as fast as possible and chances are very
  8. * small that someone wants to override those functions.
  9. */
  10. /**
  11. * Implementation of hook_help().
  12. */
  13. function admin_menu_help($section) {
  14. switch ($section) {
  15. case 'admin/settings/admin_menu':
  16. return t('The administration menu module provides a dropdown menu arranged for one- or two-click access to most administrative tasks and other common destinations (to users with the proper permissions). Use the settings below to customize the appearance of the menu.');
  17. case 'admin/help#admin_menu':
  18. $output = '';
  19. $output .= '<p>' . t('The administration menu module provides a dropdown menu arranged for one- or two-click access to most administrative tasks and other common destinations (to users with the proper permissions). Administration menu also displays the number of anonymous and authenticated users, and allows modules to add their own custom menu items. Integration with the menu varies from module to module; the contributed module <a href="@drupal">Devel</a>, for instance, makes strong use of the administration menu module to provide quick access to development tools.', array('@drupal' => 'http://drupal.org/project/devel')) . '</p>';
  20. $output .= '<p>' . t('The administration menu <a href="@settings">settings page</a> allows you to modify some elements of the menu\'s behavior and appearance. Since the appearance of the menu is dependent on your site theme, substantial customizations require modifications to your site\'s theme and CSS files. See the advanced module README.txt file for more information on theme and CSS customizations.', array('@settings' => url('admin/settings/admin_menu'))) . '</p>';
  21. $output .= '<p>' . t('The menu items displayed in the administration menu depend upon the actual permissions of the viewer. First, the administration menu is only displayed to users in roles with the <em>Access administration menu</em> (admin_menu module) permission. Second, a user must be a member of a role with the <em>Access administration pages</em> (system module) permission to view administrative links. And, third, only currently permitted links are displayed; for example, if a user is not a member of a role with the permissions <em>Administer permissions</em> (user module) and <em>Administer users</em> (user module), the <em>User management</em> menu item is not displayed.') . '</p>';
  22. return $output;
  23. }
  24. }
  25. /**
  26. * Implementation of hook_perm().
  27. */
  28. function admin_menu_perm() {
  29. return array('access administration menu', 'display drupal links');
  30. }
  31. /**
  32. * Implementation of hook_menu().
  33. *
  34. * We can't move this into admin_menu_footer(), because PHP-only based themes
  35. * like chameleon load and output scripts and stylesheets in front of
  36. * theme_closure(), so we ensure Admin menu's styles and scripts are loaded on
  37. * all pages via hook_menu().
  38. */
  39. function admin_menu_menu($may_cache) {
  40. $items = array();
  41. if ($may_cache) {
  42. // AJAX callback.
  43. $items[] = array(
  44. 'path' => 'js/admin_menu/cache',
  45. 'callback' => 'admin_menu_js_cache',
  46. 'access' => user_access('access administration menu'),
  47. 'type' => MENU_CALLBACK,
  48. );
  49. // Module settings.
  50. $items[] = array(
  51. 'path' => 'admin/settings/admin_menu',
  52. 'title' => t('Administration menu'),
  53. 'description' => t('Adjust administration menu settings.'),
  54. 'callback' => 'drupal_get_form',
  55. 'callback arguments' => array('admin_menu_theme_settings'),
  56. 'access' => user_access('administer site configuration'),
  57. );
  58. // Menu link callbacks.
  59. $items[] = array(
  60. 'path' => 'admin_menu/toggle-modules',
  61. 'callback' => 'admin_menu_toggle_modules',
  62. 'access' => user_access('administer site configuration'),
  63. 'type' => MENU_CALLBACK,
  64. );
  65. admin_menu_clear_cache();
  66. return $items;
  67. }
  68. if (!user_access('access administration menu') || admin_menu_suppress(FALSE)) {
  69. return $items;
  70. }
  71. // Performance: Skip this entirely for AJAX requests.
  72. if (strpos($_GET['q'], 'js/') === 0) {
  73. return $items;
  74. }
  75. global $user, $locale;
  76. $path = drupal_get_path('module', 'admin_menu');
  77. drupal_add_css($path . '/admin_menu.css', 'module', 'all', FALSE);
  78. if ($user->uid == 1) {
  79. drupal_add_css($path . '/admin_menu.uid1.css', 'module', 'all', FALSE);
  80. }
  81. // Performance: Defer execution.
  82. drupal_add_js($path . '/admin_menu.js', 'module', 'header', TRUE);
  83. // Destination query strings are applied via JS.
  84. // @see drupal_get_destination()
  85. $url_path = isset($_GET['q']) ? $_GET['q'] : '';
  86. $url_query = drupal_query_string_encode($_GET, array('q'));
  87. if ($url_query != '') {
  88. $url_path .= '?' . $url_query;
  89. }
  90. $settings['destination'] = 'destination='. urlencode($url_path);
  91. // Hash for client-side HTTP/AJAX caching.
  92. $cid = 'admin_menu:' . $user->uid . ':' . $locale;
  93. if (!empty($_COOKIE['has_js']) && ($hash = admin_menu_cache_get($cid))) {
  94. $settings['hash'] = $hash;
  95. // The base path to use for cache requests depends on whether clean URLs
  96. // are enabled, whether Drupal runs in a sub-directory, and on the language
  97. // system configuration. url() already provides us the proper path and also
  98. // invokes potentially existing custom_url_rewrite() functions, which may
  99. // add further required components to the URL to provide context. Due to
  100. // those components, and since url('') returns only base_path() when clean
  101. // URLs are disabled, we need to use a replacement token as path. Yuck.
  102. $settings['basePath'] = url('admin_menu');
  103. }
  104. $replacements = module_invoke_all('admin_menu_replacements');
  105. if (!empty($replacements)) {
  106. $settings['replacements'] = $replacements;
  107. }
  108. if ($setting = variable_get('admin_menu_margin_top', 1)) {
  109. $settings['margin_top'] = $setting;
  110. }
  111. if ($setting = variable_get('admin_menu_position_fixed', 0)) {
  112. $settings['position_fixed'] = $setting;
  113. }
  114. if ($setting = variable_get('admin_menu_tweak_tabs', 0)) {
  115. $settings['tweak_tabs'] = $setting;
  116. }
  117. if ($_GET['q'] == 'admin/build/menu' && variable_get('admin_menu_tweak_menu', 0)) {
  118. drupal_add_js($path . '/admin_menu.menu.js');
  119. drupal_add_js('misc/collapse.js');
  120. }
  121. if ($_GET['q'] == 'admin/build/modules' || strpos($_GET['q'], 'admin/build/modules/list') === 0) {
  122. $settings['tweak_modules'] = variable_get('admin_menu_tweak_modules', 0);
  123. }
  124. drupal_add_js(array('admin_menu' => $settings), 'setting');
  125. if ($_GET['q'] == 'admin/settings/admin_menu' || $_GET['q'] == 'admin/settings/clean-urls' || $_GET['q'] == 'admin/settings/devel') {
  126. require_once $path . '/admin_menu.inc';
  127. }
  128. return $items;
  129. }
  130. /**
  131. * Suppress display of administration menu.
  132. *
  133. * This function should be called from within another module's page callback
  134. * (preferably using module_invoke()) when the menu should not be displayed.
  135. * This is useful for modules that implement popup pages or other special
  136. * pages where the menu would be distracting or break the layout.
  137. *
  138. * @param $set
  139. * Defaults to TRUE. If called before hook_footer(), the menu will not be
  140. * displayed. If FALSE is passed, the suppression state is returned.
  141. */
  142. function admin_menu_suppress($set = TRUE) {
  143. static $suppress = FALSE;
  144. // drupal_add_js() must only be invoked once.
  145. if (!empty($set) && $suppress === FALSE) {
  146. $suppress = TRUE;
  147. drupal_add_js(array('admin_menu' => array('suppress' => 1)), 'setting');
  148. }
  149. return $suppress;
  150. }
  151. /**
  152. * Implementation of hook_footer().
  153. */
  154. function admin_menu_footer($main = 0) {
  155. return admin_menu_output();
  156. }
  157. /**
  158. * Implementation of hook_js().
  159. */
  160. function admin_menu_js() {
  161. return array(
  162. 'cache' => array(
  163. 'callback' => 'admin_menu_js_cache',
  164. 'includes' => array('common', 'theme', 'unicode'),
  165. 'dependencies' => array('devel', 'filter', 'user'),
  166. ),
  167. );
  168. }
  169. /**
  170. * Retrieve a client-side cache hash from cache.
  171. *
  172. * The hash cache is consulted more than once per request; we therefore cache
  173. * the results statically to avoid multiple database requests.
  174. *
  175. * This should only be used for client-side cache hashes. Use cache_menu for
  176. * administration menu content.
  177. *
  178. * @param $cid
  179. * The cache ID of the data to retrieve.
  180. */
  181. function admin_menu_cache_get($cid) {
  182. static $cache = array();
  183. if (!variable_get('admin_menu_cache_client', TRUE)) {
  184. return FALSE;
  185. }
  186. if (!array_key_exists($cid, $cache)) {
  187. $cache[$cid] = cache_get($cid, 'cache_admin_menu');
  188. if ($cache[$cid] && isset($cache[$cid]->data)) {
  189. $cache[$cid] = $cache[$cid]->data;
  190. }
  191. }
  192. return $cache[$cid];
  193. }
  194. /**
  195. * Store a client-side cache hash in persistent cache.
  196. *
  197. * This should only be used for client-side cache hashes. Use cache_menu for
  198. * administration menu content.
  199. *
  200. * @param $cid
  201. * The cache ID of the data to retrieve.
  202. */
  203. function admin_menu_cache_set($cid, $data) {
  204. if (variable_get('admin_menu_cache_client', TRUE)) {
  205. cache_set($cid, 'cache_admin_menu', $data);
  206. }
  207. }
  208. /**
  209. * Menu callback; Output administration menu for HTTP caching via AJAX request.
  210. */
  211. function admin_menu_js_cache($hash = NULL) {
  212. // Get the rendered menu.
  213. $content = admin_menu_output();
  214. // @todo According to http://www.mnot.net/blog/2006/05/11/browser_caching,
  215. // IE will only cache the content when it is compressed.
  216. // Determine if the client accepts gzipped data.
  217. if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
  218. if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== FALSE) {
  219. $encoding = 'x-gzip';
  220. }
  221. elseif (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) {
  222. $encoding = 'gzip';
  223. }
  224. // Perform gzip compression when:
  225. // 1) the user agent supports gzip compression.
  226. // 2) Drupal page compression is enabled. Sites may wish to perform the
  227. // compression at the web server level (e.g. using mod_deflate).
  228. // 3) PHP's zlib extension is loaded, but zlib compression is disabled.
  229. if (isset($encoding) && variable_get('page_compression', TRUE) && extension_loaded('zlib') && zlib_get_coding_type() === FALSE) {
  230. // Use Vary header to tell caches to keep separate versions of the menu
  231. // based on user agent capabilities.
  232. header('Vary: Accept-Encoding');
  233. // Since we manually perform compression, we are also responsible to
  234. // send a proper encoding header.
  235. header('Content-Encoding: ' . $encoding);
  236. $content = gzencode($content, 9, FORCE_GZIP);
  237. }
  238. }
  239. $expires = time() + (3600 * 24 * 365);
  240. header('Expires: ' . gmdate('D, d M Y H:i:s', $expires) . ' GMT');
  241. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  242. header('Cache-Control: max-age=' . $expires);
  243. header('Content-Length: ' . strlen($content));
  244. // Suppress Devel module.
  245. $GLOBALS['devel_shutdown'] = FALSE;
  246. echo $content;
  247. exit;
  248. }
  249. /**
  250. * Implementation of hook_admin_menu_replacements().
  251. */
  252. function admin_menu_admin_menu_replacements() {
  253. $items = array();
  254. if ($user_count = admin_menu_get_user_count()) {
  255. $items['.admin-menu-users a'] = $user_count;
  256. }
  257. return $items;
  258. }
  259. /**
  260. * Return count of online anonymous/authenticated users.
  261. *
  262. * @see user_block(), user.module
  263. */
  264. function admin_menu_get_user_count() {
  265. $interval = time() - variable_get('user_block_seconds_online', 900);
  266. $count_anon = sess_count($interval);
  267. $count_auth = db_result(db_query("SELECT COUNT(DISTINCT uid) FROM {sessions} WHERE uid > 0 AND timestamp >= %d", $interval));
  268. return t('@count-anon / @count-auth', array('@count-anon' => $count_anon, '@count-auth' => $count_auth));
  269. }
  270. /**
  271. * Build the administration menu output.
  272. */
  273. function admin_menu_output() {
  274. if (!user_access('access administration menu') || admin_menu_suppress(FALSE)) {
  275. return;
  276. }
  277. global $user, $locale;
  278. $cache_server_enabled = variable_get('admin_menu_cache_server', TRUE);
  279. // Determine whether we need to rebuild.
  280. $rebuild = variable_get('admin_menu_rebuild_links', FALSE);
  281. $cid = 'admin_menu:' . $user->uid . ':' . $locale;
  282. // Do nothing at all here if the client supports client-side caching, no
  283. // rebuild is needed, the user has a hash, and is NOT requesting the cache
  284. // update path. Consult the hash cache last, since it requires a DB request.
  285. // @todo Implement a sanity-check to prevent permanent double requests; i.e.
  286. // what if the client-side cache fails for any reason and performs a second
  287. // request on every page?
  288. if (!empty($_COOKIE['has_js']) && !$rebuild && strpos($_GET['q'], 'js/admin_menu/cache') !== 0) {
  289. if (admin_menu_cache_get($cid)) {
  290. return;
  291. }
  292. }
  293. // Try to load and output administration menu from server-side cache.
  294. if ($cache_server_enabled) {
  295. $cache = cache_get($cid, 'cache_menu');
  296. if (!$rebuild && $cache && isset($cache->data)) {
  297. $content = $cache->data;
  298. }
  299. }
  300. // Rebuild the output.
  301. if (!isset($content)) {
  302. // Add site name as CSS class for development/staging theming purposes. We
  303. // leverage the cookie domain instead of HTTP_HOST to account for many (but
  304. // not all) multi-domain setups (e.g. language-based sub-domains).
  305. $class_site = 'admin-menu-site' . drupal_strtolower(preg_replace('/[^a-zA-Z0-9-]/', '-', $GLOBALS['cookie_domain']));
  306. $_admin_menu = &admin_menu_get_menu();
  307. // Allow other modules to integrate with admin_menu (uncached).
  308. foreach (module_implements('admin_menu') as $module) {
  309. $function = $module . '_admin_menu';
  310. $function($_admin_menu, FALSE);
  311. }
  312. $content = '<div id="admin-menu" class="' . $class_site . '">';
  313. $content .= theme_admin_menu_tree($_admin_menu['index']['admin']);
  314. $content .= '</div>';
  315. // Cache the menu for this user.
  316. if ($cache_server_enabled) {
  317. cache_set($cid, 'cache_menu', $content, time() + (60 * 60 * 24));
  318. variable_del('admin_menu_rebuild_links');
  319. }
  320. }
  321. // Store the new hash for this user.
  322. if (!empty($_COOKIE['has_js'])) {
  323. admin_menu_cache_set($cid, md5($content));
  324. }
  325. return $content;
  326. }
  327. /**
  328. * Return administration menu from cache or rebuild it.
  329. *
  330. * @return
  331. * An array containing a complete menu structure of all cached administration
  332. * menu items.
  333. */
  334. function &admin_menu_get_menu() {
  335. static $_admin_menu;
  336. if (isset($_admin_menu)) {
  337. return $_admin_menu;
  338. }
  339. global $user, $locale;
  340. $cid = $user->uid . ':' . $locale . ':admin_menu';
  341. $cache = cache_get($cid, 'cache_menu');
  342. // Check if cache is an array needed to distinguish between v5.x-1.2 and later
  343. // versions.
  344. if ($cache && substr($cache->data, 0, 1) == 'a') {
  345. $_admin_menu = unserialize($cache->data);
  346. }
  347. else {
  348. require_once drupal_get_path('module', 'admin_menu') . '/admin_menu.inc';
  349. // Ensure all modules are loaded when we need to rebuild.
  350. drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
  351. module_list(TRUE, FALSE, FALSE);
  352. menu_get_menu();
  353. admin_menu_build($_admin_menu);
  354. cache_set($cid, 'cache_menu', serialize($_admin_menu), time() + (60 * 60 * 24));
  355. }
  356. return $_admin_menu;
  357. }
  358. /**
  359. * Return a rendered menu tree.
  360. *
  361. * @param $pid
  362. * The menu item id to use for the administration menu.
  363. *
  364. * @return string
  365. * The complete, rendered administration menu.
  366. */
  367. function theme_admin_menu_tree($pid = 5) {
  368. $_admin_menu = admin_menu_get_menu();
  369. $output = '';
  370. if (!empty($_admin_menu[$pid]['children'])) {
  371. foreach ($_admin_menu[$pid]['children'] as $mid) {
  372. $output .= theme_admin_menu_item($mid, theme_admin_menu_tree($mid), empty($_admin_menu[$mid]['children']));
  373. }
  374. }
  375. return $output ? "\n<ul>" . $output . '</ul>' : '';
  376. }
  377. /**
  378. * High-performance implementation of theme_menu_item().
  379. *
  380. * This saves us a theme() call and does only the absolute minimum to get
  381. * the admin menu links rendered.
  382. *
  383. * @param $mid
  384. * The menu id of the item.
  385. * @param $children
  386. * A string containing any rendered child items of this menu.
  387. * @param $leaf
  388. * A boolean indicating whether this menu item is a leaf.
  389. */
  390. function theme_admin_menu_item($mid, $children = '', $leaf = TRUE) {
  391. static $display_option, $destination;
  392. $_admin_menu = admin_menu_get_menu();
  393. $item = $_admin_menu[$mid];
  394. if (!isset($display_option)) {
  395. $display_option = variable_get('admin_menu_display', 0);
  396. $destination = drupal_get_destination();
  397. }
  398. // Display extra information about menu items if enabled (devel).
  399. if ($display_option) {
  400. if ($display_option == 'mid') {
  401. $item['title'] = $item['title'] . ' (' . $mid . ')';
  402. }
  403. else if (isset($item[$display_option])) {
  404. $item['title'] = $item['title'] . ' (' . $item[$display_option] . ')';
  405. }
  406. }
  407. $class = array();
  408. if (!$leaf) {
  409. $class[] = 'expandable';
  410. }
  411. if (isset($item['class'])) {
  412. $class[] = $item['class'];
  413. }
  414. // Strip destination query string from href attribute and apply a CSS class
  415. // for our JavaScript behavior instead.
  416. $path = preg_replace('/[\?|&]' . preg_quote($destination, '/') . '/', '', $item['path']);
  417. if ($path != $item['path']) {
  418. if (!empty($item['attributes']['class'])) {
  419. $item['attributes']['class'] .= ' admin-menu-destination';
  420. }
  421. else {
  422. $item['attributes']['class'] = 'admin-menu-destination';
  423. }
  424. }
  425. $output = '<li' . (!empty($class) ? ' class="' . implode(' ', $class) . '"' : '') . '>';
  426. $output .= '<a href="' . check_url($path) . '"' . drupal_attributes($item['attributes']) . '>' . filter_xss_admin($item['title']) . '</a>' . $children . '</li>';
  427. return $output;
  428. }
  429. /**
  430. * Comparator routine for use in sorting menu items.
  431. */
  432. function _admin_menu_sort($a, $b) {
  433. $_admin_menu = admin_menu_get_menu();
  434. $a = $_admin_menu[$a];
  435. $b = $_admin_menu[$b];
  436. if ($a['weight'] < $b['weight']) {
  437. return -1;
  438. }
  439. elseif ($a['weight'] > $b['weight']) {
  440. return 1;
  441. }
  442. elseif (isset($a['title']) && isset($b['title'])) {
  443. return strnatcasecmp($a['title'], $b['title']);
  444. }
  445. else {
  446. return 1;
  447. }
  448. }
  449. /**
  450. * Adjust the menu item path.
  451. *
  452. * Adjust the path of local tasks and let them point to their parent item.
  453. * Finally build the url. These functions have been moved here to be able
  454. * to cache the final results.
  455. *
  456. * @param &$item
  457. * An admin menu item.
  458. */
  459. function admin_menu_item_url(&$_admin_menu, $mid) {
  460. $link_item = $mid;
  461. while ($_admin_menu[$link_item]['type'] & MENU_LINKS_TO_PARENT) {
  462. $link_item = $_admin_menu[$link_item]['pid'];
  463. }
  464. if (!isset($_admin_menu[$link_item]['processed'])) {
  465. $_admin_menu[$mid]['path'] = url($_admin_menu[$link_item]['path'], isset($_admin_menu[$mid]['query']) ? $_admin_menu[$mid]['query'] : NULL);
  466. $_admin_menu[$link_item]['processed'] = TRUE;
  467. }
  468. else {
  469. // Copy the already processed path of the parent item to the
  470. // default local task.
  471. $_admin_menu[$mid]['path'] = $_admin_menu[$link_item]['path'];
  472. }
  473. }
  474. /**
  475. * Add a custom menu item.
  476. *
  477. * @param $_admin_menu
  478. * An array containing the complete administration menu structure, passed by
  479. * reference.
  480. * @param $pid
  481. * The parent menu item id.
  482. * @param $item
  483. * An menu item array for the menu system. May contain the key 'weight' to
  484. * adjust the item's weight.
  485. * @param $mid
  486. * (Optional) The menu item id to use. Should be specified for already
  487. * existing menu items that are copied/relocated elsewhere.
  488. *
  489. * @return
  490. * The id of the new menu item.
  491. */
  492. function admin_menu_add_item(&$_admin_menu, $pid, $item, $mid = NULL) {
  493. static $custom_mid;
  494. if (empty($item['path'])) {
  495. return FALSE;
  496. }
  497. $item['pid'] = $pid;
  498. $item['children'] = array();
  499. if (!isset($custom_mid)) {
  500. $custom_mid = max(array_keys($_admin_menu)) + 10000;
  501. }
  502. if (!isset($mid)) {
  503. $mid = $custom_mid++;
  504. }
  505. else {
  506. // Ensure we have an integer.
  507. $mid = (int) $mid;
  508. }
  509. $_admin_menu[$mid] = $item;
  510. $_admin_menu[$pid]['children'][] = $mid;
  511. $_admin_menu['index'][$item['path']] = $mid;
  512. admin_menu_item_url($_admin_menu, $mid);
  513. // Sort items.
  514. usort($_admin_menu[$pid]['children'], '_admin_menu_sort');
  515. return $mid;
  516. }
  517. /**
  518. * Implementation of hook_admin_menu().
  519. *
  520. * @param array $admin_menu
  521. * An array containing the complete administration menu structure, passed by
  522. * reference.
  523. * @param bool $may_cache
  524. * Whether changes will be cached. If new menu items contain dynamic
  525. * information, such as query strings or user-related data, these should be
  526. * added on each page request ($may_cache = FALSE).
  527. */
  528. function admin_menu_admin_menu(&$admin_menu, $may_cache) {
  529. if (!$may_cache) {
  530. // Add count of active anonymous/authenticated users.
  531. $mid_admin = $admin_menu['index']['admin'];
  532. admin_menu_add_item($admin_menu, $mid_admin, array(
  533. 'title' => admin_menu_get_user_count(),
  534. 'description' => t('Current anonymous / authenticated users'),
  535. 'path' => 'user',
  536. 'weight' => -90,
  537. 'class' => 'admin-menu-action admin-menu-users',
  538. ));
  539. }
  540. }
  541. /**
  542. * Implementation of hook_admin_menu() for Devel module (temporary).
  543. *
  544. * - Adds most used functions 'empty cache' and 'variable editor' to the menu in
  545. * Administration Menu's icon.
  546. * - Adds links to switch to a different user to the logout button.
  547. */
  548. if (!function_exists('devel_admin_menu')) {
  549. function devel_admin_menu(&$admin_menu, $may_cache) {
  550. $access_devel = user_access('access devel information');
  551. $access_switch = user_access('switch users');
  552. if (!$access_devel && !$access_switch) {
  553. return;
  554. }
  555. $mid_icon = $admin_menu['index']['admin_menu_icon'];
  556. if ($may_cache) {
  557. // Add variable editor.
  558. if ($access_devel) {
  559. admin_menu_add_item($admin_menu, $mid_icon, array('title' => t('Variable editor'), 'path' => 'devel/variable', 'weight' => 20));
  560. }
  561. }
  562. else {
  563. // Add clear-cache.
  564. if ($access_devel) {
  565. admin_menu_add_item($admin_menu, $mid_icon, array('title' => t('Empty cache'), 'path' => 'devel/cache/clear', 'weight' => 20, 'query' => drupal_get_destination()));
  566. }
  567. // Add switch_user items.
  568. if ($access_switch && $devel_user_links = module_invoke('devel', 'switch_user_list')) {
  569. global $user;
  570. $mid_user = $admin_menu['index']["user/$user->uid"];
  571. foreach ($devel_user_links as $link) {
  572. if (is_array($link)) {
  573. admin_menu_add_item($admin_menu, $mid_user, array(
  574. 'title' => $link['title'],
  575. 'description' => $link['attributes']['title'],
  576. 'path' => $link['href'],
  577. 'query' => $link['query'],
  578. ));
  579. }
  580. // @todo Remove when Devel 5.x-1.1 has been released.
  581. elseif (preg_match('!href="' . base_path() . '([^\?]+)\?([^"]+)" title="([^"]+)">((<em>)?[^<]+(</em>)?)!', $link, $match)) {
  582. admin_menu_add_item($admin_menu, $mid_user, array('title' => $match[4], 'description' => $match[3], 'path' => urldecode($match[1]), 'query' => $match[2]));
  583. }
  584. }
  585. }
  586. }
  587. }
  588. }
  589. /**
  590. * Clear the cached admin menu tree.
  591. */
  592. function admin_menu_clear_cache() {
  593. // Flush cached menu tree.
  594. // cache_clear_all() does not support a leading wildcard.
  595. db_query("DELETE FROM {cache_menu} WHERE cid LIKE '%%:admin_menu'");
  596. variable_set('admin_menu_rebuild_links', TRUE);
  597. // Flush cached output of admin_menu.
  598. cache_clear_all('admin_menu:', 'cache_menu', TRUE);
  599. // Flush client-side cache hashes.
  600. cache_clear_all('*', 'cache_admin_menu', TRUE);
  601. }
  602. /**
  603. * Implementation of hook_panels_cache().
  604. *
  605. * We're misusing the panels_cache hook here: whenever called, check if the
  606. * current request has a POST payload, and clear the cached admin menu.
  607. * This can easily be seen as a hack, as it will happen way too often when
  608. * working on the panels admin pages.
  609. */
  610. function admin_menu_panels_cache() {
  611. if (!empty($_POST)) {
  612. admin_menu_clear_cache();
  613. }
  614. }
  615. /**
  616. * Menu callback; Enable/disable developer modules.
  617. */
  618. function admin_menu_toggle_modules() {
  619. require_once drupal_get_path('module', 'admin_menu') . '/admin_menu.inc';
  620. _admin_menu_toggle_modules();
  621. }