This repository has been archived on 2025-06-21. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
suitedesk/modules/admin_menu/admin_menu.inc

566 lines
20 KiB
PHP

<?php
/**
* The key function that builds the menu links whenever there is a menu rebuild.
*/
function _admin_menu_rebuild_links() {
$menu = NULL;
// Since it's possible this core function might change, check
// that it exists. We do this instead of calling menu_router_build()
// since that may trigger another menu rebuild that is not protected
// by the lock API.
if (function_exists('_menu_router_cache')) {
// Get the newly rebuilt menu.
$menu = _menu_router_cache();
}
else {
$menu = menu_router_build();
}
if (!$menu) {
// Something went wrong. Don't risk triggering another menu rebuild.
return;
}
// Add normal and suggested items as links.
$menu_links = array();
foreach ($menu as $path => $item) {
// Exclude menu callbacks, include items below admin/* and node/add/*.
if ($item['type'] != MENU_CALLBACK && (($item['_parts'][0] == 'admin' && count($item['_parts']) > 1) || (strpos($path, 'node/add') === 0))) {
// TODO: handle local tasks with wildcards
if (!strpos($path, '%')) {
$item = admin_menu_link_build($item);
$menu_links[$path] = $item;
$sort[$path] = $item['_number_parts'];
}
}
}
$deleted = admin_menu_adjust_items($menu_links, $sort);
if ($menu_links) {
// Make sure no child comes before its parent.
array_multisort($sort, SORT_NUMERIC, $menu_links);
foreach ($menu_links as $item) {
admin_menu_link_save($item);
}
}
// Allow modules to add more links. If you want to alter links saved by
// admin_menu, use hook_menu_link_alter() and look for
// $item['module'] == 'admin_menu'
$links = array();
foreach (module_implements('admin_menu') as $module) {
$function = $module . '_admin_menu';
$links = array_merge_recursive($links, $function($deleted));
}
foreach ($links as $item) {
admin_menu_link_save($item);
}
}
/**
* Prepare a menu link from basic information formatted for a router item.
*/
function admin_menu_link_build($item) {
$item['module'] = 'admin_menu';
$item['menu_name'] = 'admin_menu';
$item += array(
'link_title' => isset($item['title']) ? $item['title'] : '',
'link_path' => isset($item['path']) ? $item['path'] : '',
'hidden' => 0,
'options' => array(),
);
$item['options']['alter'] = TRUE;
// DAM does not output item descriptions to prevent mouseover clashes and
// increase page loading performance. However, the following code shows how
// link attributes can be added (for ajaxified DAM functionality later).
/*
if (!empty($item['description'])) {
$item['options']['attributes']['title'] = $item['description'];
}
*/
if (!empty($item['query'])) {
$item['options']['query'] = $item['query'];
}
return $item;
}
/**
* Convenience function that looks up the plid if $item['parent_path'] is set.
*/
function admin_menu_link_save($item) {
$item = admin_menu_link_build($item);
// Check whether we are able to update an existing item.
$existing_item = db_fetch_array(db_query("SELECT mlid, plid, has_children FROM {menu_links} WHERE link_path = '%s' AND menu_name = '%s'", $item['link_path'], 'admin_menu'));
if ($existing_item) {
$item['mlid'] = $existing_item['mlid'];
$item['plid'] = $existing_item['plid'];
$item['has_children'] = $existing_item['has_children'];
}
// Look up the parent path for both new and existing links, since the parent
// may change.
if (isset($item['parent_path'])) {
if ($item['parent_path'] == '<root>') {
// <root> means that we want the link at the top level.
$item['plid'] = 0;
}
else {
$plid = db_result(db_query("SELECT mlid from {menu_links} WHERE link_path = '%s' AND menu_name = '%s'", $item['parent_path'], 'admin_menu'));
if ($plid) {
$item['plid'] = $plid;
}
}
}
menu_link_save($item);
}
/**
* Implementation of hook_admin_menu().
*
* @param &$deleted
* Array of links under admin/* that were removed by admin_menu_adjust_items().
* If one of these links is added back, it should be removed from the array.
*/
function admin_menu_admin_menu(&$deleted) {
$links = array();
$icon_path = '<front>';
// Add link to manually run cron.
$links[] = array(
'title' => 'Run cron',
'path' => 'admin/reports/status/run-cron',
'weight' => 50,
'parent_path' => $icon_path,
);
// Add link to run update.php.
$links[] = array(
'title' => 'Run updates',
'path' => 'update.php',
'weight' => 50,
'parent_path' => $icon_path,
'options' => array('external' => TRUE),
);
// Move 'By module' item into Site configuration.
if (isset($deleted['admin/by-module'])) {
$deleted['admin/by-module']['parent_path'] = 'admin/settings';
$deleted['admin/by-module']['weight'] = -10;
$links[] = $deleted['admin/by-module'];
unset($deleted['admin/by-module']);
}
// Add link to drupal.org.
$links[] = array(
'title' => 'Drupal.org',
'path' => 'http://drupal.org',
'weight' => 100,
'parent_path' => $icon_path,
);
// Add links to project issue queues.
$links[] = array(
'title' => 'Drupal issue queue',
'path' => 'http://drupal.org/project/issues/drupal',
'weight' => -10,
'parent_path' => 'http://drupal.org',
);
$projects = array();
foreach (module_list(FALSE, FALSE, TRUE) as $module) {
$info = drupal_parse_info_file(drupal_get_path('module', $module) .'/'. $module .'.info');
if (!isset($info['project']) || (isset($info['project']) && ($info['project'] == 'drupal' || isset($projects[$info['project']])))) {
continue;
}
$projects[$info['project']] = 1;
$url = 'http://drupal.org/project/issues/'. $info['project'];
// Filtering project versions via query string is not yet supported.
// @see http://drupal.org/node/97569
// $url .= !empty($info['version']) ? '/'. $info['version'] : '';
$links[] = array(
'title' => check_plain($info['name']) . ' issue queue',
'path' => $url,
'parent_path' => 'http://drupal.org',
);
}
// Add 'Create <content-type>' items to Content management menu.
if (isset($deleted['node/add'])) {
$deleted['node/add']['parent_path'] = 'admin/content';
$deleted['node/add']['weight'] = 0;
$links[] = $deleted['node/add'];
unset($deleted['node/add']);
}
foreach($deleted as $path => $item) {
if (strpos($path, 'node/add') !== FALSE) {
$links[] = $deleted[$path];
unset($deleted[$path]);
}
}
// Make sure longer paths are after shorter ones
ksort($deleted);
foreach (node_get_types('types', NULL, TRUE) as $type) {
$type_url_str = str_replace('_', '-', $type->type);
$type_path = 'admin/content/node-type/' . $type_url_str;
$links[$type_path] = array(
'title' => 'Edit !content-type',
'path' => $type_path,
'parent_path' => 'admin/content/types',
'options' => array('t' => array('!content-type' => $type->name)),
);
unset($deleted[$type_path . '/edit']);
// CCK and other modules adding to node types handled here.
foreach($deleted as $path => $item) {
// Precise test needed to account for multiple content-types having the
// same prefix in their name.
if ($path === $type_path || strpos($path, $type_path . '/') === 0) {
// Logically, parent path can never go shorter than $type_path.
$i = $item['_number_parts'] - 1;
do {
$parent_path = implode('/', array_slice($item['_parts'], 0, $i));
--$i;
} while (!isset($links[$parent_path]) && $i);
$item['parent_path'] = $parent_path;
$links[$path] = $item;
unset($deleted[$path]);
}
}
}
// Add clear-cache.
$links[] = array(
'title' => 'Flush all caches',
'path' => 'admin_menu/flush-cache',
'query' => 'destination',
'weight' => 20,
'parent_path' => $icon_path,
);
$caches = array(
'admin_menu' => 'Administration menu',
'cache' => 'Cache tables',
'menu' => 'Menu',
'requisites' => 'Page requisites',
'theme' => 'Theme registry',
);
foreach ($caches as $name => $title) {
$links[] = array(
'title' => $title,
'path' => 'admin_menu/flush-cache/' . $name,
'query' => 'destination',
'parent_path' => 'admin_menu/flush-cache',
);
}
// Add devel module links
if (module_exists('devel')) {
// Add variable editor.
$links[] = array(
'title' => 'Variable editor',
'path' => 'devel/variable',
'weight' => 20,
'parent_path' => $icon_path,
);
// Add switch_user items.
if ($devel_user_links = module_invoke('devel', 'switch_user_list')) {
foreach ($devel_user_links as $link) {
if (is_array($link)) {
$links[] = array(
'title' => $link['title'],
'description' => $link['attributes']['title'],
'path' => $link['href'],
'options' => array(
'query' => $link['query'],
'html' => !empty($link['html']),
),
'parent_path' => 'logout',
);
}
// @todo Remove when Devel 6.x-1.16 has been released.
elseif (preg_match('!href="' . base_path() . '([^\?]+)\?([^"]+)" title="([^"]+)">((<em>)?[^<]+(</em>)?)!', $link, $match)) {
$links[] = array(
'title' => $match[4],
'description' => $match[3],
'path' => urldecode($match[1]),
'weight' => 20,
'query' => 'destination',
'parent_path' => 'logout',
'options' => array('html' => TRUE),
);
}
}
}
}
// Add developer modules toggle link.
$saved_state = variable_get('admin_menu_devel_modules_enabled', NULL);
$links[] = array(
'title' => isset($saved_state) ? t('Enable developer modules') : t('Disable developer modules'),
'path' => 'admin_menu/toggle-modules',
'weight' => 88,
'parent_path' => $icon_path,
'options' => array('query' => 'destination'),
);
return $links;
}
/**
* Add some hard-coded features for better user experience.
*
* @param array $menu_links
* An array containing the complete administration menu structure, passed by
* reference.
* @param array $sort
* An array containing the # parts of each link - must be updated if a link
* is added.
* @return
* An array of links that were removed from $menu_links.
*/
function admin_menu_adjust_items(&$menu_links, &$sort) {
global $user;
$links = array();
$deleted = array();
// Change or remove items, or add new top-level items.
$deleted['admin/by-module'] = $menu_links['admin/by-module'];
unset($menu_links['admin/by-module'], $sort['admin/by-module']);
$deleted['admin/by-task'] = $menu_links['admin/by-task'];
unset($menu_links['admin/by-task'], $sort['admin/by-task']);
// Remove certain links to re-position them in admin_menu_admin_menu().
foreach ($menu_links as $path => $link) {
// Remove links below
// - admin/content/node-type/*
// - node/add*
if (strpos($path, 'admin/content/node-type/') !== FALSE || strpos($path, 'node/add') !== FALSE) {
$deleted[$path] = $link;
unset($menu_links[$path], $sort[$path]);
}
}
// Add the icon containing special links.
$links[] = array(
'title' => theme('admin_menu_icon'),
'path' => '<front>',
'weight' => -100,
'parent_path' => '<root>',
'options' => array('extra class' => 'admin-menu-icon', 'html' => TRUE),
);
// Add link to show current authenticated/anonymous users - we will add the
// data dynamically in the _alter hook.
$links[] = array(
'title' => 'icon_users',
'path' => 'user',
'weight' => -90,
'parent_path' => '<root>',
'options' => array('extra class' => 'admin-menu-action admin-menu-icon admin-menu-users', 'html' => TRUE),
);
$links[] = array(
'title' => 'Log out @username',
'path' => 'logout',
'weight' => -100,
'parent_path' => '<root>',
// Note: @username is dynamically replaced by default, we just invoke
// replacement by setting the 't' key here.
'options' => array('extra class' => 'admin-menu-action admin-menu-logout', 't' => array()),
);
foreach ($links as $item) {
$path = $item['path'];
$item = admin_menu_link_build($item);
$menu_links[$path] = $item;
$sort[$path] = 1;
}
return $deleted;
}
/**
* Form builder function for module settings.
*/
function admin_menu_theme_settings() {
$form['admin_menu_margin_top'] = array(
'#type' => 'checkbox',
'#title' => t('Adjust top margin'),
'#default_value' => variable_get('admin_menu_margin_top', 1),
'#description' => t('If enabled, the site output is shifted down approximately 20 pixels from the top of the viewport to display the administration menu. If disabled, some absolute- or fixed-positioned page elements may be covered by the administration menu.'),
);
$form['admin_menu_position_fixed'] = array(
'#type' => 'checkbox',
'#title' => t('Keep menu at top of page'),
'#default_value' => variable_get('admin_menu_position_fixed', 0),
'#description' => t('If enabled, the administration menu is always displayed at the top of the browser viewport (even after the page is scrolled). <strong>Note: In some browsers, this setting results in a malformed page, an invisible cursor, non-selectable elements in forms, or other issues. Disable this option if these issues occur.</strong>'),
);
$form['tweaks'] = array(
'#type' => 'fieldset',
'#title' => t('Advanced settings'),
);
$form['tweaks']['admin_menu_tweak_modules'] = array(
'#type' => 'checkbox',
'#title' => t('Collapse fieldsets on modules page'),
'#default_value' => variable_get('admin_menu_tweak_modules', 0),
'#description' => t('If enabled, fieldsets on the <a href="!modules-url">modules</a> page are automatically collapsed when loading the page.', array('!modules-url' => url('admin/build/modules'))),
);
if (module_exists('util')) {
$form['tweaks']['admin_menu_tweak_modules']['#description'] .= '<br /><strong>'. t('If the Utility module was installed for this purpose, it can be safely disabled and uninstalled.') .'</strong>';
}
$form['tweaks']['admin_menu_tweak_tabs'] = array(
'#type' => 'checkbox',
'#title' => t('Move local tasks into menu'),
'#default_value' => variable_get('admin_menu_tweak_tabs', 0),
'#description' => t('If enabled, the tabs on the current page are moved into the administration menu. This feature is only available in themes that use the CSS classes <code>tabs primary</code> and <code>tabs secondary</code> for tabs.'),
);
$form = system_settings_form($form);
$form['wipe description'] = array(
'#type' => 'item',
'#value' => t('If the administration menu displays duplicate menu items, you may need to rebuild your menu items using the <em>Wipe and rebuild</em> button.'),
);
$form['wipe'] = array(
'#type' => 'submit',
'#value' => t('Wipe and rebuild'),
'#submit' => array('admin_menu_wipe'),
);
return $form;
}
/**
* Wipe the menu so it can be rebuilt from scratch.
*/
function admin_menu_wipe() {
db_query("DELETE FROM {menu_links} WHERE menu_name = 'admin_menu'");
menu_cache_clear('admin_menu');
menu_rebuild();
}
/**
* Helper function for admin_menu_form_devel_admin_settings_alter().
*
* Extends Devel module with Administration Menu developer settings.
*/
function _admin_menu_devel_settings_form_alter(&$form, $form_state) {
// Shift system_settings_form buttons.
$weight = isset($form['buttons']['#weight']) ? $form['buttons']['#weight'] : 0;
$form['buttons']['#weight'] = $weight + 1;
$form['admin_menu'] = array(
'#type' => 'fieldset',
'#title' => t('Administration menu settings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$display_options = array('mid', 'weight', 'pid');
$display_options = array(0 => t('None'), 'mlid' => t('Menu link ID'), 'weight' => t('Weight'), 'plid' => t('Parent link ID'));
$form['admin_menu']['admin_menu_display'] = array(
'#type' => 'radios',
'#title' => t('Display additional data for each menu item'),
'#default_value' => variable_get('admin_menu_display', 0),
'#options' => $display_options,
'#description' => t('Display the selected items next to each menu item link.'),
);
$form['admin_menu']['admin_menu_show_all'] = array(
'#type' => 'checkbox',
'#title' => t('Display all menu items'),
'#default_value' => variable_get('admin_menu_show_all', 0),
'#description' => t('If enabled, all menu items are displayed regardless of your site permissions. <em>Note: Do not enable on a production site.</em>'),
);
}
/**
* Menu callback to enable/disable developer modules.
*
* This saves up to 150ms on each uncached page request. Not much, but
* on larger Drupal sites this is actually a 10% performance increase.
*/
function admin_menu_toggle_modules() {
// URL token prtects this against CSRF attacks.
if(!isset($_GET['token']) || ($_GET['token'] !== drupal_get_token($_GET['q']))) {
return MENU_ACCESS_DENIED;
}
$rebuild = FALSE;
$saved_state = variable_get('admin_menu_devel_modules_enabled', NULL);
if (isset($saved_state)) {
// Re-enable modules that were enabled before.
module_enable($saved_state);
variable_del('admin_menu_devel_modules_enabled');
drupal_set_message(t('Enabled these modules: !module-list.', array('!module-list' => implode(', ', $saved_state))));
$rebuild = TRUE;
}
else {
// Allow site admins to override this variable via settings.php.
$devel_modules = variable_get('admin_menu_devel_modules', array('cache_disable', 'coder', 'content_copy', 'debug', 'delete_all', 'demo', 'devel', 'devel_node_access', 'devel_themer', 'macro', 'form_controller', 'imagecache_ui', 'journal', 'trace', 'upgrade_status', 'user_display_ui', 'util', 'views_ui', 'views_theme_wizard'));
// Store currently enabled modules in a variable.
$devel_modules = array_intersect(module_list(FALSE, FALSE), $devel_modules);
if (!empty($devel_modules)) {
variable_set('admin_menu_devel_modules_enabled', $devel_modules);
// Disable developer modules.
module_disable($devel_modules);
drupal_set_message(t('Disabled these modules: !module-list.', array('!module-list' => implode(', ', $devel_modules))));
$rebuild = TRUE;
}
else {
drupal_set_message(t('No developer modules are enabled.'));
}
}
if ($rebuild) {
// Make sure everything is rebuilt, basically a combination of the calls
// from system_modules() and system_modules_submit().
drupal_rebuild_theme_registry();
menu_rebuild();
cache_clear_all('schema', 'cache');
cache_clear_all();
drupal_clear_css_cache();
drupal_clear_js_cache();
// Synchronize to catch any actions that were added or removed.
actions_synchronize();
}
drupal_goto('');
}
/**
* Flush all caches or a specific one.
*
* @param $name
* (optional) Name of cache to flush.
*/
function admin_menu_flush_cache($name = NULL) {
// URL token protects this against CSRF attacks.
if(!isset($_GET['token']) || ($_GET['token'] !== drupal_get_token($_GET['q']))) {
return MENU_ACCESS_DENIED;
}
switch ($name) {
case 'admin_menu':
admin_menu_wipe();
break;
case 'cache':
// Don't clear cache_form - in-progress form submissions may break.
// Ordered so clearing the page cache will always be the last action.
$core = array('cache', 'cache_block', 'cache_filter', 'cache_page');
$cache_tables = array_merge(module_invoke_all('flush_caches'), $core);
foreach ($cache_tables as $table) {
cache_clear_all('*', $table, TRUE);
}
break;
case 'menu':
menu_rebuild();
break;
case 'requisites':
// Change query-strings on css/js files to enforce reload for all users.
_drupal_flush_css_js();
drupal_clear_css_cache();
drupal_clear_js_cache();
break;
case 'theme':
module_invoke('system', 'theme_data');
drupal_rebuild_theme_registry();
break;
default:
// Flush all caches; no need to re-implement this.
module_load_include('inc', 'system', 'system.admin');
$form = $form_state = array();
system_clear_cache_submit($form, $form_state);
break;
}
drupal_goto();
}