Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1021 diff -u -p -r1.1021 common.inc --- includes/common.inc 15 Oct 2009 21:19:30 -0000 1.1021 +++ includes/common.inc 15 Oct 2009 22:32:03 -0000 @@ -3292,7 +3292,7 @@ function drupal_clear_css_cache() { * @return * The cleaned identifier. */ -function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '[' => '-', ']' => '')) { +function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '/' => '-', '[' => '-', ']' => '')) { // By default, we filter using Drupal's coding standards. $identifier = strtr($identifier, $filter); Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.353 diff -u -p -r1.353 menu.inc --- includes/menu.inc 15 Oct 2009 14:07:26 -0000 1.353 +++ includes/menu.inc 16 Oct 2009 00:51:43 -0000 @@ -1578,6 +1578,9 @@ function menu_navigation_links($menu_nam * * @param $level * The level of tasks you ask for. Primary tasks are 0, secondary are 1. + * @param $path + * (optional) A menu link path to collect tabs and actions for. + * * @return * An array containing * - tabs: Local tasks for the requested level: @@ -1589,25 +1592,28 @@ function menu_navigation_links($menu_nam * - root_path: The router path for the current page. If the current page is * a default local task, then this corresponds to the parent tab. */ -function menu_local_tasks($level = 0) { +function menu_local_tasks($level = 0, $path = NULL) { $data = &drupal_static(__FUNCTION__); - $root_path = &drupal_static(__FUNCTION__ . ':root_path', ''); + $empty = array( 'tabs' => array('count' => 0, 'output' => array()), 'actions' => array('count' => 0, 'output' => array()), - 'root_path' => &$root_path, + 'root_path' => '', ); - if (!isset($data)) { - $data = array(); + $router_item = menu_get_item($path); + if (!$router_item || !$router_item['access']) { + return $empty; + } + $root_path = $router_item['path']; + $empty['root_path'] = $root_path; + + if (!isset($data[$root_path])) { + $data[$root_path] = $empty; // Set defaults in case there are no actions or tabs. $actions = $empty['actions']; $tabs = array(); - $router_item = menu_get_item(); - if (!$router_item || !$router_item['access']) { - return $empty; - } // Get all tabs and the root page. $result = db_select('menu_router', NULL, array('fetch' => PDO::FETCH_ASSOC)) ->fields('menu_router') @@ -1615,10 +1621,9 @@ function menu_local_tasks($level = 0) { ->orderBy('weight') ->orderBy('title') ->execute(); - $map = arg(); + $map = $router_item['original_map']; $children = array(); $tasks = array(); - $root_path = $router_item['path']; foreach ($result as $item) { _menu_translate($item, $map, TRUE); @@ -1682,7 +1687,7 @@ function menu_local_tasks($level = 0) { $actions['output'] = $actions_current; $depth++; } - $data['actions'] = $actions; + $data[$root_path]['actions'] = $actions; // Find all tabs at the same level or above the current one. $parent = $router_item['tab_parent']; $path = $router_item['path']; @@ -1706,7 +1711,7 @@ function menu_local_tasks($level = 0) { // Use the path of the parent instead. $link['href'] = $tasks[$p]['href']; if ($item['path'] == $router_item['path']) { - $root_path = $tasks[$p]['path']; + $data[$root_path]['root_path'] = $tasks[$p]['path']; } } // We check for the active tab. @@ -1739,23 +1744,49 @@ function menu_local_tasks($level = 0) { ksort($tabs); // Remove the depth, we are interested only in their relative placement. $tabs = array_values($tabs); - $data['tabs'] = $tabs; + $data[$root_path]['tabs'] = $tabs; // Allow modules to alter local tasks or dynamically append further tasks. - drupal_alter('menu_local_tasks', $data, $router_item, $root_path); + drupal_alter('menu_local_tasks', $data[$root_path], $router_item, $root_path); } - if (isset($data['tabs'][$level])) { + if (isset($data[$root_path]['tabs'][$level])) { return array( - 'tabs' => $data['tabs'][$level], - 'actions' => $data['actions'], - 'root_path' => $root_path, + 'tabs' => $data[$root_path]['tabs'][$level], + 'actions' => $data[$root_path]['actions'], + 'root_path' => $data[$root_path]['root_path'], ); } return $empty; } /** + * Retrieve contextual links for a system object. + * + * @param $path + * The menu router path of the object to retrieve local tasks for. + * + * @return + * A renderable array containing menu router items that are local tasks for + * the passed in path. + */ +function menu_context_links($path) { + $local_tasks = menu_local_tasks(0, $path); + $root_length = drupal_strlen($local_tasks['root_path']); + $tasks = $local_tasks['tabs']['output']; + foreach ($tasks as $key => $task) { + unset($tasks[$key]['#theme']); + // Extract the link "type" from the path. + $tasks[$key]['#link_type'] = drupal_substr($task['#link']['path'], $root_length + 1); + } + // If there is more than one tab, unset the default tab. + if (count($tasks) > 1) { + unset($tasks[0]); + } + return $tasks; +} + +/** * Returns the rendered local tasks at the top level. */ function menu_primary_local_tasks() { Index: misc/attached_links.css =================================================================== RCS file: misc/attached_links.css diff -N misc/attached_links.css --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ misc/attached_links.css 16 Oct 2009 02:48:00 -0000 @@ -0,0 +1,41 @@ +/* $Id$ */ + +/** + * Styling for attached links and the page regions they are associated with. + */ +.attached-links-link { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + height: 19px; + width: 19px; + float: left; + display: block; + margin-right: 2px; +} + +.attached-links-enabled-region { + outline: none; + position: relative; +} + +.attached-links-region-active { + outline: #000 dashed 2px; +} + +ul.attached-links { + float: right; + padding: 0; + margin: 0; +} + +ul.attached-links li { + padding: 0; + margin: 0; + list-style: none; + display: inline; + line-height: 100%; +} + +ul.attached-links li a { + text-decoration: none; +} Index: misc/attached_links.js =================================================================== RCS file: misc/attached_links.js diff -N misc/attached_links.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ misc/attached_links.js 16 Oct 2009 02:44:57 -0000 @@ -0,0 +1,31 @@ +// $Id$ +(function ($) { + +/** + * Highlights the region of the page that an attached link is associated with. + */ +Drupal.behaviors.attachedLinks = { + attach: function (context) { + var addHighlight = function () { + // If the attached link has a CSS class containing an encoded version of + // the URL that the link points to, find the region of the page whose CSS + // class contains the same encoded URL. + var matches = $(this).attr('class').match(/\battached-links-link-to-(\S+)/); + if (matches) { + var className = '.attached-links-region-for-' + matches[1]; + $(className).addClass('attached-links-region-active'); + $(this).addClass('attached-links-link-active'); + } + }; + + var removeHighlight = function () { + $('.active-attached-links-region').removeClass('attached-links-region-active'); + $('.active-attached-links-link').removeClass('attached-links-link-active'); + }; + + // Trigger the behavior when hovering over the link. + $('.attached-links-link').hover(addHighlight, removeHighlight); + } +}; + +})(jQuery); Index: modules/block/block.module =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.module,v retrieving revision 1.389 diff -u -p -r1.389 block.module --- modules/block/block.module 14 Oct 2009 02:13:14 -0000 1.389 +++ modules/block/block.module 16 Oct 2009 01:30:00 -0000 @@ -280,6 +280,18 @@ function _block_get_renderable_array($li foreach ($list as $key => $block) { $build[$key] = $block->content; unset($block->content); + + // Attach links for this block; skip this for the system main block. + if ($key != 'system_main') { + // Add local task to configure block. + // @todo Implement proper path/subject/verb URL pattern for blocks. + $build[$key]['#pre_render'][] = 'system_pre_render_attached_links'; + $build[$key]['#attached_links'][][] = array( + '#link' => menu_get_item("admin/structure/block/configure/{$block->module}/{$block->delta}"), + '#link_type' => 'configure', + ); + } + $build[$key] += array( '#block' => $block, '#weight' => ++$weight, @@ -752,6 +764,12 @@ function _block_render_blocks($region_bl } else { $array = module_invoke($block->module, 'block_view', $block->delta); + + // Allow modules to modify the block before it is viewed, via either + // hook_block_view_MODULE_DELTA_alter() or hook_block_view_alter(). + drupal_alter("block_view_{$block->module}_{$block->delta}", $array); + drupal_alter('block_view', $array, $block->module, $block->delta); + if (isset($cid)) { cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY); } Index: modules/block/block.tpl.php =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.tpl.php,v retrieving revision 1.4 diff -u -p -r1.4 block.tpl.php --- modules/block/block.tpl.php 11 Sep 2009 06:48:02 -0000 1.4 +++ modules/block/block.tpl.php 10 Oct 2009 17:39:51 -0000 @@ -11,6 +11,12 @@ * - $block->module: Module that generated the block. * - $block->delta: An ID for the block, unique within each module. * - $block->region: The block region embedding the current block. + * - $attached_links: An array of links that are attached to the block on the + * page. Use render($attached_links) to print them all, or print a subset + * using render($attached_links['block']), which prints the configuration + * links for the block itself, or render($attached_links['block content']), + * which prints the links associated with the content that is displayed in + * the block. * - $classes: String of classes that can be used to style contextually through * CSS. It can be manipulated through the variable $classes_array from * preprocess functions. The default values can be one or more of the following: @@ -36,6 +42,11 @@ */ ?>