...
 
Commits (26)
// $Id$
Menu Block 6.x-2.x-dev, xxxx-xx-xx (development release)
----------------------------------
-
Menu Block 6.x-2.4, 2011-02-11
------------------------------
- #1050766: Improve usability of "Parent item" UI
- #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
- Fix theme hook suggestions for non-numeric block deltas
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
......
// $Id$
ADDING MENU BLOCKS
------------------
......@@ -36,9 +34,8 @@ Administrative title
unique title to be used on that page. If blank, the regular title will be
used.
Parent item
First select the menu. Then select the parent item from that menu. The tree of
links will only contain children of the selected parent item.
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
......@@ -63,6 +60,11 @@ Sort
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
-------------------
......@@ -119,7 +121,7 @@ called by it.
- [theme]_menu_item_link__menu_block__[block id number]()
For example, if you created a garland_menu_tree__menu_block() function, it would
override theme_menu_block() any time it was used by this module, but not when
override theme_menu_tree() any time it was used by this module, but not when
used by any other module. Similarly, a garland_menu_item__menu_block__1()
function would override theme_menu_item(), but only for the first menu block in
your system (the menu block with an ID of 1).
......@@ -132,5 +134,6 @@ 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_get_menus() and
HOOK_menu_block_tree_alter(). See menu_block.api.php for documentation.
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.
/* $Id$ */
#edit-admin-title-wrapper,
#edit-parent-wrapper,
#edit-depth-wrapper {
......
<?php
// $Id$
/**
* @file
* Default theme implementation to wrap menu blocks.
......
// $Id$
(function ($) {
Drupal.behaviors.menu_block = function (context) {
......@@ -10,58 +8,56 @@ Drupal.behaviors.menu_block = function (context) {
$('#menu-block-settings', context).addClass('menu-block-processed');
// Process the form if its in a Panel overlay.
if ($('#override-title-checkbox', context).size()) {
if ($('.menu-block-menu-tree-configure-form', context).size()) {
// Toggle display of "title link" if "override title" is checked.
$('#override-title-checkbox', context).change( function() {
if ($('#override-title-checkbox:checked').length) {
$('#edit-title-link-wrapper').slideUp('fast');
$('.menu-block-override-title', context).change( function() {
if ($('.menu-block-override-title:checked').length) {
$('.menu-block-title-link').slideUp('fast');
}
else {
$('#edit-title-link-wrapper').slideDown('fast');
$('.menu-block-title-link').slideDown('fast');
}
} );
if ($('#override-title-checkbox:checked').length) {
$('#edit-title-link-wrapper').css('display', 'none');
if ($('.menu-block-override-title:checked').length) {
$('.menu-block-title-link').css('display', 'none');
}
}
// Process the form if its on a block config page.
else {
else if ($('.menu-block-configure-form', context).size()) {
// Toggle display of "title link" if "block title" has a value.
$('#edit-title', context).change( function() {
if ($('#edit-title').val()) {
$('#edit-title-link-wrapper').slideUp('fast');
$('input[name="title"]', context).change( function() {
if ($('input[name="title"]').val()) {
$('.menu-block-title-link').slideUp('fast');
}
else {
$('#edit-title-link-wrapper').slideDown('fast');
$('.menu-block-title-link').slideDown('fast');
}
} );
if ($('#edit-title', context).val()) {
$('#edit-title-link-wrapper').css('display', 'none');
if ($('input[name="title"]', context).val()) {
$('.menu-block-title-link').css('display', 'none');
}
// Split the un-wieldly "parent item" pull-down into two hierarchal pull-downs.
$('#edit-parent', context)
.html(Drupal.settings.menu_block.parent_options[Drupal.settings.menu_block.menus_default])
.val(Drupal.settings.menu_block.parent_default)
.before(Drupal.settings.menu_block.menus);
$('#edit-parent-menu', context).change( function() {
$('#edit-parent')
.html(Drupal.settings.menu_block.parent_options[$('#edit-parent-menu').val()])
.val(Drupal.settings.menu_block.parent_default);
} );
// 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');
});
}
// Toggle display of "follow parent" if "follow" has been checked.
$('#edit-follow', context).change( function() {
if ($('#edit-follow:checked').length) {
$('#edit-follow-parent-wrapper').slideDown('fast');
$('.menu-block-follow', context).change( function() {
if ($('.menu-block-follow:checked').length) {
$('.menu-block-follow-parent').slideDown('fast');
}
else {
$('#edit-follow-parent-wrapper').slideUp('fast');
$('.menu-block-follow-parent').slideUp('fast');
}
} );
if (!$('#edit-follow:checked', context).length) {
$('#edit-follow-parent-wrapper', context).css('display', 'none');
if (!$('.menu-block-follow:checked', context).length) {
$('.menu-block-follow-parent', context).css('display', 'none');
}
};
......
This diff is collapsed.
<?php
// $Id$
/**
* @file
* Hooks provided by the Menu Block module.
......@@ -29,10 +27,31 @@ function hook_menu_block_tree_alter(&$tree, &$config) {
* An array containing the menus' machine names as keys with their menu titles
* as values.
*/
function hook_get_menus() {
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;
}
......
<?php
// $Id$
/**
* @file
* Provides book integration.
*/
/**
* Implements hook_get_menus().
*/
function book_get_menus() {
$menus = array();
foreach (book_get_books() AS $book) {
$menus[$book['menu_name']] = $book['title'];
}
return $menus;
}
<?php
// $Id$
/**
* @file
* Provides active menu item pruning.
......
; $Id$
name = "Menu Block"
description = "Provides configurable blocks of menu items."
......
<?php
// $Id$
/**
* @file
* Provides install, upgrade and un-install functions for menu_block.
......@@ -44,11 +42,7 @@ function menu_block_install() {
}
/**
* Implements hook_update_N().
*
* Pre-1.0 versions used a different naming convention for block names. Convert
* the old names to the new format. An unfortunate side effect of this renaming
* is that it disables all the previously enabled blocks.
* Convert pre-1.0 configurations to the new block naming convention.
*/
function menu_block_update_5100() {
$delta = 0;
......@@ -74,11 +68,7 @@ function menu_block_update_5100() {
}
/**
* Implements hook_update_N().
*
* Converts $menu_block_enabled_blocks to $menu_block_ids and
* $menu_block_DELTA_mid to $menu_block_DELTA_menu_name. Also deletes un-enabled
* menu blocks.
* Converts enabled blocks list to ID list and deletes un-enabled blocks.
*/
function menu_block_update_5200() {
$block_ids = array();
......@@ -112,8 +102,6 @@ function menu_block_update_5200() {
}
/**
* Implements hook_update_N().
*
* Converts the mids to menu names using the D5-stored menu_title variable.
*/
function menu_block_update_6200() {
......@@ -132,8 +120,6 @@ function menu_block_update_6200() {
}
/**
* Implements hook_update_N().
*
* Converts the menu names to parent items.
*/
function menu_block_update_6201() {
......
<?php
// $Id$
/**
* @file
* Provides configurable blocks of menu items.
*/
// @TODO: For PHP 4 compatibility we use foreach (array_keys($array) AS $key).
// When PHP 5 becomes required (Drupal 7.x), use the following faster
// implementation: foreach ($array AS $key => &$value) {}
/**
* Denotes that the tree should use the menu picked by the curent page.
*/
define('MENU_TREE__CURRENT_PAGE_MENU', '_active');
/**
* Implements hook_menu().
......@@ -17,26 +16,26 @@ function menu_block_menu() {
$items['admin/build/block/add-menu-block'] = array(
'title' => 'Add menu block',
'description' => 'Add a new menu block.',
'access arguments' => array('administer blocks'),
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_block_add_block_form'),
'access arguments' => array('administer blocks'),
'type' => MENU_LOCAL_TASK,
'file' => 'menu_block.admin.inc',
);
$items['admin/build/block/delete-menu-block'] = array(
'title' => 'Delete menu block',
'access arguments' => array('administer blocks'),
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_block_delete'),
'access arguments' => array('administer blocks'),
'type' => MENU_CALLBACK,
'file' => 'menu_block.admin.inc',
);
$items['admin/settings/menu_block'] = array(
'title' => 'Menu block',
'description' => 'Configure menu block.',
'access arguments' => array('administer blocks'),
'page callback' => 'drupal_get_form',
'page arguments' => array('menu_block_admin_settings_form'),
'access arguments' => array('administer blocks'),
'type' => MENU_NORMAL_ITEM,
'file' => 'menu_block.admin.inc',
);
......@@ -127,16 +126,19 @@ function menu_block_get_all_menus() {
static $all_menus;
if (!$all_menus) {
// Include book support.
if (module_exists('book')) {
module_load_include('inc', 'menu_block', 'menu_block.book');
if ($cached = cache_get('menu_block_menus', 'cache_menu')) {
$all_menus = $cached->data;
}
else {
// Retrieve core's menus.
$all_menus = menu_get_menus();
// Retrieve all the menu names provided by hook_menu_block_get_menus().
$all_menus = array_merge($all_menus, module_invoke_all('menu_block_get_menus'));
// Add an option to use the menu for the active menu item.
$all_menus[MENU_TREE__CURRENT_PAGE_MENU] = '<' . t('the menu selected by the page') . '>';
asort($all_menus);
cache_set('menu_block_menus', $all_menus, 'cache_menu');
}
// We're generalizing menu's menu_get_menus() by making it into a hook.
// Retrieve all the menu names provided by hook_get_menus().
$all_menus = module_invoke_all('get_menus');
// Add an option to use the menu for the active menu item.
$all_menus['_active'] = '<' . t('the menu selected by the page') . '>';
asort($all_menus);
}
return $all_menus;
}
......@@ -214,7 +216,7 @@ function _menu_block_block_view($delta) {
* menu tree and its title.
* - delta: (string) The menu_block's block delta.
* - menu_name: (string) The machine name of the requested menu. Can also be
* set to '_active' to use "the menu selected by the page" option.
* set to MENU_TREE__CURRENT_PAGE_MENU to use the menu selected by the page.
* - parent_mlid: (int) The mlid of the item that should root the tree. Use 0
* to use the menu's root.
* - title_link: (boolean) Specifies if the title should be rendered as a link
......@@ -235,21 +237,44 @@ function _menu_block_block_view($delta) {
*/
function menu_tree_build($config) {
// Retrieve the active menu item from the database.
if ($config['menu_name'] == '_active') {
if ($config['menu_name'] == MENU_TREE__CURRENT_PAGE_MENU) {
// Retrieve the list of available menus.
$menu_order = variable_get('menu_block_menu_order', array('primary-links' => '', 'secondary-links' => ''));
// Check for regular expressions as menu keys.
$patterns = array();
foreach (array_keys($menu_order) as $pattern) {
if ($pattern[0] == '/') {
$patterns[$pattern] = NULL;
}
}
// Retrieve all the menus containing a link to the current page.
$result = db_query("SELECT menu_name FROM {menu_links} WHERE link_path = '%s'", $_GET['q'] ? $_GET['q'] : '<front>');
while ($item = db_fetch_array($result)) {
// Check if the menu is in the list of available menus.
if (isset($menu_order[$item['menu_name']])) {
// Mark the menu.
$menu_order[$item['menu_name']] = '_active';
$menu_order[$item['menu_name']] = MENU_TREE__CURRENT_PAGE_MENU;
}
else {
// Check if the menu matches one of the available patterns.
foreach (array_keys($patterns) as $pattern) {
if (preg_match($pattern, $item['menu_name'])) {
// Mark the menu.
$menu_order[$pattern] = MENU_TREE__CURRENT_PAGE_MENU;
// Store the actual menu name.
$patterns[$pattern] = $item['menu_name'];
}
}
}
}
// Find the first marked menu.
$config['menu_name'] = array_search('_active', $menu_order);
$config['menu_name'] = array_search(MENU_TREE__CURRENT_PAGE_MENU, $menu_order);
// If a pattern was matched, use the actual menu name instead of the pattern.
if (!empty($patterns[$config['menu_name']])) {
$config['menu_name'] = $patterns[$config['menu_name']];
}
$config['parent_mlid'] = 0;
// If no menu link was found, don't display the block.
......@@ -321,7 +346,7 @@ function menu_tree_build($config) {
$data['content'] = menu_block_tree_output($tree, $config);
if ($data['content']) {
$hooks = array();
$hooks[] = 'menu_block_wrapper__' . $config['delta'];
$hooks[] = 'menu_block_wrapper__' . str_replace('-', '_', $config['delta']);
$hooks[] = 'menu_block_wrapper__' . str_replace('-', '_', $config['menu_name']);
$hooks[] = 'menu_block_wrapper';
$data['content'] = theme($hooks, $data['content'], $config, $config['delta']);
......@@ -358,7 +383,7 @@ function menu_block_get_title($render_title_as_link = TRUE, $config = array()) {
}
$hooks = array();
if (!empty($config['delta'])) {
$hooks[] = 'menu_item_link__menu_block__' . $config['delta'];
$hooks[] = 'menu_item_link__menu_block__' . str_replace('-', '_', $config['delta']);
}
$hooks[] = 'menu_item_link__menu_block__' . str_replace('-', '_', $menu_item['menu_name']);
$hooks[] = 'menu_item_link__menu_block';
......@@ -412,7 +437,7 @@ function menu_tree_add_active_path(&$tree) {
// Find each key in the active trail.
while ($tree_with_trail) {
foreach (array_keys($tree_with_trail) AS $key) {
foreach (array_keys($tree_with_trail) as $key) {
if ($tree_with_trail[$key]['link']['in_active_trail']) {
// Set the active trail info in the original tree.
$subtree_pointer[$key]['link']['in_active_trail'] = TRUE;
......@@ -437,7 +462,7 @@ function menu_tree_add_active_path(&$tree) {
* void
*/
function menu_tree_trim_active_path(&$tree) {
foreach (array_keys($tree) AS $key) {
foreach (array_keys($tree) as $key) {
if (($tree[$key]['link']['in_active_trail'] || $tree[$key]['link']['expanded']) && $tree[$key]['below']) {
// Continue in the subtree, if it exists.
menu_tree_trim_active_path($tree[$key]['below']);
......@@ -483,7 +508,7 @@ function menu_tree_prune_tree(&$tree, $level, $parent_item = FALSE) {
$plid = $parent_item["p$i"];
$found_active_trail = FALSE;
// Examine each element at this level for the ancestor.
foreach (array_keys($tree) AS $key) {
foreach (array_keys($tree) as $key) {
if ($tree[$key]['link']['mlid'] == $plid) {
menu_block_set_title($tree[$key]['link']);
// Prune the tree to the children of this ancestor.
......@@ -504,7 +529,7 @@ function menu_tree_prune_tree(&$tree, $level, $parent_item = FALSE) {
for ($i = 1; $i < $level; $i++) {
$found_active_trail = FALSE;
// Examine each element at this level for the active trail.
foreach (array_keys($tree) AS $key) {
foreach (array_keys($tree) as $key) {
if ($tree[$key]['link']['in_active_trail']) {
// Get the title for the pruned tree.
menu_block_set_title($tree[$key]['link']);
......@@ -549,7 +574,9 @@ function menu_tree_prune_active_tree(&$tree, $level) {
*/
function menu_tree_depth_trim(&$tree, $depth_limit) {
// Prevent invalid input from returning a trimmed tree.
if ($depth_limit < 1) { return; }
if ($depth_limit < 1) {
return;
}
// Examine each element at this level to find any possible children.
foreach (array_keys($tree) AS $key) {
......@@ -592,7 +619,7 @@ function menu_block_tree_output(&$tree, $config = array()) {
$menu_item = current($tree);
$config['menu_name'] = $menu_item['link']['menu_name'];
}
$hook_delta = $config['delta'];
$hook_delta = str_replace('-', '_', $config['delta']);
$hook_menu_name = str_replace('-', '_', $config['menu_name']);
// Pull out just the menu items we are going to render so that we
......@@ -658,3 +685,23 @@ function menu_block_tree_output(&$tree, $config = array()) {
$hooks[] = 'menu_tree';
return $output ? theme($hooks, $output) : '';
}
/**
* Implements hook_menu_block_get_menus() on behalf of book.module.
*/
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() on behalf of book.module.
*/
function book_menu_block_get_sort_menus() {
return array(
'/^book\-toc\-.+/' => t('Book navigation'),
);
}
<?php
// $Id$
/**
* @file
* Provides infrequently used pages for menu_block.
......@@ -40,8 +38,8 @@ function _menu_block_help($path, $arg) {
. '<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('Parent item') . '</strong></dt>'
. '<dd>' . t('First select the menu. Then select the parent item from that menu. The tree of links will only contain children of the selected parent item.') . '</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('Make the starting level follow the active menu item') . '</strong></dt>'
......@@ -52,6 +50,8 @@ function _menu_block_help($path, $arg) {
. '<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>'
......
<?php
// $Id$
/**
* @file
* Provides optional sorting of the active trail in the menu tree.
......
<?php
// $Id$
/**
* @file
* Provides ctools integration for "Menu block" trees.
......@@ -44,8 +42,11 @@ function menu_block_menu_tree_content_type_content_types() {
$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 for some menus.
// 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 'primary-links':
case 'secondary-links':
$items[$menu_name]['icon'] = 'icon_contrib_primarylinks.png';
......@@ -107,6 +108,20 @@ function menu_block_menu_tree_content_type_edit_form(&$form, &$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/settings/menu_block'))) . '</div>';
}
// Add classes for jQuery script.
$form['menu-block-wrapper-start']['#value'] = '<div id="menu-block-settings" class="menu-block-menu-tree-configure-form">';
if (empty($form['override_title']['#attributes']['class'])) {
$form['override_title']['#attributes']['class'] = 'menu-block-override-title';
}
else {
$form['override_title']['#attributes']['class'] .= ' menu-block-override-title';
}
}
/**
......@@ -124,14 +139,14 @@ function menu_block_menu_tree_content_type_edit_form_submit(&$form, &$form_state
* 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'])) {
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);
else {
// Build the menu tree.
module_load_include('inc', 'menu_block', 'menu_block.admin');
$output = _menu_block_format_title($conf);
}
return $output;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.