566 lines
20 KiB
PHP
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();
|
|
}
|
|
|