...
 
Commits (137)
Menu Block 7.x-2.x-dev, xxxx-xx-xx (development release)
----------------------------------
-
Menu Block 7.x-2.1, 2011-02-06
------------------------------
- #1051988: Fix wrong path in menu_block_help()
- #1050766: Improve usability of "Parent item" UI
- #1050040 by AgentRickard: Remove performance-killing variable_set() calls on non-admin pages
Menu Block 7.x-2.0, 2011-01-11
------------------------------
- #1022478: Add "Edit book outline" contextual link for book menu blocks
- #1022428: Contextual links for all menu blocks disappear if book menu_block used
- #1017142: except for one menu, all other menus (including books) never receive an active trail
- #1017122 by becw, mfer and JohnAlbin: Core bug work-around: add active trail to custom menus
- Always show core menu blocks if they are in a region
- #993998 by tgf: Core menu suppression broken
- #993998 by jackinloadup: Delete links misplaced on menu list form
- #958166 by Simon Georges and JohnAlbin: secondary-menu removed from core; replace with user-menu
- #825132: Performance problem on sites with many books
- #945714 by pedrochristopher: theme override misidentified in README
- #593126: hook_get_menus() causes conflicts with Menu Access, og_menu, etc
- #825132: Add hook_menu_block_get_sort_menus() for improved performance and UX of book integration
- #957362 by blixxxa: Add Swedish translation
- Fix theme hook suggestions for non-numeric block deltas
Menu Block 7.x-2.0-beta4, 2010-09-29
------------------------------------
- #891690: Never accept a fix not in patch form. And if you do, test it!
Menu Block 7.x-2.0-beta3, 2010-09-29
------------------------------------
- #891698 by Chris Gillis: Incorrect link to configuration page
- #891690 by Chris Gillis: Undefined function db_fetch_array
- Updated menu_block_get_title() to return a renderable array
- Add #bid context to menu_link theme hook
Menu Block 7.x-2.0-beta2, 2010-04-16
------------------------------------
- Add ability to suppress core's standard menu blocks on block admin page
- #693302: Add simple bulk export module
- Fixed import of exportable menu_blocks
- Fixed bug causing missing "delete" link for menu blocks
- Fixed configure link on modules page
Menu Block 7.x-2.0-beta1, 2010-03-26
------------------------------------
- #693302: Add API for exportable menu_blocks
- #749838: Port to Drupal 7
Menu Block 6.x-2.3, 2010-03-24
------------------------------
- #739282: Users with "administer menu" privileges can exploit XSS vulnerability
- #343061 by sun and JohnAlbin: CSS styling breaks form layout
- #345552 by Dmitriy.trt: Inconsistent display of starting level set to children
of active item
- #474784: Menu title as link is incorrectly always marked as in active trail
- #540842 by JohnAlbin and agentrickard: Add option to use current page's
selected menu
- #580348: Add administrative title to config form to help organize blocks
- #350029: Add theme hook suggestions for all theme function calls
- #741284 by JohnAlbin, sdboyer, and hefox: Add "menu tree" content types to
Chaos Tools/Panels.
- #741284 by JohnAlbin and hefox: Add menu_block_get_config() and
menu_block_configure_form() to make the configuration form reusable by
separating it from the Block API functions.
- #553842 by apodran and JohnAlbin: split() is deprecated in PHP 5.3
- #398888: "exanded" elements of a menu-item-rooted tree aren't expanded
- #703968 by hefox and JohnAlbin: Add menu_tree_build() to allow reuse of tree
building code outside of blocks
- Refactored API for menu_block_get_title() and menu_block_set_title()
- Added HOOK_menu_block_tree_alter() to allow direct tree manipulation
- by Denes Szabo and Zoltan Balogh: Added Hungarian translation
- Make default menu be "Primary links" instead of "Navigation"
- #378206 by sbordage: Added French translation
- #345419: Add option for menu title as link
- #347805: Add delta to variables and classes in menu-block-wrapper.tpl
Menu Block 6.x-2.2, 2008-12-16
------------------------------
- #342498: Give unique class to depth-limited leaves that have children
- #341436: Depth-limited leaves that have children get "collapsed" icon
- #341345: WSOD on "Add menu block" page with some PHP versions
Menu Block 6.x-2.1, 2008-12-01
------------------------------
- #300086: Add option to make starting level follow active menu item
- #340868: Clean up display of block configuration options
- #300094: Add option to sort active trail to top of menu tree
- #328238 by gorbeia: Add support for i18n menu translation
- #331934: Add option to select parent item of menu tree
- #330978: Add hook_get_menus() and implement Book module integration
- #331935: Replace admin/by-module hack with README.txt
- #332974: Collapsed menu items get "leaf" class when using "depth" option
- #333988: Create template for menu-block-wrapper
- #331933 by sun: Help links are displayed if Help module is disabled
- #338263: Migration from 5.x-1.x to 6.x-2.x is broken
- #305267: Menu blocks incorrectly cached per role
Menu Block 6.x-2.0, 2008-08-25
------------------------------
- Added extensive documentation help text
- Added extended classes to menu trees
- Menu block's administrative interface now matches the block module's standard
add, configure, and delete interface
- #266230: Port Menu block to D6
Menu Block 5.x-2.0, 2008-11-24
------------------------------
- #304675: Port 6.x admin interface to 5.x
- Added missing dependency on menu module
Menu Block 5.x-1.0, 2008-08-05
------------------------------
- Simplified block configuration
Menu Block 5.x-0.9, 2008-08-05
------------------------------
- #266223: Add option to limit the depth of the tree to an arbitrary level
- Added block config to specify whether to expand all children
- Added settings to enable specific menu blocks
ADDING MENU BLOCKS
------------------
To add new menu blocks, use the "Add menu block" link on the administer blocks
page, admin/structure/block. You will then be able to configure your menu block
before adding it.
CONFIGURING MENU BLOCKS
-----------------------
When adding or configuring a menu block, several configuration options are
available:
Basic Options:
Block title
For menu trees that start with the 1st level, the default block title will be
the menu name. For menu trees that start with the 2nd level or deeper, the
default block title will be the title for the parent menu item of the
specified level.
For example, if the active menu trail for the Management menu is: Administer >
Structure > Menus > Main menu, then a menu block configured to start with the
1st level of the Management menu will display a block title of "Management".
And a menu block configured to start with the 3rd level of the Management menu
will display a block title of "Structure".
Block title as link
For menu trees that start with the 2nd level or deeper, the default block
title will be the title for the parent menu item of the specified level. If
this option is checked, the block title will be a link to that menu item.
Administrative title
To help identify the block on the administer blocks page, you can specify a
unique title to be used on that page. If blank, the regular title will be
used.
Menu name
Select the menu to use for the tree of links.
Starting level
Blocks that start with the 1st level will always be visible. Blocks that start
with the 2nd level or deeper will only be visible when the trail to the active
menu item is in the block's tree.
Maximum depth
From the starting level, specify the maximum depth of the tree. Blocks with a
maximum depth of 1 will just be a single un-nested list of links with none of
those links' children displayed.
Advanced options:
Make the starting level follow the active menu item
If the active menu item is deeper than the level specified above, the starting
level will follow the active menu item. Otherwise, the starting level of the
tree will remain fixed.
Expand
All children of this menu will be expanded.
Sort
Sort each item in the active trail to the top of its level. When used on a
deep or wide menu tree, the active menu item's children will be easier to see
when the page is reloaded.
Fixed parent item
If you select a specific menu item, you alter the "starting level" and
"maximum depth" options to be relative to the fixed parent item. The tree of
links will only contain children of the selected parent item.
STYLING MENU BLOCKS
-------------------
Classes:
Themers should look at the myriad of classes added to the <div>, <li> and <a>
elements.
<div>
The <div> wrapped around the menu tree has a class for several of the
configurable options of the block: menu-block-[block id number]
menu-name-[menu name] parent-mlid-[menu link ID] menu-level-[level number]
<li>
The <li> elements of the menu tree can have an extended list of classes
(compared to standard menu trees): first last menu-mlid-[menu link ID]
has-children active active-trail
<a>
The <a> elements of the menu tree can have: active active-trail
Templates:
In addition, the wrapper <div> for the block is generated using the
menu-block-wrapper.tpl.php template. And Menu block provides several theme hook
suggestions for that template:
- menu-block-wrapper--[block id number].tpl.php
- menu-block-wrapper--[menu name].tpl.php
For example, a file in your theme called
menu-block-wrapper--main-menu.tpl.php can be used to override the <div> for
just the "Primary links" menu blocks.
Theme functions:
Menu block uses Drupal core's menu theme functions. However, it also provides
theme hook suggestions that can be used to override any of the theme functions
called by it.
- theme_menu_tree() can be overridden by creating one of:
- [theme]_menu_tree__[menu name]()
- [theme]_menu_tree__menu_block()
- [theme]_menu_tree__menu_block__[menu name]()
- [theme]_menu_tree__menu_block__[block id number]()
- theme_menu_link() can be overridden by creating one of:
- [theme]_menu_link__[menu name]()
- [theme]_menu_link__menu_block()
- [theme]_menu_link__menu_block__[menu name]()
- [theme]_menu_link__menu_block__[block id number]()
For example, if you created a bartik_menu_tree__menu_block() function, it would
override theme_menu_tree() any time it was used by this module, but not when
used by any other module. Similarly, a bartik_menu_link__menu_block__1()
function would override theme_menu_link(), but only for the first menu block in
your system (the menu block with an ID of 1).
MENU BLOCK API
--------------
Developers can use the API of this module to create their own menu trees outside
the confines of blocks. All of the publicly available API functions are
documented in the menu_block.module file.
In addition, Menu block implements HOOK_menu_block_get_menus(),
HOOK_menu_block_get_sort_menus() and HOOK_menu_block_tree_alter(). See
menu_block.api.php for documentation.
block.settings.menu_block:*:
type: block_settings
label: 'Menu block'
mapping:
level:
type: integer
label: 'Starting level'
depth:
type: integer
label: 'Maximum number of levels'
expanded:
type: boolean
label: 'Expand all menu links'
parent:
type: string
label: 'Parent menu link'
<?php
/**
* @file
* Add custom menus to the active trail; required to fix Drupal 7.0 core.
*
* This file WILL be removed after this core bug is fixed: #942782. You have
* been warned!
*/
/**
* Implements hook_menu_insert().
*/
function menu_block_menu_insert($menu) {
_menu_block_add($menu);
}
/**
* Implements hook_menu_update().
*/
function menu_block_menu_update($menu) {
_menu_block_add($menu);
}
/**
* Implements hook_menu_delete().
*/
function menu_block_menu_delete($menu) {
$active_menus = variable_get('menu_default_active_menus', array_keys(menu_list_system_menus()));
if (in_array($menu['menu_name'], $active_menus)) {
$key = array_search($menu['menu_name'], $active_menus);
unset($active_menus[$key]);
variable_set('menu_default_active_menus', $active_menus);
}
}
/**
* Add a custom menu to the active trail.
*
* @param array $menu
* A menu item to add to the active trail.
*/
function _menu_block_add($menu) {
// Make sure the menu is present in the active menus variable so that its
// items may appear in the menu active trail.
// @see menu_set_active_menu_names()
$active_menus = variable_get('menu_default_active_menus', array_keys(menu_list_system_menus()));
if (!in_array($menu['menu_name'], $active_menus)) {
$active_menus[] = $menu['menu_name'];
variable_set('menu_default_active_menus', $active_menus);
}
}
/**
* Muck with the static cache of menu_link_get_preferred.
*
* In ensure all menus get an active trail, pre-set the static cache of
* menu_link_get_preferred() for the specific menu we want to render.
*/
function __menu_block_set_menu_trail($menu_name = FALSE) {
// If there's no menu item, no active trail hack is needed.
if (($item = menu_get_item()) && $item == FALSE) {
return;
}
$preferred_links = &drupal_static('menu_link_get_preferred');
$originals = &drupal_static(__FUNCTION__, array());
$path = $_GET['q'];
if ($menu_name) {
// Alter the preferred link so it always checks the requested menu.
$originals['preferred_link'] = isset($preferred_links[$path]) ? $preferred_links[$path] : NULL;
$preferred_links[$path] = _menu_link_get_preferred($path, $menu_name);
// Ensure the active menu always includes the requested menu.
$originals['active_menus'] = variable_get('menu_default_active_menus', NULL);
$GLOBALS['conf']['menu_default_active_menus'] = array($menu_name);
}
else {
// Reset the original preferred_link static variables.
if (is_null($originals['preferred_link'])) {
unset($preferred_links[$path]);
}
else {
$preferred_links[$path] = $originals['preferred_link'];
}
// Reset the original menu_default_active_menus variable.
$GLOBALS['conf']['menu_default_active_menus'] = $originals['active_menus'];
}
}
/**
* Lookup the preferred menu link for a given system path.
*
* @param $path
* The path, for example 'node/5'. The function will find the corresponding
* menu link ('node/5' if it exists, or fallback to 'node/%').
* @param $menu_name
* The name of a menu used to restrict the search for a prefered menu link.
* If not specified, all the menus returned by menu_get_active_menu_names()
* will be used.
*
* @return
* A fully translated menu link, or NULL if not matching menu link was
* found. The most specific menu link ('node/5' preferred over 'node/%') in
* the most preferred menu (as defined by menu_get_active_menu_names()) is
* returned.
*/
function _menu_link_get_preferred($path = NULL, $menu_name = '') {
$preferred_links = &drupal_static(__FUNCTION__);
if (!isset($path)) {
$path = $_GET['q'];
}
if (!isset($preferred_links[$path][$menu_name])) {
$preferred_links[$path][$menu_name] = FALSE;
// Look for the correct menu link by building a list of candidate paths,
// which are ordered by priority (translated hrefs are preferred over
// untranslated paths). Afterwards, the most relevant path is picked from
// the menus, ordered by menu preference.
$item = menu_get_item($path);
$path_candidates = array();
// 1. The current item href.
$path_candidates[$item['href']] = $item['href'];
// 2. The tab root href of the current item (if any).
if ($item['tab_parent'] && ($tab_root = menu_get_item($item['tab_root_href']))) {
$path_candidates[$tab_root['href']] = $tab_root['href'];
}
// 3. The current item path (with wildcards).
$path_candidates[$item['path']] = $item['path'];
// 4. The tab root path of the current item (if any).
if (!empty($tab_root)) {
$path_candidates[$tab_root['path']] = $tab_root['path'];
}
// Retrieve a list of menu names, ordered by preference.
if ($menu_name !== '') {
$menu_names = array($menu_name);
}
else {
$menu_names = menu_get_active_menu_names();
}
$query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
$query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
$query->fields('ml');
// Weight must be taken from {menu_links}, not {menu_router}.
$query->addField('ml', 'weight', 'link_weight');
$query->fields('m');
$query->condition('ml.link_path', $path_candidates, 'IN');
// Sort candidates by link path and menu name.
$candidates = array();
foreach ($query->execute() as $candidate) {
$candidate['weight'] = $candidate['link_weight'];
$candidates[$candidate['link_path']][$candidate['menu_name']] = $candidate;
}
// Pick the most specific link, in the most preferred menu.
$preferred_link = FALSE;
foreach ($path_candidates as $link_path) {
if (isset($candidates[$link_path])) {
foreach ($menu_names as $menu) {
if (!$preferred_links[$path][$menu] && isset($candidates[$link_path][$menu])) {
$candidate_item = $candidates[$link_path][$menu];
$map = explode('/', $path);
_menu_translate($candidate_item, $map);
if ($candidate_item['access']) {
$preferred_links[$path][$menu] = $candidate_item;
if (!$preferred_link) {
$preferred_link = $candidate_item;
}
}
}
}
}
}
if ($menu_name === '' && $preferred_link) {
$preferred_links[$path][$menu_name] = $preferred_link;
}
}
return $preferred_links[$path][$menu_name];
}
.menu-block-parent-wrapper {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
label#item-label {
font-weight: normal;
}
/* Proper buttonset styling is missing from Seven */
.menu-block-processed {
padding: 0 15px 15px 15px;
border: 1px solid #ccc;
}
.form-type-radios.form-item-display-options {
display: none;
margin: 0 -15px;
padding: 5px 15px;
text-align: left;
background-color: #eee;
}
.form-type-radio.form-item-display-options {
float: left;
}
.form-item.form-type-radios.form-item-display-options label,
.form-item.form-type-radio.form-item-display-options label {
font-size: 12px;
line-height: 18px;
font-weight: bold;
}
.form-item-display-options .ui-buttonset .form-item {
padding: 0; /* Seven adds padding */
}
.form-item-display-options .ui-buttonset label.ui-button {
display: inline-block;
padding: 2px 10px;
border: 1px solid #666;
color: #666;
font-weight: bold;
background-image: url(menu-block-background-display-options.png);
background-position: left top;
background-repeat: no-repeat;
}
.form-item-display-options .ui-buttonset label.ui-state-active {
color: #fff;
background-position: left -50px;
}
.form-item-display-options .ui-buttonset label.ui-button.ui-corner-right {
border-left: none;
}
.form-item-display-options .ui-button .ui-button-text {
display: inline;
padding: 0;
}
.ui-buttonset .ui-corner-left {
-moz-border-radius-topleft: 5px
-moz-border-radius-bottomleft: 5px;
-webkit-border-bottom-left-radius: 5px;
-webkit-border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
border-top-left-radius: 5px;
}
.ui-buttonset .ui-corner-right {
-moz-border-radius-topright: 5px
-moz-border-radius-bottomright: 5px;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
border-top-right-radius: 5px;
}
<?php
/**
* @file
* Default theme implementation to wrap menu blocks.
*
* Available variables:
* - $content: The renderable array containing the menu.
* - $classes: A string containing the CSS classes for the DIV tag. Includes:
* menu-block-DELTA, menu-name-NAME, parent-mlid-MLID, and menu-level-LEVEL.
* - $classes_array: An array containing each of the CSS classes.
*
* The following variables are provided for contextual information.
* - $delta: (string) The menu_block's block delta.
* - $config: An array of the block's configuration settings. Includes
* menu_name, parent_mlid, title_link, admin_title, level, follow, depth,
* expanded, and sort.
*
* @see template_preprocess_menu_block_wrapper()
*/
?>
<div class="<?php print $classes; ?>">
<?php print render($content); ?>
</div>
(function ($) {
Drupal.behaviors.menu_block = {
attach: function (context, settings) {
// This behavior attaches by ID, so is only valid once on a page.
if ($('#menu-block-settings.menu-block-processed').size()) {
return;
}
$('#menu-block-settings', context).addClass('menu-block-processed');
// Show the "display options" if javascript is on.
$('.form-item-display-options.form-type-radios>label', context).addClass('element-invisible');
$('.form-item-display-options.form-type-radios', context).show();
// Make the radio set into a jQuery UI buttonset.
$('#edit-display-options', context).buttonset();
// Override the default show/hide animation for Form API states.
$('#menu-block-settings', context).bind('state:visible', function(e) {
if (e.trigger) {
e.stopPropagation() /* Stop the handler further up the tree. */
$(e.target).closest('.form-item, .form-wrapper')[e.value ? 'slideDown' : 'slideUp']('fast');
}
});
// Syncronize the display of menu and parent item selects.
$('.menu-block-parent-mlid', context).change( function() {
var menuItem = $(this).val().split(':');
$('.menu-block-menu-name').val(menuItem[0]);
});
$('.menu-block-menu-name', context).change( function() {
$('.menu-block-parent-mlid').val($(this).val() + ':0');
});
}
};
})(jQuery);
This diff is collapsed.
<?php
/**
* @file
* Hooks provided by the Menu Block module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Alter the menu tree and its configuration before the tree is rendered.
*
* @param $tree
* An array containing the unrendered menu tree.
* @param $config
* An array containing the configuration of the tree.
*/
function hook_menu_block_tree_alter(&$tree, &$config) {
}
/**
* Return a list of configurations for menu blocks.
*
* Modules that want to have menu block configurations exported to code should
* provide them using this hook.
*
* @see menu_tree_build() for a description of the config array.
*/
function hook_menu_block_blocks() {
return array(
// The array key is the block id used by menu block.
'custom-nav' => array(
// Use the array keys/values described in menu_tree_build().
'menu_name' => 'primary-links',
'parent_mlid' => 0,
'title_link' => FALSE,
'admin_title' => 'Drop-down navigation',
'level' => 1,
'follow' => 0,
'depth' => 2,
'expanded' => TRUE,
'sort' => FALSE,
),
// To prevent clobbering of the block id, it is recommended to prefix it
// with the module name.
'custom-active' => array(
'menu_name' => MENU_TREE__CURRENT_PAGE_MENU,
'title_link' => TRUE,
'admin_title' => 'Secondary navigation',
'level' => 3,
'depth' => 3,
// Any config options not specified will get the default value.
),
);
}
/**
* Return a list of menus to use with the menu_block module.
*
* @return
* An array containing the menus' machine names as keys with their menu titles
* as values.
*/
function hook_menu_block_get_menus() {
$menus = array();
// For each menu, add the following information:
$menus['menu_name'] = 'menu title';
return $menus;
}
/**
* Return a list of menus to use on menu block's settings form.
*
* Menu block's settings page sorts menus for use with its "the menu selected by
* the page" option.
*
* @return
* An array containing the menus' machine names as keys with their menu titles
* as values. The key may optionally be a regular expression to match several
* menus at a time; see book_menu_block_get_sort_menus() for an example.
*/
function hook_menu_block_get_sort_menus() {
$menus = array();
// For each menu, add the following information:
$menus['menu_name'] = 'menu title';
// Optionally, add a regular expression to match several menus at once.
$menus['/^my\-menus\-.+/'] = t('My menus');
return $menus;
}
/**
* @} End of "addtogroup hooks".
*/
<?php
/**
* @file
* Provides book integration.
*/
/**
* Implements hook_menu_block_get_menus().
*/
function book_menu_block_get_menus() {
$menus = array();
foreach (book_get_books() AS $book) {
$menus[$book['menu_name']] = $book['title'];
}
return $menus;
}
/**
* Implements hook_menu_block_get_sort_menus().
*/
function book_menu_block_get_sort_menus() {
return array(
'/^book\-toc\-.+/' => t('Book navigation'),
);
}
<?php
/**
* @file
* Provides active menu item pruning.
*/
/**
* Prune a tree so that it begins at the active menu item.
*
* @param $tree
* array The menu tree to prune.
* @param $level
* string The level which the tree will be pruned to: 'active' or 'child'.
* @return
* void
*/
function _menu_tree_prune_active_tree(&$tree, $level) {
do {
$found_active_trail = FALSE;
// Examine each element at this level for the active trail.
foreach ($tree AS $key => &$value) {
if ($tree[$key]['link']['in_active_trail']) {
$found_active_trail = TRUE;
// If the active trail item has children, examine them.
if ($tree[$key]['below']) {
// If we are pruning to the active menu item's level, check if this
// is the active menu item by checking its children.
if ($level == 'active') {
foreach ($tree[$key]['below'] AS $child_key => &$value) {
if ($tree[$key]['below'][$child_key]['link']['in_active_trail']) {
// Get the title for the pruned tree.
menu_block_set_title($tree[$key]['link']);
$tree = $tree[$key]['below'];
// Continue in the pruned tree.
break 2;
}
}
// If we've found the active item, we're done.
break 2;
}
// Set the title for the pruned tree.
menu_block_set_title($tree[$key]['link']);
// If we are pruning to the children of the active menu item, just
// prune the tree to the children of the item in the active trail.
$tree = $tree[$key]['below'];
// Continue in the pruned tree.
break;
}
// If the active menu item has no children, we're done.
else {
if ($level == 'child') {
$tree = array();
}
break 2;
}
}
}
} while ($found_active_trail);
}
name = "Menu Block"
description = "Provides configurable blocks of menu items."
core = 7.x
dependencies[] = menu
files[] = menu_block.module
files[] = menu_block.admin.inc
files[] = menu_block.book.inc
files[] = menu_block.follow.inc
files[] = menu_block.pages.inc
files[] = menu_block.sort.inc
files[] = menu_block.install
# files[] = plugins/content_types/menu_tree/menu_tree.inc
configure = admin/config/user-interface/menu-block
name: Menu Block
description: Provides configurable blocks of menu links.
core: 8.x
type: module
dependencies:
- menu_ui
<?php
/**
* @file
* Provides install, upgrade and un-install functions for menu_block.
*/
/**
* Implements hook_uninstall().
*/
function menu_block_uninstall() {
// Delete menu block variables.
foreach (variable_get('menu_block_ids', array()) AS $delta) {
variable_del("menu_block_{$delta}_title_link");
variable_del("menu_block_{$delta}_admin_title");
variable_del("menu_block_{$delta}_parent");
variable_del("menu_block_{$delta}_level");
variable_del("menu_block_{$delta}_follow");
variable_del("menu_block_{$delta}_depth");
variable_del("menu_block_{$delta}_expanded");
variable_del("menu_block_{$delta}_sort");
}
variable_del('menu_block_ids');
variable_del('menu_block_suppress_core');
variable_del('menu_block_menu_order');
// Remove block configurations.
db_delete('block')
->condition('module', 'menu_block')
->execute();
db_delete('block_role')
->condition('module', 'menu_block')
->execute();
cache_clear_all();
}
/**
* Implements hook_enable().
*/
function menu_block_enable() {
drupal_set_message(t('To use menu blocks, find the "Add menu block" link on the <a href="@url">administer blocks page</a>.', array('@url' => url('admin/structure/block'))));
}
/**
* Implements hook_install().
*/
function menu_block_install() {
// No-op.
}
/**
* Converts pre-5.x-1.0 block names to the new format.
*/
function menu_block_update_5100() {
$delta = 0;
$enabled_blocks = array();
// Find the old enabled blocks.
foreach (variable_get('menu_block_enabled_blocks', array()) AS $old_delta => $enabled) {
list($mid, $level) = explode('-', $old_delta);
if ($enabled) {
$enabled_blocks[++$delta] = TRUE;
variable_set("menu_block_{$delta}_mid", $mid);
variable_set("menu_block_{$delta}_level", $level);
variable_set("menu_block_{$delta}_depth", variable_get("menu_block_depth_{$mid}_{$level}", 0));
variable_set("menu_block_{$delta}_expanded", variable_get("menu_block_expanded_{$mid}_{$level}", 0));
}
// Remove any of the old-style variables.
variable_del("menu_block_depth_{$mid}_{$level}");
variable_del("menu_block_expanded_{$mid}_{$level}");
}
variable_set('menu_block_enabled_blocks', $enabled_blocks);
return t('A pre-release version of Menu block has been detected. All menu blocks from the pre-release version have been given a new delta and are no longer placed in any block regions; their block placement should be re-configured immediately.');
}
/**
* Converts menu block-tracking data to new format.
*/
function menu_block_update_5200() {
$block_ids = array();
foreach (variable_get('menu_block_enabled_blocks', array()) AS $delta => $enabled) {
if ($enabled) {
$block_ids[] = $delta; // Build new $menu_block_ids.
// Convert $menu_block_DELTA_mid to $menu_block_DELTA_menu_name.
$mid = variable_get("menu_block_{$delta}_mid", 1);
variable_set("menu_block_{$delta}_menu_name", $mid);
// If we weren't upgraded to 5.x-2.x before the Drupal 6 upgrade, the
// mid-to-menu_name conversion is not possible.
variable_set("menu_block_{$delta}_title", $mid == 2 ? 'primary-links' : 'navigation');
variable_del("menu_block_{$delta}_mid");
}
else {
// Delete un-enabled menu block.
variable_del("menu_block_{$delta}_mid");
variable_del("menu_block_{$delta}_level");
variable_del("menu_block_{$delta}_depth");
variable_del("menu_block_{$delta}_expanded");
db_delete('block')
->condition('module', 'menu_block')
->condition('delta', $delta)
->execute();
db_delete('block_role')
->condition('module', 'menu_block')
->condition('delta', $delta)
->execute();
}
}
// Finish conversion of $menu_block_enabled_blocks to $menu_block_ids.
sort($block_ids);
variable_set('menu_block_ids', $block_ids);
variable_del('menu_block_enabled_blocks');
cache_clear_all();
return t('A 5.x-1.x version of Menu block has been detected and an attempt was made to upgrade it. Unfortunately, you should have upgraded to Menu block 5.x-2.x before your upgrade to Drupal 7. You may need to re-configure all your menu blocks.');
}
/**
* Converts to Drupal 6 menu names.
*/
function menu_block_update_6200() {
$menus = menu_get_menus();
foreach (variable_get('menu_block_ids', array()) AS $delta) {
// Drupal 6 uses the menu title to create the new menu_name.
$menu_name = preg_replace('/[^a-zA-Z0-9]/', '-', strtolower(variable_get("menu_block_{$delta}_title", '')));
// If we can't find the new menu_name, default to the navigation menu.
if (empty($menus[$menu_name])) {
$menu_name = 'navigation';
}
variable_set("menu_block_{$delta}_menu_name", $menu_name);
variable_del("menu_block_{$delta}_title");
}
return t('The 5.x-2.x version of Menu block has been upgraded to the 6.x-2.0 data storage format.');
}
/**
* Converts the menu names to parent items.
*/
function menu_block_update_6201() {
$menus = menu_get_menus();
foreach (variable_get('menu_block_ids', array()) AS $delta) {
variable_set("menu_block_{$delta}_parent", variable_get("menu_block_{$delta}_menu_name", 'navigation') . ':0');
variable_del("menu_block_{$delta}_menu_name");
}
return t('The 6.x-2.0 version of Menu block has been upgraded to the 6.x-2.1 data storage format.');
}
/**
* Converts to Drupal 7 menu names.
*/
function menu_block_update_7200() {
foreach (variable_get('menu_block_ids', array()) AS $delta) {
$menu_name = '';
list($old_menu_name, $parent_mlid) = explode(':', variable_get("menu_block_{$delta}_parent", ' : '));
switch ($old_menu_name) {
case 'primary-links':
$menu_name = 'main-menu';
break;
case 'secondary-links':
$menu_name = 'secondary-menu';
break;
case 'navigation':
$menu_name = 'management';
break;
}
if ($menu_name) {
variable_set("menu_block_{$delta}_parent", $menu_name . ':' . $parent_mlid);
}
}
return t('The 6.x-2.x version of Menu block has been upgraded to use the new menu names in Drupal 7. To use menu blocks in Drupal 7, find the "Add menu block" link on the <a href="@url">administer blocks page</a>.', array('@url' => url('admin/structure/block')));
}
/**
* Add missing custom menus to active menus list.
*/
function menu_block_update_7201() {
// Make sure all custom menus are present in the active menus variable so that
// their items may appear in the active trail.
// @see menu_set_active_menu_names()
$active_menus = variable_get('menu_default_active_menus', array_keys(menu_list_system_menus()));
$update_variable = FALSE;
foreach (menu_get_names() as $menu_name) {
if (!in_array($menu_name, $active_menus) && (strpos($menu_name, 'menu-') === 0)) {
$active_menus[] = $menu_name;
$update_variable = TRUE;
}
}
if ($update_variable) {
variable_set('menu_default_active_menus', $active_menus);
}
// Clear the menu cache.
cache_clear_all(NULL, 'cache_menu');
}
This diff is collapsed.
<?php
/**
* @file
* Provides infrequently used pages for menu_block.
*/
/**
* Implements hook_help().
*/
function _menu_block_help($path, $arg) {
$output = '';
switch ($path) {
case 'admin/structure/block/manage/%/%':
case 'admin/structure/block/add-menu-block':
if (module_exists('help')) {
$output = '<p>' . t('To learn more about configuring menu blocks, see <a href="!url">menu block’s detailed help</a>.', array('!url' => url('admin/help/menu_block'))) . '</p>';
}
break;
case 'admin/help#menu_block':
$output =
'<h3>' . t('Adding menu blocks') . '</h3>'
. '<p>' . t('To add new menu blocks, use the "<a href="!add-url">Add menu block</a>" link on the <a href="!block-url">administer blocks page</a>. You will then be able to configure your menu block before adding it.', array('!add-url' => url('admin/structure/block/add-menu-block'), '!block-url' => url('admin/structure/block'))) . '</p>'
. '<h3>' . t('Configuring menu blocks') . '</h3>'
. '<p>' . t('When adding or configuring a menu block, several configuration options are available:') . '</p>'
. '<h4>' . t('Basic options') . '</h4>'
. '<dl>'
. '<dt><strong>' . t('Block title') . '</strong></dt>'
. '<dd>' . t('For menu trees that start with the 1st level, the default block title will be the menu name. For menu trees that start with the 2nd level or deeper, the default block title will be the title for the parent menu item of the specified level.')
. '<p>' . t('For example, if the active menu trail for the Mangement menu is: Administer › Structure › Menus › Main menu, then a menu block configured to start with the 1st level of the Management menu will display a block title of “Management”. And a menu block configured to start with the 3rd level of the Management menu will display a block title of “Structure”.') . '</p></dd>'
. '<dt><strong>' . t('Block title as link') . '</strong></dt>'
. '<dd>' . t('For menu trees that start with the 2nd level or deeper, the default block title will be the title for the parent menu item of the specified level. If this option is checked, the block title will be a link to that menu item.') . '</dd>'
. '<dt><strong>' . t('Administrative title') . '</strong></dt>'
. '<dd>' . t('To help identify the block on the administer blocks page, you can specify a unique title to be used on that page. If blank, the regular title will be used.') . '</dd>'
. '<dt><strong>' . t('Menu name') . '</strong></dt>'
. '<dd>' . t('Select the menu to use for the tree of links.') . '</dd>'
. '<dt><strong>' . t('Starting level') . '</strong></dt>'
. '<dd>' . t('Blocks that start with the 1st level will always be visible. Blocks that start with the 2nd level or deeper will only be visible when the trail to the active menu item is in the block’s tree.') . '</dd>'
. '<dt><strong>' . t('Maximum depth') . '</strong></dt>'
. '<dd>' . t('From the starting level, specify the maximum depth of the tree. Blocks with a maximum depth of 1 will just be a single un-nested list of links with none of those links’ children displayed.') . '</dd>'
. '</dl>'
. '<h4>' . t('Advanced options') . '</h4>'
. '<dl>'
. '<dt><strong>' . t('Make the starting level follow the active menu item') . '</strong></dt>'
. '<dd>' . t('If the active menu item is deeper than the level specified above, the starting level will follow the active menu item. Otherwise, the starting level of the tree will remain fixed.') . '</dd>'
. '<dt><strong>' . t('Expand all children') . '</strong></dt>'
. '<dd>' . t('All children of this menu will be expanded.') . '</dd>'
. '<dt><strong>' . t('Sort') . '</strong></dt>'
. '<dd>' . t('Sort each item in the active trail to the top of its level. When used on a deep or wide menu tree, the active menu item’s children will be easier to see when the page is reloaded.') . '</dd>'
. '<dt><strong>' . t('Fixed parent item') . '</strong></dt>'
. '<dd>' . t('If you select a specific menu item, you alter the “starting level” and “maximum depth” options to be relative to the fixed parent item. The tree of links will only contain children of the selected parent item.') . '</dd>'
. '</dl>'
. '<h3>' . t('Styling menu blocks') . '</h3>'
. '<p>' . t('Themers should be aware of the myriad of classes, templates and theme functions available to them. See the <a href="!url">online documentation</a> or the README.txt file for detailed information about each of them.', array('!url' => url('http://drupal.org/node/748022'))) . '</p>'
. '<h3>' . t('Menu block API') . '</h3>'
. '<p>' . t('Developers can use the API of this module to create their own menu trees outside the confines of blocks. All of the publicly available API functions are documented in the menu_block.module file.') . '</p>'
. '<p>' . t('In addition, Menu block implements HOOK_get_menus() and HOOK_menu_block_tree_alter(). See menu_block.api.php for documentation.') . '</p>';
break;
}
return $output;
}
services:
menu_block.kernel_view_subscriber:
class: Drupal\menu_block\EventSubscriber\MenuBlockKernelViewSubscriber
arguments: ['@current_route_match']
tags:
- { name: event_subscriber }
<?php
/**
* @file
* Provides optional sorting of the active trail in the menu tree.
*/
/**
* Sort the active trail to the top of the tree.
*
* @param $tree
* array The menu tree to sort.
* @return
* void
*/
function _menu_tree_sort_active_path(&$tree) {
// To traverse the original tree down the active trail, we use a pointer.
$current_level =& $tree;
// Traverse the tree along the active trail.
do {
$next_level = $sort = $first_key = FALSE;
foreach ($current_level AS $key => &$value) {
// Save the first key for later use.
if (!$first_key) {
$first_key = $key;
}
if ($current_level[$key]['link']['in_active_trail'] && $current_level[$key]['below']) {
// Don't re-sort if its already sorted.
if ($key != $first_key) {
// Create a new key that will come before the first key.
list($first_key, ) = explode(' ', $first_key);
$first_key--;
list(, $new_key) = explode(' ', $key, 2);
$new_key = "$first_key $new_key";
// Move the item to the new key.
$current_level[$new_key] = $current_level[$key];
unset($current_level[$key]);
$key = $new_key;
$sort = TRUE; // Flag sorting.
}
$next_level = $key; // Flag subtree.
break;
}
}
// Sort this level.
if ($sort) {
ksort($current_level);
}
// Continue in the subtree, if it exists.
if ($next_level) {
$current_level =& $current_level[$next_level]['below'];
}
} while ($next_level);
}
<?php
/**
* @file
* Provides infrequently used functions and hooks for menu_block_export.
*/
/**
* Implements hook_menu().
*/
function _menu_block_export_menu() {
$items['admin/config/user-interface/menu-block/config'] = array(
'title' => 'Configure',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/config/user-interface/menu-block/export'] = array(
'title' => 'export',
'description' => 'Export menu blocks.',
'access arguments' => array('administer blocks'),
'page callback' => 'menu_block_export_export',
'type' => MENU_LOCAL_TASK,
'file' => 'menu_block_export.admin.inc',
);
$items['admin/config/user-interface/menu-block/export/results'] = array(
'title' => 'Menu block bulk export results',
'description' => 'Exported menu blocks.',
'access arguments' => array('administer blocks'),
'page callback' => 'menu_block_export_export',
'type' => MENU_CALLBACK,
'file' => 'menu_block_export.admin.inc',
);
return $items;
}
/**
* Page callback to export menu blocks in bulk.
*/
function menu_block_export_export() {
$blocks = variable_get('menu_block_ids', array());
if (!empty($blocks)) {
$form_state = array(
'no_redirect' => TRUE,
);
$output = drupal_build_form('menu_block_export_form', $form_state);
if (!empty($form_state['output'])) {
$output = $form_state['output'];
}
return $output;
}
else {
return t('There are no menu blocks to be exported at this time.');
}
}
/**
* Menu callback; export form.
*
* @return
* The export form used by Menu block.
*/
function menu_block_export_form($form, &$form_state) {
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Module name'),
'#default_value' => 'custom',
'#description' => t('Enter the module name to export code to.'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Export'),
);
$form['#action'] = url('admin/config/user-interface/menu-block/export/results');
return $form;
}
/**
* Submit callback for menu_block_export_form().
*/
function menu_block_export_form_submit(&$form, &$form_state) {
$output = '';
$module = check_plain($form_state['values']['name']);
foreach (variable_get('menu_block_ids', array()) AS $delta) {
$config = menu_block_get_config($delta);
// Use the constant instead of the constant's value.
if ($config['menu_name'] == MENU_TREE__CURRENT_PAGE_MENU) {
$config['menu_name'] = 'MENU_TREE__CURRENT_PAGE_MENU';
}
else {
// If it's not the constant, wrap value in quotes.
$config['menu_name'] = "'" . $config['menu_name'] . "'";
}
$output .= <<<END_OF_CONFIG
'$module-$delta' => array(
'menu_name' => {$config['menu_name']},
'parent_mlid' => {$config['parent_mlid']},
'title_link' => {$config['title_link']},
'admin_title' => '{$config['admin_title']}',
'level' => {$config['level']},
'follow' => {$config['follow']},
'depth' => {$config['depth']},
'expanded' => {$config['expanded']},
'sort' => {$config['sort']},
),
END_OF_CONFIG;
}
$output = <<<END_OF_CONFIG
/**
* Implements hook_menu_block_blocks().
*/
function {$module}_menu_block_blocks() {
// The array key is the block delta used by menu block.
return array($output
);
}
END_OF_CONFIG;
$element = array(
'#type' => 'textarea',
'#title' => t('Use this in your !module.module file:', array('!module' => $module)),
'#value' => $output,
'#rows' => 20,
// Since this isn't a real form, manually add additional required properties.
'#id' => 'menu-block-export-textarea',
'#name' => 'export',
'#required' => FALSE,
'#attributes' => array('style' => 'font-family: monospace;'),
'#title_display' => 'before',
'#parents' => array('dummy'),
);
$form_state['output'] = drupal_render($element);
}
name = "Menu Block Export"
description = "Provides export interface for Menu block module."
core = 7.x
dependencies[] = menu_block
files[] = menu_block_export.module
files[] = menu_block_export.admin.inc
configure = admin/config/user-interface/menu-block/export
<?php
/**
* @file
* Provides export interface for Menu block.
*/
// Off-load the following infrequently called hooks to another file.
function menu_block_export_menu() {
module_load_include('inc', 'menu_block_export', 'menu_block_export.admin');
return _menu_block_export_menu();
}
<?php
/**
* @file
* Provides ctools integration for "Menu block" trees.
*
* "Menu block" trees operate with no context at all. They are basically the
* same as a 'custom content' block, but not even that sophisticated.
*/
/**
* Plugins are described by creating a $plugin array which will be used
* by the system that includes this file.
*/
$plugin = array(
// And this is just the administrative title.
// All our callbacks are named according to the standard pattern and can be deduced.
'title' => t('Menu tree'),
);
/**
* Supplies a list of menu tree content sub-types.
*/
function menu_block_menu_tree_content_type_content_types() {
$common_plugin = array(
'description' => t('A configurable tree provided by Menu block.'),
'icon' => 'icon_contrib_menu.png',
'category' => array(t('Menus'), -9),
// The default context.
'defaults' => menu_block_get_config(),
// JavaScript and CSS for the config form.
'js' => array(drupal_get_path('module', 'menu_block') . '/menu-block.js'),
'css' => array(drupal_get_path('module', 'menu_block') . '/menu-block-admin.css'),
);
unset($common_plugin['defaults']['delta']);
$menus = menu_block_get_all_menus();
$items = array();
foreach ($menus as $menu_name => $title) {
$items[$menu_name] = $common_plugin;
$items[$menu_name]['title'] = t('%menu menu tree', array('%menu' => $title));
$items[$menu_name]['defaults']['menu_name'] = $menu_name;
$items[$menu_name]['menu_title'] = $title;
// Custom icons/titles for some menus.
switch ($menu_name) {
case MENU_TREE__CURRENT_PAGE_MENU:
$items[$menu_name]['title'] = t('menu tree of %menu', array('%menu' => 'the menu selected by the page'));
break;
case 'main-menu':
case 'secondary-menu':
$items[$menu_name]['icon'] = 'icon_contrib_main_menu.png';
break;
case 'management':
$items[$menu_name]['icon'] = 'icon_contrib_management.png';
break;
}
if (strpos($menu_name, 'book-toc-') === 0) {
$items[$menu_name]['icon'] = 'icon_contrib_booknavigation.png';
}
}
return $items;
}
/**
* Renders a menu_tree content type based on the delta supplied in the configuration.
*
* @param $subtype
* @param $conf
* Configuration as done at admin time.
* @param $args
* @param $context
* Context; in this case we don't have any.
* @return
* object An object with at least title and content members.
*/
function menu_block_menu_tree_content_type_render($subtype, $conf, $args, $context) {
// Ensure the delta is unique.
$ids = &drupal_static(__FUNCTION__, array());
if (empty($ids[$conf['menu_name']])) {
$ids[$conf['menu_name']] = 0;
}
$delta = ++$ids[$conf['menu_name']];
$conf['delta'] = 'ctools-' . $conf['menu_name'] . '-' . $delta;
$tree = menu_tree_build($conf);
$block = new stdClass();
$block->subtype = $conf['menu_name'];
$block->title = $tree['subject'];
$block->title_array = $tree['subject_array'];
$block->content = $tree['content'];
return $block;
}
/**
* 'Edit form' callback for the content type.
*/
function menu_block_menu_tree_content_type_edit_form(&$form, &$form_state) {
$conf = $form_state['conf'];
// Load the standard config form.
module_load_include('inc', 'menu_block', 'menu_block.admin');
// Create a pseudo form state.
$sub_form_state = array('values' => $conf);
$form += menu_block_configure_form($form, $sub_form_state);
// Set the options to a simple list of menu links for the configured menu.
$menus = menu_block_get_all_menus();
$form['parent']['#options'] = menu_parent_options(array($conf['menu_name'] => $menus[$conf['menu_name']]), array('mlid' => 0));
// Hide the Parent item option for the special "active" menu.
if ($conf['menu_name'] == MENU_TREE__CURRENT_PAGE_MENU) {
$form['parent']['#type'] = 'hidden';
$form['admin_title']['#suffix'] = '<div id="edit-parent-wrapper"><strong>' . t('Parent item:') . '</strong><br />' . t('<em>The menu selected by the page</em> can be customized on the <a href="!url">Menu block settings page</a>.', array('!url' => url('admin/config/user-interface/menu-block'))) . '</div>';
}
// Remove CSS class hooks for jQuery script on parent select.
unset($form['parent']['#attributes']);
}
/**
* Submit callback for content type editing form.
*/
function menu_block_menu_tree_content_type_edit_form_submit(&$form, &$form_state) {
foreach (array_keys($form_state['subtype']['defaults']) as $key) {
if (!empty($form_state['values'][$key])) {
$form_state['conf'][$key] = $form_state['values'][$key];
}
}
}
/**
* Return the tree's title with an admin-sensitive prefix.
*/
function menu_block_menu_tree_content_type_admin_title($subtype, $conf, $context = NULL) {
if (!empty($conf['admin_title'])) {
$output = filter_xss_admin($conf['admin_title']);
}
else {
// Build the menu tree.
module_load_include('inc', 'menu_block', 'menu_block.admin');
$output = _menu_block_format_title($conf);
}
return $output;
}
/**
* Callback to provide administrative info (the preview in panels when building a panel).
*/
function menu_block_menu_tree_content_type_admin_info($subtype, $conf, $context = NULL) {
// Ensure the delta is unique.
$ids = &drupal_static(__FUNCTION__, array());
if (empty($ids[$conf['menu_name']])) {
$ids[$conf['menu_name']] = 0;
}
$delta = ++$ids[$conf['menu_name']];
$conf['delta'] = 'ctools-' . $conf['menu_name'] . '-' . $delta;
// Force the title to not be a link.
$conf['title_link'] = 0;
$tree = menu_tree_build($conf);
$block = new stdClass();
$block->subtype = $conf['menu_name'];
$block->title = $tree['subject'];
$block->content = $tree['content'];
return $block;
}
<?php
namespace Drupal\menu_block\EventSubscriber;
use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Alters the block library modal.
*
* We can't use hook_ajax_render_alter() because the #markup is rendered before
* it is passed to that hook.
*/
class MenuBlockKernelViewSubscriber implements EventSubscriberInterface {
/**
* The current route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $currentRouteMatch;
/**
* Constructs a new MenuBlockKernelViewSubscriber.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $current_route_match
* The current route match.
*/
public function __construct(RouteMatchInterface $current_route_match) {