#473268 by David_Rothstein, sun, yoroy, Gábor Hojtsy, cwgordon7, et al.: Allow contextual editing of dang near everything on the page. Also adds a context system to menu local tasks.
parent
29067790f1
commit
4278afa16b
|
@ -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);
|
||||
|
||||
|
|
|
@ -184,6 +184,26 @@ define('MENU_LOCAL_ACTION', MENU_IS_LOCAL_TASK | MENU_IS_LOCAL_ACTION);
|
|||
* @} End of "Menu item types".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name Menu context types
|
||||
* @{
|
||||
* Flags for use in the "context" attribute of menu router items.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Internal menu flag: Local task should be displayed in page context.
|
||||
*/
|
||||
define('MENU_CONTEXT_PAGE', 0x0001);
|
||||
|
||||
/**
|
||||
* Internal menu flag: Local task should be displayed inline.
|
||||
*/
|
||||
define('MENU_CONTEXT_INLINE', 0x0002);
|
||||
|
||||
/**
|
||||
* @} End of "Menu context types".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name Menu status codes
|
||||
* @{
|
||||
|
@ -663,14 +683,14 @@ function _menu_item_localize(&$item, $map, $link_translate = FALSE) {
|
|||
* a non existing node) then this function return FALSE.
|
||||
*/
|
||||
function _menu_translate(&$router_item, $map, $to_arg = FALSE) {
|
||||
if ($to_arg) {
|
||||
if ($to_arg && !empty($router_item['to_arg_functions'])) {
|
||||
// Fill in missing path elements, such as the current uid.
|
||||
_menu_link_map_translate($map, $router_item['to_arg_functions']);
|
||||
}
|
||||
// The $path_map saves the pieces of the path as strings, while elements in
|
||||
// $map may be replaced with loaded objects.
|
||||
$path_map = $map;
|
||||
if (!_menu_load_objects($router_item, $map)) {
|
||||
if (!empty($router_item['load_functions']) && !_menu_load_objects($router_item, $map)) {
|
||||
// An error occurred loading an object.
|
||||
$router_item['access'] = FALSE;
|
||||
return FALSE;
|
||||
|
@ -706,17 +726,15 @@ function _menu_translate(&$router_item, $map, $to_arg = FALSE) {
|
|||
* An array of helper function (ex: array(2 => 'menu_tail_to_arg'))
|
||||
*/
|
||||
function _menu_link_map_translate(&$map, $to_arg_functions) {
|
||||
if ($to_arg_functions) {
|
||||
$to_arg_functions = unserialize($to_arg_functions);
|
||||
foreach ($to_arg_functions as $index => $function) {
|
||||
// Translate place-holders into real values.
|
||||
$arg = $function(!empty($map[$index]) ? $map[$index] : '', $map, $index);
|
||||
if (!empty($map[$index]) || isset($arg)) {
|
||||
$map[$index] = $arg;
|
||||
}
|
||||
else {
|
||||
unset($map[$index]);
|
||||
}
|
||||
$to_arg_functions = unserialize($to_arg_functions);
|
||||
foreach ($to_arg_functions as $index => $function) {
|
||||
// Translate place-holders into real values.
|
||||
$arg = $function(!empty($map[$index]) ? $map[$index] : '', $map, $index);
|
||||
if (!empty($map[$index]) || isset($arg)) {
|
||||
$map[$index] = $arg;
|
||||
}
|
||||
else {
|
||||
unset($map[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -751,7 +769,9 @@ function _menu_link_translate(&$item) {
|
|||
}
|
||||
else {
|
||||
$map = explode('/', $item['link_path']);
|
||||
_menu_link_map_translate($map, $item['to_arg_functions']);
|
||||
if (!empty($item['to_arg_functions'])) {
|
||||
_menu_link_map_translate($map, $item['to_arg_functions']);
|
||||
}
|
||||
$item['href'] = implode('/', $map);
|
||||
|
||||
// Note - skip callbacks without real values for their arguments.
|
||||
|
@ -761,7 +781,7 @@ function _menu_link_translate(&$item) {
|
|||
}
|
||||
// menu_tree_check_access() may set this ahead of time for links to nodes.
|
||||
if (!isset($item['access'])) {
|
||||
if (!_menu_load_objects($item, $map)) {
|
||||
if (!empty($item['load_functions']) && !_menu_load_objects($item, $map)) {
|
||||
// An error occurred loading an object.
|
||||
$item['access'] = FALSE;
|
||||
return FALSE;
|
||||
|
@ -1614,10 +1634,11 @@ function menu_local_tasks($level = 0) {
|
|||
$result = db_select('menu_router', NULL, array('fetch' => PDO::FETCH_ASSOC))
|
||||
->fields('menu_router')
|
||||
->condition('tab_root', $router_item['tab_root'])
|
||||
->condition('context', MENU_CONTEXT_INLINE, '<>')
|
||||
->orderBy('weight')
|
||||
->orderBy('title')
|
||||
->execute();
|
||||
$map = arg();
|
||||
$map = $router_item['original_map'];
|
||||
$children = array();
|
||||
$tasks = array();
|
||||
$root_path = $router_item['path'];
|
||||
|
@ -1757,6 +1778,100 @@ function menu_local_tasks($level = 0) {
|
|||
return $empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve contextual links for a system object based on registered local tasks.
|
||||
*
|
||||
* This leverages the menu system to retrieve the first layer of registered
|
||||
* local tasks for a given system path. All local tasks of the tab type 'task'
|
||||
* or 'context' are taken into account.
|
||||
*
|
||||
* @see hook_menu()
|
||||
*
|
||||
* For example, when considering the following registered local tasks:
|
||||
* - node/%node/view (default local task) with no 'context' defined
|
||||
* - node/%node/edit with context: MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE
|
||||
* - node/%node/revisions with context: MENU_CONTEXT_PAGE
|
||||
* - node/%node/report-as-spam with context: MENU_CONTEXT_INLINE
|
||||
*
|
||||
* If the path "node/123" is passed to this function, then it will return the
|
||||
* links for 'edit' and 'report-as-spam'.
|
||||
*
|
||||
* @param $path
|
||||
* The menu router path of the object to retrieve local tasks for, for example
|
||||
* "node/123" or "admin/structure/menu/manage/[menu_name]".
|
||||
*
|
||||
* @return
|
||||
* A list of menu router items that are local tasks for the passed in path.
|
||||
*
|
||||
* @see system_preprocess()
|
||||
*/
|
||||
function menu_contextual_links($parent_path, $args) {
|
||||
static $path_empty = array();
|
||||
|
||||
$links = array();
|
||||
// Performance: In case a previous invocation for the same parent path did not
|
||||
// return any links, we immediately return here.
|
||||
if (isset($path_empty[$parent_path])) {
|
||||
return $links;
|
||||
}
|
||||
// Construct the item-specific parent path.
|
||||
$path = $parent_path . '/' . implode('/', $args);
|
||||
|
||||
// Get the router item for the given parent link path.
|
||||
$router_item = menu_get_item($path);
|
||||
if (!$router_item || !$router_item['access']) {
|
||||
$path_empty[$parent_path] = TRUE;
|
||||
return $links;
|
||||
}
|
||||
$data = &drupal_static(__FUNCTION__, array());
|
||||
$root_path = $router_item['path'];
|
||||
|
||||
// Performance: For a single, normalized path (such as 'node/%') we only query
|
||||
// available tasks once per request.
|
||||
if (!isset($data[$root_path])) {
|
||||
// Get all contextual links that are direct children of the router item and
|
||||
// not of the tab type 'view'.
|
||||
$data[$root_path] = db_select('menu_router', 'm')
|
||||
->fields('m')
|
||||
->condition('tab_parent', $router_item['tab_root'])
|
||||
->condition('context', MENU_CONTEXT_PAGE, '<>')
|
||||
->orderBy('weight')
|
||||
->orderBy('title')
|
||||
->execute()
|
||||
->fetchAllAssoc('path', PDO::FETCH_ASSOC);
|
||||
}
|
||||
$parent_length = drupal_strlen($root_path) + 1;
|
||||
$map = $router_item['original_map'];
|
||||
foreach ($data[$root_path] as $item) {
|
||||
// Extract the actual "task" string from the path argument.
|
||||
$key = drupal_substr($item['path'], $parent_length);
|
||||
|
||||
// Denormalize and translate the contextual link.
|
||||
_menu_translate($item, $map, TRUE);
|
||||
if (!$item['access']) {
|
||||
continue;
|
||||
}
|
||||
// All contextual links are keyed by the actual "task" path argument. The
|
||||
// menu system does not allow for two local tasks with the same name, and
|
||||
// since the key is also used as CSS class for the link item, which may be
|
||||
// styled as icon, it wouldn't make sense to display the same icon for
|
||||
// different tasks.
|
||||
$links[$key] = $item;
|
||||
}
|
||||
|
||||
// Allow modules to alter contextual links.
|
||||
drupal_alter('menu_contextual_links', $links, $router_item, $root_path);
|
||||
|
||||
// Performance: If the current user does not have access to any links for this
|
||||
// router path and no other module added further links, we assign FALSE here
|
||||
// to skip the entire process the next time the same router path is requested.
|
||||
if (empty($links)) {
|
||||
$path_empty[$parent_path] = TRUE;
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rendered local tasks at the top level.
|
||||
*/
|
||||
|
@ -2898,6 +3013,10 @@ function _menu_router_build($callbacks) {
|
|||
$item['tab_parent'] = '';
|
||||
$item['tab_root'] = $path;
|
||||
}
|
||||
// If not specified, assign the default tab type for local tasks.
|
||||
elseif (!isset($item['context'])) {
|
||||
$item['context'] = MENU_CONTEXT_PAGE;
|
||||
}
|
||||
for ($i = $item['_number_parts'] - 1; $i; $i--) {
|
||||
$parent_path = implode('/', array_slice($item['_parts'], 0, $i));
|
||||
if (isset($menu[$parent_path])) {
|
||||
|
@ -2970,6 +3089,7 @@ function _menu_router_build($callbacks) {
|
|||
'theme callback' => '',
|
||||
'description' => '',
|
||||
'position' => '',
|
||||
'context' => 0,
|
||||
'tab_parent' => '',
|
||||
'tab_root' => $path,
|
||||
'path' => $path,
|
||||
|
@ -3013,6 +3133,7 @@ function _menu_router_save($menu, $masks) {
|
|||
'delivery_callback',
|
||||
'fit',
|
||||
'number_parts',
|
||||
'context',
|
||||
'tab_parent',
|
||||
'tab_root',
|
||||
'title',
|
||||
|
@ -3041,6 +3162,7 @@ function _menu_router_save($menu, $masks) {
|
|||
'delivery_callback' => $item['delivery callback'],
|
||||
'fit' => $item['_fit'],
|
||||
'number_parts' => $item['_number_parts'],
|
||||
'context' => $item['context'],
|
||||
'tab_parent' => $item['tab_parent'],
|
||||
'tab_root' => $item['tab_root'],
|
||||
'title' => $item['title'],
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/* $Id$ */
|
||||
|
||||
/**
|
||||
* Contextual links regions.
|
||||
*/
|
||||
.contextual-links-region {
|
||||
outline: none;
|
||||
position: relative;
|
||||
}
|
||||
.contextual-links-region-active {
|
||||
outline: #000 dashed 1px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contextual links.
|
||||
*/
|
||||
ul.contextual-links {
|
||||
float: right;
|
||||
font-size: 90%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
ul.contextual-links li {
|
||||
border-left: 1px solid #ccc;
|
||||
display: inline;
|
||||
line-height: 100%;
|
||||
list-style: none;
|
||||
margin: 0 0 0 0.3em;
|
||||
padding: 0 0 0 0.6em;
|
||||
}
|
||||
ul.contextual-links li.first {
|
||||
border-left: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
ul.contextual-links li a {
|
||||
text-decoration: none;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// $Id$
|
||||
(function ($) {
|
||||
|
||||
Drupal.contextualLinks = Drupal.contextualLinks || {};
|
||||
|
||||
/**
|
||||
* Attach outline behavior for regions associated with contextual links.
|
||||
*/
|
||||
Drupal.behaviors.contextualLinks = {
|
||||
attach: function (context) {
|
||||
$('ul.contextual-links', context).once('contextual-links', function () {
|
||||
$(this).hover(Drupal.contextualLinks.hover, Drupal.contextualLinks.hoverOut);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables outline for the region contextual links are associated with.
|
||||
*/
|
||||
Drupal.contextualLinks.hover = function () {
|
||||
$(this).addClass('contextual-links-link-active')
|
||||
.closest('.contextual-links-region').addClass('contextual-links-region-active');
|
||||
};
|
||||
|
||||
/**
|
||||
* Disables outline for the region contextual links are associated with.
|
||||
*/
|
||||
Drupal.contextualLinks.hoverOut = function () {
|
||||
$(this).removeClass('contextual-links-link-active')
|
||||
.closest('.contextual-links-region').removeClass('contextual-links-region-active');
|
||||
};
|
||||
|
||||
})(jQuery);
|
|
@ -140,6 +140,7 @@ function hook_block_view($delta = '') {
|
|||
'content' => mymodule_display_block_exciting(),
|
||||
);
|
||||
break;
|
||||
|
||||
case 'amazing':
|
||||
$block = array(
|
||||
'subject' => t('Default title of the amazing block'),
|
||||
|
@ -150,6 +151,79 @@ function hook_block_view($delta = '') {
|
|||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform alterations to the content of a block.
|
||||
*
|
||||
* This hook allows you to modify any data returned by hook_block_view().
|
||||
*
|
||||
* Note that instead of hook_block_view_alter(), which is called for all
|
||||
* blocks, you can also use hook_block_view_MODULE_DELTA_alter() to alter a
|
||||
* specific block.
|
||||
*
|
||||
* @param $data
|
||||
* An array of data, as returned from the hook_block_view() implementation of
|
||||
* the module that defined the block:
|
||||
* - subject: The localized title of the block.
|
||||
* - content: Either a string or a renderable array representing the content
|
||||
* of the block. You should check that the content is an array before trying
|
||||
* to modify parts of the renderable structure.
|
||||
* @param $block
|
||||
* The block object, as loaded from the database, having the main properties:
|
||||
* - module: The name of the module that defined the block.
|
||||
* - delta: The identifier for the block within that module, as defined within
|
||||
* hook_block_info().
|
||||
*
|
||||
* @see hook_block_view_alter()
|
||||
* @see hook_block_view()
|
||||
*/
|
||||
function hook_block_view_alter(&$data, $block) {
|
||||
// Remove the contextual links on all blocks that provide them.
|
||||
if (is_array($data['content']) && isset($data['content']['#contextual_links'])) {
|
||||
unset($data['content']['#contextual_links']);
|
||||
}
|
||||
// Add a theme wrapper function defined by the current module to all blocks
|
||||
// provided by the "somemodule" module.
|
||||
if (is_array($data['content']) && $block->module == 'somemodule') {
|
||||
$data['content']['#theme_wrappers'][] = 'mymodule_special_block';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform alterations to a specific block.
|
||||
*
|
||||
* Modules can implement hook_block_view_MODULE_DELTA_alter() to modify a
|
||||
* specific block, rather than implementing hook_block_view_alter().
|
||||
*
|
||||
* Note that this hook fires before hook_block_view_alter(). Therefore, all
|
||||
* implementations of hook_block_view_MODULE_DELTA_alter() will run before all
|
||||
* implementations of hook_block_view_alter(), regardless of the module order.
|
||||
*
|
||||
* @param $data
|
||||
* An array of data, as returned from the hook_block_view() implementation of
|
||||
* the module that defined the block:
|
||||
* - subject: The localized title of the block.
|
||||
* - content: Either a string or a renderable array representing the content
|
||||
* of the block. You should check that the content is an array before trying
|
||||
* to modify parts of the renderable structure.
|
||||
* @param $block
|
||||
* The block object, as loaded from the database, having the main properties:
|
||||
* - module: The name of the module that defined the block.
|
||||
* - delta: The identifier for the block within that module, as defined within
|
||||
* hook_block_info().
|
||||
*
|
||||
* @see hook_block_view_alter()
|
||||
* @see hook_block_view()
|
||||
*/
|
||||
function hook_block_view_MODULE_DELTA_alter(&$data, $block) {
|
||||
// This code will only run for a specific block. For example, if MODULE_DELTA
|
||||
// in the function definition above is set to "mymodule_somedelta", the code
|
||||
// will only run on the "somedelta" block provided by the "mymodule" module.
|
||||
|
||||
// Change the title of the "somedelta" block provided by the "mymodule"
|
||||
// module.
|
||||
$data['subject'] = t('New title of the block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Act on blocks prior to rendering.
|
||||
*
|
||||
|
|
|
@ -95,15 +95,20 @@ function block_menu() {
|
|||
'type' => MENU_CALLBACK,
|
||||
'file' => 'block.admin.inc',
|
||||
);
|
||||
$items['admin/structure/block/manage/%block/%/configure'] = array(
|
||||
$items['admin/structure/block/manage/%block/%'] = array(
|
||||
'title' => 'Configure block',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('block_admin_configure', 4),
|
||||
'load arguments' => array(5),
|
||||
'access arguments' => array('administer blocks'),
|
||||
'type' => MENU_CALLBACK,
|
||||
'file' => 'block.admin.inc',
|
||||
);
|
||||
$items['admin/structure/block/manage/%block/%/configure'] = array(
|
||||
'title' => 'Configure block',
|
||||
'load arguments' => array(5),
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
);
|
||||
$items['admin/structure/block/manage/%block/%/delete'] = array(
|
||||
'title' => 'Delete block',
|
||||
'page callback' => 'drupal_get_form',
|
||||
|
@ -282,6 +287,12 @@ function _block_get_renderable_array($list = array()) {
|
|||
foreach ($list as $key => $block) {
|
||||
$build[$key] = $block->content;
|
||||
unset($block->content);
|
||||
|
||||
// Add contextual links for this block; skipping the system main block.
|
||||
if ($key != 'system_main') {
|
||||
$build[$key]['#contextual_links']['block'] = menu_contextual_links('admin/structure/block/manage', array($block->module, $block->delta));
|
||||
}
|
||||
|
||||
$build[$key] += array(
|
||||
'#block' => $block,
|
||||
'#weight' => ++$weight,
|
||||
|
@ -785,6 +796,12 @@ function _block_render_blocks($region_blocks) {
|
|||
}
|
||||
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, $block);
|
||||
drupal_alter('block_view', $array, $block);
|
||||
|
||||
if (isset($cid)) {
|
||||
cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
* - $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.
|
||||
* - $contextual_links (array): An array of contextual links for 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 +37,11 @@
|
|||
*/
|
||||
?>
|
||||
<div id="block-<?php print $block->module . '-' . $block->delta; ?>" class="<?php print $classes; ?>"<?php print $attributes; ?>>
|
||||
|
||||
<?php if ($contextual_links): ?>
|
||||
<?php print render($contextual_links); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($block->subject): ?>
|
||||
<h2<?php print $title_attributes; ?>><?php print $block->subject ?></h2>
|
||||
<?php endif;?>
|
||||
|
|
|
@ -185,6 +185,7 @@ function comment_menu() {
|
|||
'access callback' => 'comment_access',
|
||||
'access arguments' => array('edit', 1),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
|
||||
'weight' => 0,
|
||||
);
|
||||
$items['comment/%comment/approve'] = array(
|
||||
|
@ -193,6 +194,7 @@ function comment_menu() {
|
|||
'page arguments' => array(1),
|
||||
'access arguments' => array('administer comments'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
'file' => 'comment.pages.inc',
|
||||
'weight' => 1,
|
||||
);
|
||||
|
@ -202,6 +204,7 @@ function comment_menu() {
|
|||
'page arguments' => array('comment_confirm_delete', 1),
|
||||
'access arguments' => array('administer comments'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
|
||||
'file' => 'comment.admin.inc',
|
||||
'weight' => 2,
|
||||
);
|
||||
|
@ -794,6 +797,8 @@ function comment_build($comment, $node, $build_mode = 'full') {
|
|||
'#node' => $node,
|
||||
'#build_mode' => $build_mode,
|
||||
);
|
||||
// Add contextual links for this comment.
|
||||
$build['#contextual_links']['comment'] = menu_contextual_links('comment', array($comment->cid));
|
||||
|
||||
$prefix = '';
|
||||
$is_threaded = isset($comment->divs) && variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED) == COMMENT_MODE_THREADED;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
* - $status: Comment status. Possible values are:
|
||||
* comment-unpublished, comment-published or comment-preview.
|
||||
* - $title: Linked title.
|
||||
* - $contextual_links (array): An array of contextual links for the comment.
|
||||
* - $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:
|
||||
|
@ -46,6 +47,10 @@
|
|||
*/
|
||||
?>
|
||||
<div class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
|
||||
<?php if ($contextual_links): ?>
|
||||
<?php print render($contextual_links); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php print $picture ?>
|
||||
|
||||
<?php if ($new): ?>
|
||||
|
|
|
@ -1089,7 +1089,7 @@ class LanguageSwitchingFunctionalTest extends DrupalWebTestCase {
|
|||
$this->assertText(t('Languages'), t('Language switcher block found.'));
|
||||
|
||||
// Assert that only the current language is marked as active.
|
||||
list($language_switcher) = $this->xpath('//div[@id="block-locale-language"]');
|
||||
list($language_switcher) = $this->xpath('//div[@id="block-locale-language"]/div[@class="content"]');
|
||||
$links = array(
|
||||
'active' => array(),
|
||||
'inactive' => array(),
|
||||
|
@ -1098,7 +1098,7 @@ class LanguageSwitchingFunctionalTest extends DrupalWebTestCase {
|
|||
'active' => array(),
|
||||
'inactive' => array(),
|
||||
);
|
||||
foreach ($language_switcher->div->ul->li as $link) {
|
||||
foreach ($language_switcher->ul->li as $link) {
|
||||
$classes = explode(" ", (string) $link['class']);
|
||||
list($language) = array_intersect($classes, array('en', 'fr'));
|
||||
if (in_array('active', $classes)) {
|
||||
|
|
|
@ -199,6 +199,21 @@
|
|||
* this alone; the default alphabetical order is usually best.
|
||||
* - "menu_name": Optional. Set this to a custom menu if you don't want your
|
||||
* item to be placed in Navigation.
|
||||
* - "context": (optional) Defines the type of a tab to control its placement
|
||||
* depending on the requested context. By default, all tabs are only
|
||||
* displayed as local tasks when being rendered in a page context. All tabs
|
||||
* that should be accessible as contextual links in page region containers
|
||||
* outside of the parent menu item's primary page context should be
|
||||
* registered using one of the following contexts:
|
||||
* - MENU_CONTEXT_PAGE: (default) The tab is displayed as local task for the
|
||||
* page context only.
|
||||
* - MENU_CONTEXT_INLINE: The tab is displayed as contextual link outside of
|
||||
* the primary page context only.
|
||||
* Contexts can be combined. For example, to display a tab both on a page
|
||||
* and inline, a menu router item may specify:
|
||||
* @code
|
||||
* 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
|
||||
* @endcode
|
||||
* - "tab_parent": For local task menu items, the path of the task's parent
|
||||
* item; defaults to the same path without the last component (e.g., the
|
||||
* default parent for 'admin/people/create' is 'admin/people').
|
||||
|
@ -496,6 +511,47 @@ function hook_menu_local_tasks_alter(&$data, $router_item, $root_path) {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter contextual links before they are rendered.
|
||||
*
|
||||
* This hook is invoked by menu_contextual_links(). The system-determined
|
||||
* contextual links are passed in by reference. Additional links may be added
|
||||
* or existing links can be altered.
|
||||
*
|
||||
* Each contextual link must at least contain:
|
||||
* - title: The localized title of the link.
|
||||
* - href: The system path to link to.
|
||||
* - localized_options: An array of options to pass to url().
|
||||
*
|
||||
* @param $links
|
||||
* An associative array containing contextual links for the given $root_path,
|
||||
* as described above. The array keys are used to build CSS class names for
|
||||
* contextual links and must therefore be unique for each set of contextual
|
||||
* links.
|
||||
* @param $router_item
|
||||
* The menu router item belonging to the $root_path being requested.
|
||||
* @param $root_path
|
||||
* The (parent) path that has been requested to build contextual links for.
|
||||
* This is a normalized path, which means that an originally passed path of
|
||||
* 'node/123' became 'node/%'.
|
||||
*
|
||||
* @see menu_contextual_links()
|
||||
*/
|
||||
function hook_menu_contextual_links_alter(&$links, $router_item, $root_path) {
|
||||
// Add a link to all contextual links for nodes.
|
||||
if ($root_path == 'node/%') {
|
||||
$links['foo'] = array(
|
||||
'title' => t('Do fu'),
|
||||
'href' => 'foo/do',
|
||||
'localized_options' => array(
|
||||
'query' => array(
|
||||
'foo' => 'bar',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
||||
|
|
|
@ -98,6 +98,7 @@ function menu_menu() {
|
|||
'title' => 'List links',
|
||||
'weight' => -10,
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
|
||||
);
|
||||
$items['admin/structure/menu/manage/%menu/add'] = array(
|
||||
'title' => 'Add link',
|
||||
|
@ -113,6 +114,7 @@ function menu_menu() {
|
|||
'page arguments' => array('menu_edit_menu', 'edit', 4),
|
||||
'access arguments' => array('administer menu'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
|
||||
'file' => 'menu.admin.inc',
|
||||
);
|
||||
$items['admin/structure/menu/manage/%menu/delete'] = array(
|
||||
|
@ -423,9 +425,26 @@ function menu_block_view($delta = '') {
|
|||
$menus = menu_get_menus(FALSE);
|
||||
$data['subject'] = check_plain($menus[$delta]);
|
||||
$data['content'] = menu_tree($delta);
|
||||
// Add contextual links for this block.
|
||||
if (!empty($data['content'])) {
|
||||
$data['content']['#contextual_links']['menu'] = menu_contextual_links('admin/structure/menu/manage', array($delta));
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement hook_block_view_alter().
|
||||
*/
|
||||
function menu_block_view_alter(&$data, $block) {
|
||||
// Add contextual links for system menu blocks.
|
||||
if ($block->module == 'system' && !empty($data['content'])) {
|
||||
$system_menus = menu_list_system_menus();
|
||||
if (isset($system_menus[$block->delta])) {
|
||||
$data['content']['#contextual_links']['menu'] = menu_contextual_links('admin/structure/menu/manage', array($block->delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement hook_node_insert().
|
||||
*/
|
||||
|
|
|
@ -1112,6 +1112,9 @@ function node_build($node, $build_mode = 'full') {
|
|||
'#node' => $node,
|
||||
'#build_mode' => $build_mode,
|
||||
);
|
||||
// Add contextual links for this node.
|
||||
$build['#contextual_links']['node'] = menu_contextual_links('node', array($node->nid));
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
|
@ -1806,11 +1809,13 @@ function node_menu() {
|
|||
'page arguments' => array(1),
|
||||
'access callback' => 'node_access',
|
||||
'access arguments' => array('view', 1),
|
||||
'type' => MENU_CALLBACK);
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
$items['node/%node/view'] = array(
|
||||
'title' => 'View',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'weight' => -10);
|
||||
'weight' => -10,
|
||||
);
|
||||
$items['node/%node/edit'] = array(
|
||||
'title' => 'Edit',
|
||||
'page callback' => 'node_page_edit',
|
||||
|
@ -1818,8 +1823,9 @@ function node_menu() {
|
|||
'access callback' => 'node_access',
|
||||
'access arguments' => array('update', 1),
|
||||
'theme callback' => '_node_custom_theme',
|
||||
'weight' => 1,
|
||||
'weight' => 0,
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
|
||||
'file' => 'node.pages.inc',
|
||||
);
|
||||
$items['node/%node/delete'] = array(
|
||||
|
@ -1829,7 +1835,8 @@ function node_menu() {
|
|||
'access callback' => 'node_access',
|
||||
'access arguments' => array('delete', 1),
|
||||
'weight' => 1,
|
||||
'type' => MENU_CALLBACK,
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
'file' => 'node.pages.inc',
|
||||
);
|
||||
$items['node/%node/revisions'] = array(
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
* - $node_url: Direct url of the current node.
|
||||
* - $terms: the themed list of taxonomy term links output from theme_links().
|
||||
* - $display_submitted: whether submission information should be displayed.
|
||||
* - $contextual_links (array): An array of contextual links for the node.
|
||||
* - $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:
|
||||
|
@ -74,6 +75,10 @@
|
|||
|
||||
<?php print $user_picture; ?>
|
||||
|
||||
<?php if (!$page && $contextual_links): ?>
|
||||
<?php print render($contextual_links); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!$page): ?>
|
||||
<h2<?php print $title_attributes; ?>><a href="<?php print $node_url; ?>"><?php print $node_title; ?></a></h2>
|
||||
<?php endif; ?>
|
||||
|
|
|
@ -1026,6 +1026,12 @@ function system_schema() {
|
|||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'context' => array(
|
||||
'description' => 'Only for local tasks (tabs) - the context of a local task to control its placement.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'tab_parent' => array(
|
||||
'description' => 'Only for local tasks (tabs) - the router path of the parent page (which may also be a local task).',
|
||||
'type' => 'varchar',
|
||||
|
@ -2757,6 +2763,18 @@ function system_update_7042() {
|
|||
db_add_unique_key('url_alias', 'alias_language_pid', array('alias', 'language', 'pid'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a 'context' field to {menu_router} to control contextual placement of local tasks.
|
||||
*/
|
||||
function system_update_7043() {
|
||||
db_add_field('menu_router', 'context', array(
|
||||
'description' => 'Only for local tasks (tabs) - the context of a local task to control its placement.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "defgroup updates-6.x-to-7.x"
|
||||
* The next series of updates should start at 8000.
|
||||
|
|
|
@ -3488,3 +3488,83 @@ function system_archiver_info() {
|
|||
function theme_confirm_form($variables) {
|
||||
return drupal_render_children($variables['form']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template variable preprocessor for contextual links.
|
||||
*/
|
||||
function system_preprocess(&$variables, $hook) {
|
||||
static $hooks;
|
||||
|
||||
if (!isset($hooks)) {
|
||||
$hooks = theme_get_registry();
|
||||
}
|
||||
|
||||
// Initialize contextual links template variable.
|
||||
$variables['contextual_links'] = array();
|
||||
|
||||
// Determine the primary theme function argument.
|
||||
$keys = array_keys($hooks[$hook]['arguments']);
|
||||
$key = $keys[0];
|
||||
if (isset($variables[$key])) {
|
||||
$element = $variables[$key];
|
||||
}
|
||||
|
||||
if (isset($element) && is_array($element) && isset($element['#contextual_links'])) {
|
||||
$variables['contextual_links'] = system_build_contextual_links($element);
|
||||
if (!empty($variables['contextual_links'])) {
|
||||
$variables['classes_array'][] = 'contextual-links-region';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a renderable array for contextual links.
|
||||
*
|
||||
* @param $element
|
||||
* A renderable array containing a #contextual_links property.
|
||||
*
|
||||
* @return
|
||||
* A renderable array representing contextual links.
|
||||
*/
|
||||
function system_build_contextual_links($element) {
|
||||
static $destination;
|
||||
|
||||
// Transform contextual links into parameters suitable for theme_link().
|
||||
$items = call_user_func_array('array_merge_recursive', $element['#contextual_links']);
|
||||
$build = array();
|
||||
if (empty($items)) {
|
||||
return $build;
|
||||
}
|
||||
|
||||
if (!isset($destination)) {
|
||||
$destination = drupal_get_destination();
|
||||
}
|
||||
|
||||
$links = array();
|
||||
foreach ($items as $class => $item) {
|
||||
$class = drupal_html_class($class);
|
||||
$links[$class] = array(
|
||||
'title' => $item['title'],
|
||||
'href' => $item['href'],
|
||||
);
|
||||
// @todo theme_links() should *really* use the same parameters as l()...
|
||||
if (!isset($item['localized_options']['query'])) {
|
||||
$item['localized_options']['query'] = array();
|
||||
}
|
||||
$item['localized_options']['query'] += $destination;
|
||||
$links[$class] += $item['localized_options'];
|
||||
}
|
||||
if ($links) {
|
||||
$build = array(
|
||||
'#theme' => 'links',
|
||||
'#links' => $links,
|
||||
'#attributes' => array('class' => array('contextual-links')),
|
||||
'#attached' => array(
|
||||
'js' => array('misc/contextual_links.js'),
|
||||
'css' => array('misc/contextual_links.css'),
|
||||
),
|
||||
);
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
?>
|
||||
<div id="block-<?php print $block->module . '-' . $block->delta; ?>" class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
|
||||
|
||||
<?php if ($contextual_links): ?>
|
||||
<?php print render($contextual_links); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($block->subject)): ?>
|
||||
<h2 class="title"<?php print $title_attributes; ?>><?php print $block->subject ?></h2>
|
||||
<?php endif;?>
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
|
||||
<div class="clearfix">
|
||||
|
||||
<?php if ($contextual_links): ?>
|
||||
<?php print render($contextual_links); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<span class="submitted"><?php print $created; ?> — <?php print $author; ?></span>
|
||||
|
||||
<?php if ($new) : ?>
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
?>
|
||||
<div id="node-<?php print $node->nid; ?>" class="<?php print $classes; ?>"<?php print $attributes; ?>>
|
||||
|
||||
<?php if (!$page && $contextual_links): ?>
|
||||
<?php print render($contextual_links); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php print $user_picture; ?>
|
||||
|
||||
<?php if (!$page): ?>
|
||||
|
|
|
@ -650,8 +650,8 @@ ul.secondary li.active a {
|
|||
*/
|
||||
.node {
|
||||
border-bottom: 1px solid #e9eff3;
|
||||
margin: 0 -26px 1.5em;
|
||||
padding: 1.5em 26px;
|
||||
margin: 0 -16px 1.5em;
|
||||
padding: 1.5em 16px;
|
||||
}
|
||||
|
||||
ul.links li, ul.inline li {
|
||||
|
@ -808,6 +808,17 @@ tr.even td.menu-disabled {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contextual links.
|
||||
*/
|
||||
.contextual-links-region-active {
|
||||
outline: #027AC6 dashed 1px;
|
||||
}
|
||||
.block ul.contextual-links {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapsible fieldsets
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue