Issue #2084463 by dawehner, pwolanin, David_Rothstein, tim.plunkett, Xano: Convert contextual links to a plugin system similar to local tasks/actions.
parent
952f57a111
commit
0d263c64e9
|
@ -197,6 +197,9 @@ services:
|
|||
class: Drupal\Core\Menu\LocalTaskManager
|
||||
arguments: ['@controller_resolver', '@request', '@router.route_provider', '@module_handler', '@cache.cache', '@language_manager', '@access_manager', '@current_user']
|
||||
scope: request
|
||||
plugin.manager.menu.contextual_link:
|
||||
class: Drupal\Core\Menu\ContextualLinkManager
|
||||
arguments: ['@controller_resolver', '@module_handler', '@cache.cache', '@language_manager', '@access_manager', '@current_user']
|
||||
request:
|
||||
class: Symfony\Component\HttpFoundation\Request
|
||||
# @TODO the synthetic setting must be uncommented whenever drupal_session_initialize()
|
||||
|
|
|
@ -250,11 +250,6 @@ const MENU_CONTEXT_NONE = 0x0000;
|
|||
*/
|
||||
const MENU_CONTEXT_PAGE = 0x0001;
|
||||
|
||||
/**
|
||||
* Internal menu flag: Local task should be displayed inline.
|
||||
*/
|
||||
const MENU_CONTEXT_INLINE = 0x0002;
|
||||
|
||||
/**
|
||||
* @} End of "defgroup menu_context_types".
|
||||
*/
|
||||
|
@ -2055,7 +2050,6 @@ function _menu_get_legacy_tasks($router_item, &$data, &$root_path) {
|
|||
$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();
|
||||
|
@ -2223,114 +2217,6 @@ function _menu_get_legacy_tasks($router_item, &$data, &$root_path) {
|
|||
$data['tabs'] += $tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves contextual links for a path 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
|
||||
* MENU_CONTEXT_INLINE are taken into account.
|
||||
*
|
||||
* 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 $module
|
||||
* The name of the implementing module. This is used to prefix the key for
|
||||
* each contextual link, which is transformed into a CSS class during
|
||||
* rendering by theme_links(). For example, if $module is 'block' and the
|
||||
* retrieved local task path argument is 'edit', then the resulting CSS class
|
||||
* will be 'block-edit'.
|
||||
* @param $parent_path
|
||||
* The static menu router path of the object to retrieve local tasks for, for
|
||||
* example 'node' or 'admin/structure/block/manage'.
|
||||
* @param $args
|
||||
* A list of dynamic path arguments to append to $parent_path to form the
|
||||
* fully-qualified menu router path; for example, array(123) for a certain
|
||||
* node or array('system', 'tools') for a certain block.
|
||||
*
|
||||
* @return
|
||||
* A list of menu router items that are local tasks for the passed-in path.
|
||||
*
|
||||
* @see contextual_links_preprocess()
|
||||
* @see hook_menu()
|
||||
*/
|
||||
function menu_contextual_links($module, $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]) && strpos($parent_path, '%') !== FALSE) {
|
||||
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_NONE, '<>')
|
||||
->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;
|
||||
}
|
||||
|
||||
// If this item is a default local task, rewrite the href to link to its
|
||||
// parent item.
|
||||
if ($item['type'] == MENU_DEFAULT_LOCAL_TASK) {
|
||||
$item['href'] = $item['tab_parent_href'];
|
||||
}
|
||||
|
||||
// All contextual links are keyed by the actual "task" path argument,
|
||||
// prefixed with the name of the implementing module.
|
||||
$links[$module . '-' . $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.
|
||||
*/
|
||||
|
|
|
@ -1693,8 +1693,12 @@ function theme_links($variables) {
|
|||
$class[] = 'last';
|
||||
}
|
||||
|
||||
$link += array(
|
||||
'href' => NULL,
|
||||
);
|
||||
|
||||
// Handle links.
|
||||
if (isset($link['href'])) {
|
||||
if (isset($link['href']) || isset($link['route_name'])) {
|
||||
$is_current_path = ($link['href'] == current_path() || ($link['href'] == '<front>' && drupal_is_front_page()));
|
||||
$is_current_language = (empty($link['language']) || $link['language']->id == $language_url->id);
|
||||
if ($is_current_path && $is_current_language) {
|
||||
|
@ -1715,8 +1719,17 @@ function theme_links($variables) {
|
|||
$item = drupal_render($link_element);
|
||||
}
|
||||
else {
|
||||
// Pass in $link as $options, they share the same keys.
|
||||
$item = l($link['title'], $link['href'], $link);
|
||||
// @todo theme_links() should *really* use the same parameters as l(),
|
||||
// and just take an array of '#type' => 'link' elements, see
|
||||
// https://drupal.org/node/2102777.
|
||||
// Pass in $link as $options, as they share the same keys.
|
||||
if (isset($link['href'])) {
|
||||
$item = l($link['title'], $link['href'], $link);
|
||||
}
|
||||
else {
|
||||
$link += array('route_parameters' => array());
|
||||
$item = \Drupal::l($link['title'], $link['route_name'], $link['route_parameters'], $link);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle title-only text items.
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Menu\ContextualLinkDefault.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Menu;
|
||||
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
|
||||
/**
|
||||
* Provides a common base implementation of a contextual link.
|
||||
*/
|
||||
class ContextualLinkDefault extends PluginBase implements ContextualLinkInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
$options = array();
|
||||
if (!empty($this->pluginDefinition['title_context'])) {
|
||||
$options['context'] = $this->pluginDefinition['title_context'];
|
||||
}
|
||||
return $this->t($this->pluginDefinition['title'], array(), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRouteName() {
|
||||
return $this->pluginDefinition['route_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getGroup() {
|
||||
return $this->pluginDefinition['group'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOptions() {
|
||||
return $this->pluginDefinition['options'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWeight() {
|
||||
return $this->pluginDefinition['weight'];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Menu\ContextualLinkInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Menu;
|
||||
|
||||
/**
|
||||
* Defines a contextual link plugin.
|
||||
*/
|
||||
interface ContextualLinkInterface {
|
||||
|
||||
/**
|
||||
* Returns the localized title to be shown for this contextual link.
|
||||
*
|
||||
* Subclasses may add optional arguments like NodeInterface $node = NULL that
|
||||
* will be supplied by the ControllerResolver.
|
||||
*
|
||||
* @return string
|
||||
* The title to be shown for this action.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\ContextualLinksManager::getTitle()
|
||||
*/
|
||||
public function getTitle();
|
||||
|
||||
/**
|
||||
* Returns the route name of the contextual link.
|
||||
*
|
||||
* @return string
|
||||
* The name of the route this contextual link links to.
|
||||
*/
|
||||
public function getRouteName();
|
||||
|
||||
/**
|
||||
* Returns the group this contextual link should be rendered in.
|
||||
*
|
||||
* A contextual link group is a set of contextual links that are displayed
|
||||
* together on a certain page. For example, the 'block' group displays all
|
||||
* links related to the block, such as the block instance edit link as well as
|
||||
* the views edit link, if it is a view block.
|
||||
*
|
||||
* @return string
|
||||
* The contextual links group name.
|
||||
*/
|
||||
public function getGroup();
|
||||
|
||||
/**
|
||||
* Returns the link options passed to the link generator.
|
||||
*
|
||||
* @return array
|
||||
* The options as expected by LinkGeneratorInterface::generate()
|
||||
*
|
||||
* @see \Drupal\Core\Utility\LinkGeneratorInterface::generate()
|
||||
*/
|
||||
public function getOptions();
|
||||
|
||||
/**
|
||||
* Returns the weight of the contextual link.
|
||||
*
|
||||
* The contextual links in one group are sorted by weight for display.
|
||||
*
|
||||
* @return int
|
||||
* The weight as positive/negative integer.
|
||||
*/
|
||||
public function getWeight();
|
||||
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Menu\ContextualLinkManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Menu;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\Core\Access\AccessManager;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Controller\ControllerResolverInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\LanguageManager;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
|
||||
use Drupal\Core\Plugin\Discovery\YamlDiscovery;
|
||||
use Drupal\Core\Plugin\Factory\ContainerFactory;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines a contextual link plugin manager to deal with contextual links.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\ContextualLinkInterface
|
||||
*/
|
||||
class ContextualLinkManager extends DefaultPluginManager implements ContextualLinkManagerInterface {
|
||||
|
||||
/**
|
||||
* Provides default values for a contextual link definition.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaults = array(
|
||||
// (required) The name of the route to link to.
|
||||
'route_name' => '',
|
||||
// (required) The contextual links group.
|
||||
'group' => '',
|
||||
// The static title text for the link.
|
||||
'title' => '',
|
||||
// The default link options.
|
||||
'options' => array(),
|
||||
// The weight of the link.
|
||||
'weight' => NULL,
|
||||
// Default class for contextual link implementations.
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
// The plugin id. Set by the plugin system based on the top-level YAML key.
|
||||
'id' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
* A controller resolver object.
|
||||
*
|
||||
* @var \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface
|
||||
*/
|
||||
protected $controllerResolver;
|
||||
|
||||
/**
|
||||
* The access manager.
|
||||
*
|
||||
* @var \Drupal\Core\Access\AccessManager
|
||||
*/
|
||||
protected $accessManager;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* A static cache of all the contextual link plugins by group name.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $pluginsByGroup;
|
||||
|
||||
/**
|
||||
* Constructs a new ContextualLinkManager instance.
|
||||
*
|
||||
* @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
|
||||
* The controller resolver.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* The cache backend.
|
||||
* @param \Drupal\Core\Language\LanguageManager $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Access\AccessManager $access_manager
|
||||
* The access manager.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(ControllerResolverInterface $controller_resolver, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, LanguageManager $language_manager, AccessManager $access_manager, AccountInterface $account) {
|
||||
$this->discovery = new YamlDiscovery('contextual_links', $module_handler->getModuleDirectories());
|
||||
$this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
|
||||
$this->factory = new ContainerFactory($this);
|
||||
|
||||
$this->controllerResolver = $controller_resolver;
|
||||
$this->accessManager = $access_manager;
|
||||
$this->account = $account;
|
||||
$this->alterInfo($module_handler, 'contextual_links_plugins');
|
||||
$this->setCacheBackend($cache_backend, $language_manager, 'contextual_links_plugins');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processDefinition(&$definition, $plugin_id) {
|
||||
parent::processDefinition($definition, $plugin_id);
|
||||
|
||||
// If there is no route name, this is a broken definition.
|
||||
if (empty($definition['route_name'])) {
|
||||
throw new PluginException(sprintf('Contextual link plugin (%s) definition must include "route_name".', $plugin_id));
|
||||
}
|
||||
// If there is no group name, this is a broken definition.
|
||||
if (empty($definition['group'])) {
|
||||
throw new PluginException(sprintf('Contextual link plugin (%s) definition must include "group".', $plugin_id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContextualLinkPluginsByGroup($group_name) {
|
||||
if (isset($this->pluginsByGroup[$group_name])) {
|
||||
$contextual_links = $this->pluginsByGroup[$group_name];
|
||||
}
|
||||
elseif ($cache = $this->cacheBackend->get($this->cacheKey . ':' . $group_name)) {
|
||||
$contextual_links = $cache->data;
|
||||
$this->pluginsByGroup[$group_name] = $contextual_links;
|
||||
}
|
||||
else {
|
||||
$contextual_links = array();
|
||||
foreach ($this->getDefinitions() as $plugin_id => $plugin_definition) {
|
||||
if ($plugin_definition['group'] == $group_name) {
|
||||
$contextual_links[$plugin_id] = $plugin_definition;
|
||||
}
|
||||
}
|
||||
$this->cacheBackend->set($this->cacheKey . ':' . $group_name, $contextual_links);
|
||||
$this->pluginsByGroup[$group_name] = $contextual_links;
|
||||
}
|
||||
return $contextual_links;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContextualLinksArrayByGroup($group_name, array $route_parameters, array $metadata = array()) {
|
||||
$links = array();
|
||||
foreach ($this->getContextualLinkPluginsByGroup($group_name) as $plugin_id => $plugin_definition) {
|
||||
/** @var $plugin \Drupal\Core\Menu\ContextualLinkInterface */
|
||||
$plugin = $this->createInstance($plugin_id);
|
||||
$route_name = $plugin->getRouteName();
|
||||
|
||||
// Check access.
|
||||
if (!$this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$links[$plugin_id] = array(
|
||||
'route_name' => $route_name,
|
||||
'route_parameters' => $route_parameters,
|
||||
'title' => $plugin->getTitle(),
|
||||
'weight' => $plugin->getWeight(),
|
||||
'localized_options' => $plugin->getOptions(),
|
||||
'metadata' => $metadata,
|
||||
);
|
||||
}
|
||||
|
||||
$this->moduleHandler->alter('contextual_links', $links, $group_name, $route_parameters);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Menu\ContextualLinkManagerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Menu;
|
||||
|
||||
/**
|
||||
* Provides an object which returns the available contextual links.
|
||||
*/
|
||||
interface ContextualLinkManagerInterface {
|
||||
|
||||
/**
|
||||
* Gets the contextual link plugins by contextual link group.
|
||||
*
|
||||
* @param string $group_name
|
||||
* The group name.
|
||||
*
|
||||
* @return array
|
||||
* A list of contextual links plugin definitions.
|
||||
*/
|
||||
public function getContextualLinkPluginsByGroup($group_name);
|
||||
|
||||
/**
|
||||
* Gets the contextual links prepared as expected by theme_links().
|
||||
*
|
||||
* @param string $group_name
|
||||
* The group name.
|
||||
* @param array $route_parameters
|
||||
* The incoming route parameters. The route parameters need to have the same
|
||||
* name on all contextual link routes, e.g. you cannot use 'node' and
|
||||
* 'entity' in parallel.
|
||||
* @param array $metadata
|
||||
* Additional metadata of contextual links, like the position (optional).
|
||||
*
|
||||
* @return array
|
||||
* An array of link information, keyed by the plugin ID. Each entry is an
|
||||
* associative array with the following keys:
|
||||
* - route_name: The route name to link to.
|
||||
* - route_parameters: The route parameters for the contextual link.
|
||||
* - title: The title of the contextual link.
|
||||
* - weight: The weight of the contextual link.
|
||||
* - localized_options: The options of the link, which will be passed
|
||||
* to the link generator.
|
||||
* - metadata: The array of additional metadata that was passed in.
|
||||
*/
|
||||
public function getContextualLinksArrayByGroup($group_name, array $route_parameters, array $metadata = array());
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
block_configure:
|
||||
title: 'Configure block'
|
||||
route_name: 'block.admin_edit'
|
||||
group: 'block'
|
|
@ -104,11 +104,6 @@ function block_menu() {
|
|||
'title' => 'Configure block',
|
||||
'route_name' => 'block.admin_edit',
|
||||
);
|
||||
$items['admin/structure/block/manage/%block/configure'] = array(
|
||||
'title' => 'Configure block',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
);
|
||||
$items['admin/structure/block/add/%/%'] = array(
|
||||
'title' => 'Place block',
|
||||
'type' => MENU_VISIBLE_IN_BREADCRUMB,
|
||||
|
@ -268,7 +263,9 @@ function _block_get_renderable_region($list = array()) {
|
|||
// to perform contextual actions on the help block, and the links needlessly
|
||||
// draw attention on it.
|
||||
if (isset($build[$key]) && !in_array($block->get('plugin'), array('system_help_block', 'system_main_block'))) {
|
||||
$build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($key));
|
||||
$build[$key]['#contextual_links']['block'] = array(
|
||||
'route_parameters' => array('block' => $key),
|
||||
);
|
||||
|
||||
// If there are any nested contextual links, move them to the top level.
|
||||
if (isset($build[$key]['content']['#contextual_links'])) {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
custom_block.block_edit:
|
||||
title: 'Edit'
|
||||
group: custom_block
|
||||
route_name: 'custom_block.edit'
|
||||
|
||||
custom_block.block_delete:
|
||||
title: 'Delete'
|
||||
group: custom_block
|
||||
route_name: 'custom_block.delete'
|
||||
weight: 1
|
|
@ -80,24 +80,6 @@ function custom_block_menu() {
|
|||
'route_name' => 'custom_block.add_page',
|
||||
);
|
||||
|
||||
// There has to be a base-item in order for contextual links to work.
|
||||
$items['block/%custom_block'] = array(
|
||||
'title' => 'Edit',
|
||||
'route_name' => 'custom_block.edit',
|
||||
);
|
||||
$items['block/%custom_block/edit'] = array(
|
||||
'title' => 'Edit',
|
||||
'weight' => 0,
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
);
|
||||
$items['block/%custom_block/delete'] = array(
|
||||
'title' => 'Delete',
|
||||
'weight' => 1,
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
'route_name' => 'custom_block.delete',
|
||||
);
|
||||
return $items;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,9 @@ class CustomBlockViewBuilder extends EntityViewBuilder {
|
|||
parent::alterBuild($build, $entity, $display, $view_mode, $langcode);
|
||||
// Add contextual links for this custom block.
|
||||
if (!empty($entity->id->value) && $view_mode == 'full') {
|
||||
$build['#contextual_links']['custom_block'] = array('block', array($entity->id()));
|
||||
$build['#contextual_links']['custom_block'] = array(
|
||||
'route_parameters' => array('custom_block' => $entity->id()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ class DisplayBlockTest extends ViewTestBase {
|
|||
$block = $this->drupalPlaceBlock('views_block:test_view_block-block_1');
|
||||
$this->drupalGet('test-page');
|
||||
|
||||
$id = 'block:admin/structure/block/manage:' . $block->id() . ':|views_ui:admin/structure/views/view:test_view_block:location=block&name=test_view_block&display_id=block_1';
|
||||
$id = 'block:block=' . $block->id() . ':|views_ui_edit:view=test_view_block:location=block&name=test_view_block&display_id=block_1';
|
||||
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:assertContextualLinkPlaceHolder()
|
||||
$this->assertRaw('<div data-contextual-id="'. $id . '"></div>', format_string('Contextual link placeholder with id @id exists.', array('@id' => $id)));
|
||||
|
||||
|
@ -240,7 +240,7 @@ class DisplayBlockTest extends ViewTestBase {
|
|||
$response = $this->drupalPost('contextual/render', 'application/json', $post, array('query' => array('destination' => 'test-page')));
|
||||
$this->assertResponse(200);
|
||||
$json = drupal_json_decode($response);
|
||||
$this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="block-configure odd first"><a href="' . base_path() . 'admin/structure/block/manage/' . $block->id() . '?destination=test-page">Configure block</a></li><li class="views-ui-edit even last"><a href="' . base_path() . 'admin/structure/views/view/test_view_block/edit/block_1?destination=test-page">Edit view</a></li></ul>');
|
||||
$this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="block-configure odd first"><a href="' . base_path() . 'admin/structure/block/manage/' . $block->id() . '?destination=test-page">Configure block</a></li><li class="views-uiedit even last"><a href="' . base_path() . 'admin/structure/views/view/test_view_block/edit/block_1?destination=test-page">Edit view</a></li></ul>');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
content_translation.contextual_links:
|
||||
derivative: 'Drupal\content_translation\Plugin\Derivative\ContentTranslationContextualLinks'
|
||||
weight: 2
|
|
@ -180,7 +180,7 @@ function content_translation_menu() {
|
|||
'title' => 'Translate',
|
||||
'route_name' => "content_translation.translation_overview_$entity_type",
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
|
||||
'context' => MENU_CONTEXT_PAGE,
|
||||
'weight' => 2,
|
||||
) + $item;
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\content_translation\Plugin\Derivative\ContentTranslationContextualLinks.
|
||||
*/
|
||||
|
||||
namespace Drupal\content_translation\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DerivativeBase;
|
||||
use Drupal\Core\Entity\EntityManager;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides dynamic contextual links for content translation.
|
||||
*
|
||||
* @see \Drupal\content_translation\Plugin\Menu\ContextualLink\ContentTranslationContextualLinks
|
||||
*/
|
||||
class ContentTranslationContextualLinks extends DerivativeBase implements ContainerDerivativeInterface {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManager
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a new ContentTranslationContextualLinks.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManager $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManager $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('entity.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions(array $base_plugin_definition) {
|
||||
// Create contextual links for all possible entity types.
|
||||
foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
|
||||
if ($entity_info['translatable'] && isset($entity_info['translation'])) {
|
||||
$this->derivatives[$entity_type]['title'] = t('Translate');
|
||||
$this->derivatives[$entity_type]['route_name'] = "content_translation.translation_overview_$entity_type";
|
||||
$this->derivatives[$entity_type]['group'] = $entity_type;
|
||||
}
|
||||
}
|
||||
return parent::getDerivativeDefinitions($base_plugin_definition);
|
||||
}
|
||||
|
||||
}
|
|
@ -22,10 +22,12 @@
|
|||
* A renderable array representing the contextual links.
|
||||
* @param $items
|
||||
* An associative array containing the original contextual link items, as
|
||||
* generated by menu_contextual_links(), which were used to build
|
||||
* $element['#links'].
|
||||
* generated by
|
||||
* \Drupal\Core\Menu\ContextualLinkManagerInterface::getContextualLinksArrayByGroup(),
|
||||
* which were used to build $element['#links'].
|
||||
*
|
||||
* @see hook_menu_contextual_links_alter()
|
||||
* @see hook_contextual_links_alter()
|
||||
* @see hook_contextual_links_plugins_alter()
|
||||
* @see contextual_pre_render_links()
|
||||
* @see contextual_element_info()
|
||||
*/
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
use Drupal\Component\Utility\Url;
|
||||
|
||||
/**
|
||||
* @file
|
||||
|
@ -237,27 +238,40 @@ function contextual_pre_render_placeholder($element) {
|
|||
*
|
||||
* @param $element
|
||||
* A renderable array containing a #contextual_links property, which is a
|
||||
* keyed array. Each key is the name of the implementing module, and each
|
||||
* value is an array that forms the function arguments for
|
||||
* menu_contextual_links(). For example:
|
||||
* keyed array. Each key is the name of the group of contextual links to
|
||||
* render (based on the 'group' key in the *.contextual_links.yml files for
|
||||
* all enabled modules). The value contains an associative array containing
|
||||
* the following keys:
|
||||
* - route_parameters: The route parameters passed to the url generator.
|
||||
* - metadata: Any additional data needed in order to alter the link.
|
||||
* @code
|
||||
* array('#contextual_links' => array(
|
||||
* 'block' => array('admin/structure/block/manage', array('system', 'menu-tools')),
|
||||
* 'menu' => array('admin/structure/menu/manage', array('tools')),
|
||||
* 'block' => array(
|
||||
* 'route_parameters' => array('block' => 'system.menu-tools'),
|
||||
* ),
|
||||
* 'menu' => array(
|
||||
* 'route_parameters' => array('menu' => 'tools'),
|
||||
* ),
|
||||
* ))
|
||||
* @endcode
|
||||
*
|
||||
* @return
|
||||
* A renderable array representing contextual links.
|
||||
*
|
||||
* @see menu_contextual_links()
|
||||
* @see contextual_element_info()
|
||||
*/
|
||||
function contextual_pre_render_links($element) {
|
||||
// Retrieve contextual menu links.
|
||||
$items = array();
|
||||
foreach ($element['#contextual_links'] as $module => $args) {
|
||||
$items += menu_contextual_links($module, $args[0], $args[1]);
|
||||
|
||||
/** @var $contextual_links_manager \Drupal\Core\Menu\ContextualLinkManager */
|
||||
$contextual_links_manager = \Drupal::service('plugin.manager.menu.contextual_link');
|
||||
foreach ($element['#contextual_links'] as $group => $args) {
|
||||
$args += array(
|
||||
'route_parameters' => array(),
|
||||
'metadata' => array(),
|
||||
);
|
||||
$items += $contextual_links_manager->getContextualLinksArrayByGroup($group, $args['route_parameters'], $args['metadata']);
|
||||
}
|
||||
|
||||
// Transform contextual links into parameters suitable for theme_links().
|
||||
|
@ -266,9 +280,9 @@ function contextual_pre_render_links($element) {
|
|||
$class = drupal_html_class($class);
|
||||
$links[$class] = array(
|
||||
'title' => $item['title'],
|
||||
'href' => $item['href'],
|
||||
'route_name' => isset($item['route_name']) ? $item['route_name'] : '',
|
||||
'route_parameters' => isset($item['route_parameters']) ? $item['route_parameters'] : array(),
|
||||
);
|
||||
// @todo theme_links() should *really* use the same parameters as l().
|
||||
$item['localized_options'] += array('query' => array());
|
||||
$item['localized_options']['query'] += drupal_get_destination();
|
||||
$links[$class] += $item['localized_options'];
|
||||
|
@ -293,7 +307,7 @@ function contextual_pre_render_links($element) {
|
|||
*/
|
||||
function contextual_contextual_links_view_alter(&$element, $items) {
|
||||
if (isset($element['#contextual_links']['contextual'])) {
|
||||
$encoded_links = $element['#contextual_links']['contextual'][2]['contextual-views-field-links'];
|
||||
$encoded_links = $element['#contextual_links']['contextual']['metadata']['contextual-views-field-links'];
|
||||
$element['#links'] = drupal_json_decode(rawurldecode($encoded_links));
|
||||
}
|
||||
}
|
||||
|
@ -302,15 +316,14 @@ function contextual_contextual_links_view_alter(&$element, $items) {
|
|||
* Serializes #contextual_links property value array to a string.
|
||||
*
|
||||
* Examples:
|
||||
* - node:node:1:
|
||||
* - views_ui:admin/structure/views/view:frontpage:location=page&view_name=frontpage&view_display_id=page_1
|
||||
* - menu:admin/structure/menu/manage:tools:|block:admin/structure/block/manage:bartik.tools:
|
||||
* - node:node=1:
|
||||
* - views_ui_edit:view=frontpage:location=page&view_name=frontpage&view_display_id=page_1
|
||||
* - menu:menu=tools:|block:block=bartik.tools:
|
||||
*
|
||||
* So, expressed in a pattern:
|
||||
* <module name>:<parent path>:<path args>:<options>
|
||||
* <group>:<route parameters>:<metadata>
|
||||
*
|
||||
* The (dynamic) path args are joined with slashes. The options are encoded as a
|
||||
* query string.
|
||||
* The route parameters and options are encoded as query strings.
|
||||
*
|
||||
* @param array $contextual_links
|
||||
* The $element['#contextual_links'] value for some render element.
|
||||
|
@ -320,18 +333,13 @@ function contextual_contextual_links_view_alter(&$element, $items) {
|
|||
* use in a data- attribute.
|
||||
*/
|
||||
function _contextual_links_to_id($contextual_links) {
|
||||
$id = '';
|
||||
foreach ($contextual_links as $module => $args) {
|
||||
$parent_path = $args[0];
|
||||
$path_args = implode('/', $args[1]);
|
||||
$metadata = drupal_http_build_query((isset($args[2])) ? $args[2] : array());
|
||||
|
||||
if (drupal_strlen($id) > 0) {
|
||||
$id .= '|';
|
||||
}
|
||||
$id .= $module . ':' . $parent_path . ':' . $path_args . ':' . $metadata;
|
||||
$ids = array();
|
||||
foreach ($contextual_links as $group => $args) {
|
||||
$route_parameters = Url::buildQuery($args['route_parameters']);
|
||||
$metadata = Url::buildQuery((isset($args['metadata'])) ? $args['metadata'] : array());
|
||||
$ids[] = "{$group}:{$route_parameters}:{$metadata}";
|
||||
}
|
||||
return $id;
|
||||
return implode('|', $ids);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -349,11 +357,14 @@ function _contextual_id_to_links($id) {
|
|||
$contextual_links = array();
|
||||
$contexts = explode('|', $id);
|
||||
foreach ($contexts as $context) {
|
||||
list($module, $parent_path, $path_args, $metadata_raw) = explode(':', $context);
|
||||
$path_args = explode('/', $path_args);
|
||||
list($group, $route_parameters_raw, $metadata_raw) = explode(':', $context);
|
||||
parse_str($route_parameters_raw, $route_parameters);
|
||||
$metadata = array();
|
||||
parse_str($metadata_raw, $metadata);
|
||||
$contextual_links[$module] = array($parent_path, $path_args, $metadata);
|
||||
$contextual_links[$group] = array(
|
||||
'route_parameters' => $route_parameters,
|
||||
'metadata' => $metadata,
|
||||
);
|
||||
}
|
||||
return $contextual_links;
|
||||
}
|
||||
|
|
|
@ -60,10 +60,10 @@ class ContextualDynamicContextTest extends WebTestBase {
|
|||
// Now, on the front page, all article nodes should have contextual links
|
||||
// placeholders, as should the view that contains them.
|
||||
$ids = array(
|
||||
'node:node:' . $node1->id() . ':',
|
||||
'node:node:' . $node2->id() . ':',
|
||||
'node:node:' . $node3->id() . ':',
|
||||
'views_ui:admin/structure/views/view:frontpage:location=page&name=frontpage&display_id=page_1',
|
||||
'node:node=' . $node1->id() . ':',
|
||||
'node:node=' . $node2->id() . ':',
|
||||
'node:node=' . $node3->id() . ':',
|
||||
'views_ui_edit:view=frontpage:location=page&name=frontpage&display_id=page_1',
|
||||
);
|
||||
|
||||
// Editor user: can access contextual links and can edit articles.
|
||||
|
@ -77,9 +77,9 @@ class ContextualDynamicContextTest extends WebTestBase {
|
|||
$response = $this->renderContextualLinks($ids, 'node');
|
||||
$this->assertResponse(200);
|
||||
$json = drupal_json_decode($response);
|
||||
$this->assertIdentical($json[$ids[0]], '<ul class="contextual-links"><li class="node-edit odd first last"><a href="' . base_path() . 'node/1/edit?destination=node">Edit</a></li></ul>');
|
||||
$this->assertIdentical($json[$ids[0]], '<ul class="contextual-links"><li class="nodepage-edit odd first last"><a href="' . base_path() . 'node/1/edit?destination=node">Edit</a></li></ul>');
|
||||
$this->assertIdentical($json[$ids[1]], '');
|
||||
$this->assertIdentical($json[$ids[2]], '<ul class="contextual-links"><li class="node-edit odd first last"><a href="' . base_path() . 'node/3/edit?destination=node">Edit</a></li></ul>');
|
||||
$this->assertIdentical($json[$ids[2]], '<ul class="contextual-links"><li class="nodepage-edit odd first last"><a href="' . base_path() . 'node/3/edit?destination=node">Edit</a></li></ul>');
|
||||
$this->assertIdentical($json[$ids[3]], '');
|
||||
|
||||
// Authenticated user: can access contextual links, cannot edit articles.
|
||||
|
|
|
@ -26,75 +26,83 @@ class ContextualUnitTest extends DrupalUnitTestBase {
|
|||
*/
|
||||
function _contextual_links_id_testcases() {
|
||||
// Test branch conditions:
|
||||
// - one module.
|
||||
// - one group.
|
||||
// - one dynamic path argument.
|
||||
// - no metadata.
|
||||
$tests[] = array(
|
||||
'links' => array(
|
||||
'node' => array(
|
||||
'node',
|
||||
array('14031991'),
|
||||
array()
|
||||
'route_parameters' => array(
|
||||
'node' => '14031991',
|
||||
),
|
||||
'metadata' => array()
|
||||
),
|
||||
),
|
||||
'id' => 'node:node:14031991:',
|
||||
'id' => 'node:node=14031991:',
|
||||
);
|
||||
|
||||
// Test branch conditions:
|
||||
// - one module.
|
||||
// - one group.
|
||||
// - multiple dynamic path arguments.
|
||||
// - no metadata.
|
||||
$tests[] = array(
|
||||
'links' => array(
|
||||
'foo' => array(
|
||||
'baz/in/ga',
|
||||
array('bar', 'baz', 'qux'),
|
||||
array()
|
||||
'route_parameters'=> array(
|
||||
'bar',
|
||||
'key' => 'baz',
|
||||
'qux',
|
||||
),
|
||||
'metadata' => array(),
|
||||
),
|
||||
),
|
||||
'id' => 'foo:baz/in/ga:bar/baz/qux:',
|
||||
'id' => 'foo:0=bar&key=baz&1=qux:',
|
||||
);
|
||||
|
||||
// Test branch conditions:
|
||||
// - one module.
|
||||
// - one group.
|
||||
// - one dynamic path argument.
|
||||
// - metadata.
|
||||
$tests[] = array(
|
||||
'links' => array(
|
||||
'views_ui' => array(
|
||||
'admin/structure/views/view',
|
||||
array('frontpage'),
|
||||
array(
|
||||
'views_ui_edit' => array(
|
||||
'route_parameters' => array(
|
||||
'view' => 'frontpage'
|
||||
),
|
||||
'metadata' => array(
|
||||
'location' => 'page',
|
||||
'display' => 'page_1',
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
'id' => 'views_ui:admin/structure/views/view:frontpage:location=page&display=page_1',
|
||||
'id' => 'views_ui_edit:view=frontpage:location=page&display=page_1',
|
||||
);
|
||||
|
||||
// Test branch conditions:
|
||||
// - multiple modules.
|
||||
// - multiple groups.
|
||||
// - multiple dynamic path arguments.
|
||||
$tests[] = array(
|
||||
'links' => array(
|
||||
'node' => array(
|
||||
'node',
|
||||
array('14031991'),
|
||||
array()
|
||||
'route_parameters' => array(
|
||||
'node' => '14031991',
|
||||
),
|
||||
'metadata' => array(),
|
||||
),
|
||||
'foo' => array(
|
||||
'baz/in/ga',
|
||||
array('bar', 'baz', 'qux'),
|
||||
array()
|
||||
'route_parameters' => array(
|
||||
'bar',
|
||||
'key' => 'baz',
|
||||
'qux',
|
||||
),
|
||||
'metadata' => array(),
|
||||
),
|
||||
'edge' => array(
|
||||
'edge',
|
||||
array('20011988'),
|
||||
array()
|
||||
'route_parameters' => array('20011988'),
|
||||
'metadata' => array(),
|
||||
),
|
||||
),
|
||||
'id' => 'node:node:14031991:|foo:baz/in/ga:bar/baz/qux:|edge:edge:20011988:',
|
||||
'id' => 'node:node=14031991:|foo:0=bar&key=baz&1=qux:|edge:0=20011988:',
|
||||
);
|
||||
|
||||
return $tests;
|
||||
|
|
|
@ -408,7 +408,7 @@ class MenuTest extends MenuWebTestBase {
|
|||
$block = $this->drupalPlaceBlock('system_menu_block:tools', array('label' => 'Tools', 'module' => 'system'));
|
||||
$this->drupalGet('test-page');
|
||||
|
||||
$id = 'block:admin/structure/block/manage:' . $block->id() . ':|menu:admin/structure/menu/manage:tools:';
|
||||
$id = 'block:block=' . $block->id() . ':|menu:menu=tools:';
|
||||
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:assertContextualLinkPlaceHolder()
|
||||
$this->assertRaw('<div data-contextual-id="'. $id . '"></div>', format_string('Contextual link placeholder with id @id exists.', array('@id' => $id)));
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
menu_edit:
|
||||
title: 'Edit menu'
|
||||
route_name: 'menu.menu_edit'
|
||||
group: menu
|
|
@ -94,7 +94,7 @@ function menu_menu() {
|
|||
$items['admin/structure/menu/manage/%menu/edit'] = array(
|
||||
'title' => 'Edit menu',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
|
||||
'context' => MENU_CONTEXT_PAGE,
|
||||
);
|
||||
$items['admin/structure/menu/item/%menu_link/edit'] = array(
|
||||
'title' => 'Edit menu link',
|
||||
|
@ -338,7 +338,9 @@ function menu_block_view_system_menu_block_alter(array &$build, BlockPluginInter
|
|||
list(, $menu_name) = explode(':', $block->getPluginId());
|
||||
if (isset($menus[$menu_name]) && isset($build['content'])) {
|
||||
foreach (element_children($build['content']) as $key) {
|
||||
$build['content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($build['content'][$key]['#original_link']['menu_name']));
|
||||
$build['content']['#contextual_links']['menu'] = array(
|
||||
'route_parameters' => array('menu' => $build['content'][$key]['#original_link']['menu_name']),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,9 @@ class NodeViewBuilder extends EntityViewBuilder {
|
|||
protected function alterBuild(array &$build, EntityInterface $entity, EntityDisplay $display, $view_mode, $langcode = NULL) {
|
||||
parent::alterBuild($build, $entity, $display, $view_mode, $langcode);
|
||||
if ($entity->id()) {
|
||||
$build['#contextual_links']['node'] = array('node', array($entity->id()));
|
||||
$build['#contextual_links']['node'] = array(
|
||||
'route_parameters' =>array('node' => $entity->id()),
|
||||
);
|
||||
}
|
||||
|
||||
// The node 'submitted' info is not rendered in a standard way (renderable
|
||||
|
|
|
@ -47,13 +47,15 @@ class NodeContextualLinksTest extends NodeTestBase {
|
|||
$user = $this->drupalCreateUser(array('administer nodes', 'access contextual links'));
|
||||
$this->drupalLogin($user);
|
||||
|
||||
$response = $this->renderContextualLinks(array('node:node:1:'), 'node');
|
||||
$response = $this->renderContextualLinks(array('node:node=1:'), 'node');
|
||||
$this->assertResponse(200);
|
||||
$json = Json::decode($response);
|
||||
$this->drupalSetContent($json['node:node:1:']);
|
||||
$this->drupalSetContent($json['node:node=1:']);
|
||||
|
||||
$this->assertLinkByHref('node/1/contextual-links', 0, 'The contextual link to the view was found.');
|
||||
$this->assertLink('Test contextual link', 0, 'The contextual link to the view was found.');
|
||||
// @todo Add these back when the functionality for making Views displays
|
||||
// appear in contextual links is working again.
|
||||
// $this->assertLinkByHref('node/1/contextual-links', 0, 'The contextual link to the view was found.');
|
||||
// $this->assertLink('Test contextual link', 0, 'The contextual link to the view was found.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
node.page_edit:
|
||||
route_name: node.page_edit
|
||||
group: node
|
||||
title: Edit
|
||||
|
||||
node.delete_confirm:
|
||||
route_name: node.delete_confirm
|
||||
group: node
|
||||
title: Delete
|
||||
weight: 10
|
|
@ -980,19 +980,6 @@ function node_menu() {
|
|||
// overridden by a menu link.
|
||||
'route_name' => 'node.view',
|
||||
);
|
||||
$items['node/%node/edit'] = array(
|
||||
'title' => 'Edit',
|
||||
'route_name' => 'node.page_edit',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
);
|
||||
$items['node/%node/delete'] = array(
|
||||
'title' => 'Delete',
|
||||
'route_name' => 'node.delete_confirm',
|
||||
'weight' => 10,
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
);
|
||||
$items['node/%node/revisions/%node_revision/view'] = array(
|
||||
'title' => 'Revisions',
|
||||
'route_name' => 'node.revision_show',
|
||||
|
|
|
@ -296,10 +296,6 @@ class MenuRouterTest extends WebTestBase {
|
|||
$depth = $parent['depth'] + 1;
|
||||
$plid = $parent['mlid'];
|
||||
|
||||
$link = $links['menu-test/hidden/block/manage/%/%/configure'];
|
||||
$this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
|
||||
$this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
|
||||
|
||||
$link = $links['menu-test/hidden/block/manage/%/%/delete'];
|
||||
$this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
|
||||
$this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
|
||||
|
|
|
@ -677,47 +677,61 @@ function hook_local_task_alter(&$local_tasks) {
|
|||
/**
|
||||
* 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.
|
||||
* This hook is invoked by
|
||||
* \Drupal\Core\Menu\ContextualLinkManager::getContextualLinkPluginsByGroup().
|
||||
* The system-determined contextual links are passed in by reference. Additional
|
||||
* links may be added and existing links can be altered.
|
||||
*
|
||||
* Each contextual link must at least contain:
|
||||
* Each contextual link contains the following entries:
|
||||
* - title: The localized title of the link.
|
||||
* - href: The system path to link to.
|
||||
* - route_name: The route name of the link.
|
||||
* - route_parameters: The route parameters of the link.
|
||||
* - localized_options: An array of options to pass to url().
|
||||
* - (optional) weight: The weight of the link, which is used to sort the links.
|
||||
*
|
||||
* @param $links
|
||||
* An associative array containing contextual links for the given $root_path,
|
||||
*
|
||||
* @param array $links
|
||||
* An associative array containing contextual links for the given $group,
|
||||
* 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/%'.
|
||||
* @param string $group
|
||||
* The group of contextual links being rendered.
|
||||
* @param array $route_parameters.
|
||||
* The route parameters passed to each route_name of the contextual links.
|
||||
* For example:
|
||||
* @code
|
||||
* array('node' => $node->id())
|
||||
* @endcode
|
||||
*
|
||||
* @see hook_contextual_links_view_alter()
|
||||
* @see menu_contextual_links()
|
||||
* @see hook_menu()
|
||||
* @see contextual_preprocess()
|
||||
* @see \Drupal\Core\Menu\ContextualLinkManager
|
||||
*/
|
||||
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',
|
||||
),
|
||||
),
|
||||
);
|
||||
function hook_contextual_links_alter(array &$links, $group, array $route_parameters) {
|
||||
if ($group == 'menu') {
|
||||
// Dynamically use the menu name for the title of the menu_edit contextual
|
||||
// link.
|
||||
$menu = \Drupal::entityManager()->getStorageController('menu')->load($route_parameters['menu']);
|
||||
$links['menu_edit']['title'] = t('Edit menu: !label', array('!label' => $menu->label()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the plugin definition of contextual links.
|
||||
*
|
||||
* @param array $contextual_links
|
||||
* An array of contextual_links plugin definitions, keyed by contextual link
|
||||
* ID. Each entry contains the following keys:
|
||||
* - title: The displayed title of the link
|
||||
* - route_name: The route_name of the contextual link to be displayed
|
||||
* - group: The group under which the contextual links should be added to.
|
||||
* Possible values are e.g. 'node' or 'menu'.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\ContextualLinkManager
|
||||
*/
|
||||
function hook_contextual_links_plugins_alter(array &$contextual_links) {
|
||||
$contextual_links['menu_edit']['title'] = 'Edit the menu';
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform alterations before a page is rendered.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
menu_test.hidden_manage:
|
||||
title: 'List links'
|
||||
group: menu_test_menu
|
||||
route_name: menu_test.hidden_manage
|
||||
|
||||
menu_test.hidden_manage_edit:
|
||||
title: 'Edit menu'
|
||||
group: menu_test_menu
|
||||
route_name: menu_test.hidden_manage_edit
|
||||
|
||||
menu_test.hidden_block_configure:
|
||||
title: 'Configure block'
|
||||
group: menu_test_block
|
||||
route_name: menu_test.hidden_block_configure
|
|
@ -126,7 +126,7 @@ function menu_test_menu() {
|
|||
$items['menu-test/hidden/menu/manage/%menu/list'] = array(
|
||||
'title' => 'List links',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
|
||||
'context' => MENU_CONTEXT_PAGE,
|
||||
);
|
||||
$items['menu-test/hidden/menu/manage/%menu/add'] = array(
|
||||
'title' => 'Add link',
|
||||
|
@ -137,7 +137,7 @@ function menu_test_menu() {
|
|||
'title' => 'Edit menu',
|
||||
'route_name' => 'menu_test.hidden_manage_edit',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
|
||||
'context' => MENU_CONTEXT_PAGE,
|
||||
);
|
||||
$items['menu-test/hidden/menu/manage/%menu/delete'] = array(
|
||||
'title' => 'Delete menu',
|
||||
|
@ -162,11 +162,6 @@ function menu_test_menu() {
|
|||
'title' => 'Configure block',
|
||||
'route_name' => 'menu_test.hidden_block_configure',
|
||||
);
|
||||
$items['menu-test/hidden/block/manage/%/%/configure'] = array(
|
||||
'title' => 'Configure block',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
);
|
||||
$items['menu-test/hidden/block/manage/%/%/delete'] = array(
|
||||
'title' => 'Delete block',
|
||||
'route_name' => 'menu_test.hidden_block_delete',
|
||||
|
|
|
@ -54,7 +54,9 @@ class TermViewBuilder extends EntityViewBuilder {
|
|||
protected function alterBuild(array &$build, EntityInterface $entity, EntityDisplay $display, $view_mode, $langcode = NULL) {
|
||||
parent::alterBuild($build, $entity, $display, $view_mode, $langcode);
|
||||
$build['#attached']['css'][] = drupal_get_path('module', 'taxonomy') . '/css/taxonomy.module.css';
|
||||
$build['#contextual_links']['taxonomy'] = array('taxonomy/term', array($entity->id()));
|
||||
$build['#contextual_links']['taxonomy_term'] = array(
|
||||
'route_parameters' => array('taxonomy_term' => $entity->id()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
taxonomy.term_edit:
|
||||
title: Edit
|
||||
group: taxonomy_term
|
||||
route_name: taxonomy.term_edit
|
||||
weight: 10
|
||||
|
||||
taxonomy.term_delete:
|
||||
title: Delete
|
||||
group: taxonomy_term
|
||||
route_name: taxonomy.term_delete
|
||||
weight: 20
|
||||
|
||||
taxonomy.vocabulary_delete:
|
||||
title: Delete
|
||||
group: taxonomy_vocabulary
|
||||
route_name: taxonomy.vocabulary_delete
|
||||
weight: 20
|
|
@ -255,20 +255,6 @@ function taxonomy_menu() {
|
|||
'title arguments' => array(2),
|
||||
'route_name' => 'taxonomy.term_page',
|
||||
);
|
||||
$items['taxonomy/term/%taxonomy_term/edit'] = array(
|
||||
'title' => 'Edit',
|
||||
'route_name' => 'taxonomy.term_edit',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
'weight' => 10,
|
||||
);
|
||||
$items['taxonomy/term/%taxonomy_term/delete'] = array(
|
||||
'title' => 'Delete',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
'weight' => 20,
|
||||
'route_name' => 'taxonomy.term_delete',
|
||||
);
|
||||
$items['taxonomy/term/%taxonomy_term/feed'] = array(
|
||||
'title' => 'Taxonomy term',
|
||||
'title callback' => 'taxonomy_term_title',
|
||||
|
@ -291,13 +277,6 @@ function taxonomy_menu() {
|
|||
'route_name' => 'taxonomy.vocabulary_edit',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
);
|
||||
$items['admin/structure/taxonomy/%taxonomy_vocabulary/delete'] = array(
|
||||
'title' => 'Delete',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
'weight' => 20,
|
||||
'route_name' => 'taxonomy.vocabulary_delete',
|
||||
);
|
||||
|
||||
$items['admin/structure/taxonomy/manage/%taxonomy_vocabulary/add'] = array(
|
||||
'title' => 'Add term',
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
user.role_delete:
|
||||
title: 'Delete role'
|
||||
group: role
|
||||
weight: 10
|
||||
route_name: 'user.role_delete'
|
|
@ -795,12 +795,6 @@ function user_menu() {
|
|||
'title' => 'Edit',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
);
|
||||
$items['admin/people/roles/manage/%user_role/delete'] = array(
|
||||
'title' => 'Delete role',
|
||||
'route_name' => 'user.role_delete',
|
||||
'weight' => 10,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
);
|
||||
|
||||
// Administration pages.
|
||||
$items['admin/config/people'] = array(
|
||||
|
|
|
@ -302,9 +302,9 @@ abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouter
|
|||
}
|
||||
|
||||
// Add context for contextual links.
|
||||
// @see menu_contextual_links()
|
||||
if (!empty($menu['context'])) {
|
||||
$items[$path]['context'] = MENU_CONTEXT_INLINE;
|
||||
// @todo Make this work with the new contextual links system.
|
||||
$items[$path]['context'] = TRUE;
|
||||
}
|
||||
|
||||
// If this is a 'default' tab, check to see if we have to create the
|
||||
|
|
|
@ -552,11 +552,11 @@ function views_add_contextual_links(&$render_element, $location, ViewExecutable
|
|||
$plugin['contextual_links_locations'][] = 'exposed_filter';
|
||||
$has_links = !empty($plugin['contextual links']) && !empty($plugin['contextual_links_locations']);
|
||||
if ($has_links && in_array($location, $plugin['contextual_links_locations'])) {
|
||||
foreach ($plugin['contextual links'] as $module => $link) {
|
||||
foreach ($plugin['contextual links'] as $group => $link) {
|
||||
$args = array();
|
||||
$valid = TRUE;
|
||||
if (!empty($link['argument properties'])) {
|
||||
foreach ($link['argument properties'] as $property) {
|
||||
if (!empty($link['route_parameters_names'])) {
|
||||
foreach ($link['route_parameters_names'] as $parameter_name => $property) {
|
||||
// If the plugin is trying to create an invalid contextual link
|
||||
// (for example, "path/to/{$view->storage->property}", where
|
||||
// $view->storage->{property} does not exist), we cannot construct
|
||||
|
@ -566,7 +566,7 @@ function views_add_contextual_links(&$render_element, $location, ViewExecutable
|
|||
break;
|
||||
}
|
||||
else {
|
||||
$args[] = $view->storage->{$property};
|
||||
$args[$parameter_name] = $view->storage->{$property};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -574,14 +574,13 @@ function views_add_contextual_links(&$render_element, $location, ViewExecutable
|
|||
// array.
|
||||
if ($valid) {
|
||||
$render_element['#views_contextual_links'] = TRUE;
|
||||
$render_element['#contextual_links'][$module] = array(
|
||||
$link['parent path'],
|
||||
$args,
|
||||
array(
|
||||
$render_element['#contextual_links'][$group] = array(
|
||||
'route_parameters' => $args,
|
||||
'metadata' => array(
|
||||
'location' => $location,
|
||||
'name' => $view->storage->id(),
|
||||
'display_id' => $display_id,
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -219,13 +219,13 @@ class DisplayTest extends UITestBase {
|
|||
$definitions = Views::pluginManager('display')->getDefinitions();
|
||||
|
||||
$expected = array(
|
||||
'parent path' => 'admin/structure/views/view',
|
||||
'argument properties' => array('id'),
|
||||
'route_name' => 'views_ui.edit',
|
||||
'route_parameters_names' => array('view' => 'id'),
|
||||
);
|
||||
|
||||
// Test the expected views_ui array exists on each definition.
|
||||
foreach ($definitions as $definition) {
|
||||
$this->assertIdentical($definition['contextual links']['views_ui'], $expected, 'Expected views_ui array found in plugin definition.');
|
||||
$this->assertIdentical($definition['contextual links']['views_ui_edit'], $expected, 'Expected views_ui array found in plugin definition.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,7 +288,7 @@ class DisplayTest extends UITestBase {
|
|||
$view->enable()->save();
|
||||
|
||||
$this->drupalGet('test-display');
|
||||
$id = 'views_ui:admin/structure/views/view:test_display:location=page&name=test_display&display_id=page_1';
|
||||
$id = 'views_ui_edit:view=test_display:location=page&name=test_display&display_id=page_1';
|
||||
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:assertContextualLinkPlaceHolder()
|
||||
$this->assertRaw('<div data-contextual-id="'. $id . '"></div>', format_string('Contextual link placeholder with id @id exists.', array('@id' => $id)));
|
||||
|
||||
|
@ -298,7 +298,7 @@ class DisplayTest extends UITestBase {
|
|||
$response = $this->drupalPost('contextual/render', 'application/json', $post, array('query' => array('destination' => 'test-display')));
|
||||
$this->assertResponse(200);
|
||||
$json = drupal_json_decode($response);
|
||||
$this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="views-ui-edit odd first last"><a href="' . base_path() . 'admin/structure/views/view/test_display/edit/page_1?destination=test-display">Edit view</a></li></ul>');
|
||||
$this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="views-uiedit odd first last"><a href="' . base_path() . 'admin/structure/views/view/test_display/edit/page_1?destination=test-display">Edit view</a></li></ul>');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
views_ui.edit:
|
||||
title: 'Edit view'
|
||||
route_name: views_ui.edit
|
||||
group: views_ui_edit
|
||||
|
||||
views_ui.preview:
|
||||
title: 'Preview view'
|
||||
route_name: views_ui.preview
|
||||
group: views_ui_preview
|
|
@ -30,11 +30,6 @@ function views_ui_menu() {
|
|||
$items['admin/structure/views/view/%'] = array(
|
||||
'route_name' => 'views_ui.edit',
|
||||
);
|
||||
$items['admin/structure/views/view/%/edit'] = array(
|
||||
'title' => 'Edit view',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'context' => MENU_CONTEXT_INLINE,
|
||||
);
|
||||
|
||||
// A page in the Reports section to show usage of plugins in all views.
|
||||
$items['admin/reports/views-plugins'] = array(
|
||||
|
@ -277,9 +272,9 @@ function views_ui_views_plugins_display_alter(&$plugins) {
|
|||
// paths underneath "admin/structure/views/view/{$view->id()}" (i.e., paths
|
||||
// for editing and performing other contextual actions on the view).
|
||||
foreach ($plugins as &$display) {
|
||||
$display['contextual links']['views_ui'] = array(
|
||||
'parent path' => 'admin/structure/views/view',
|
||||
'argument properties' => array('id'),
|
||||
$display['contextual links']['views_ui_edit'] = array(
|
||||
'route_name' => 'views_ui.edit',
|
||||
'route_parameters_names' => array('view' => 'id'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -296,9 +291,10 @@ function views_ui_contextual_links_view_alter(&$element, $items) {
|
|||
// Append the display ID to the Views UI edit links, so that clicking on the
|
||||
// contextual link takes you directly to the correct display tab on the edit
|
||||
// screen.
|
||||
elseif (!empty($element['#links']['views-ui-edit'])) {
|
||||
$display_id = $element['#contextual_links']['views_ui'][2]['display_id'];
|
||||
$element['#links']['views-ui-edit']['href'] .= '/edit/' . $display_id;
|
||||
elseif (!empty($element['#links']['views-uiedit'])) {
|
||||
$display_id = $items['views_ui.edit']['metadata']['display_id'];
|
||||
$element['#links']['views-uiedit']['route_parameters']['display_id'] = $display_id;
|
||||
$element['#links']['views-uiedit']['route_name'] = 'views_ui.edit_display';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Menu\ContextualLinkDefaultTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Menu;
|
||||
|
||||
use Drupal\Core\Menu\ContextualLinkDefault;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the contextual link default class.
|
||||
*
|
||||
* @group Drupal
|
||||
* @group Menu
|
||||
*/
|
||||
class ContextualLinkDefaultTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested contextual link default plugin.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\ContextualLinkDefault
|
||||
*/
|
||||
protected $contextualLinkDefault;
|
||||
|
||||
/**
|
||||
* The used plugin configuration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config = array();
|
||||
|
||||
/**
|
||||
* The used plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pluginId = 'contextual_link_default';
|
||||
|
||||
/**
|
||||
* The used plugin definition.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $pluginDefinition = array(
|
||||
'id' => 'contextual_link_default',
|
||||
);
|
||||
|
||||
/**
|
||||
* The mocked translator.
|
||||
*
|
||||
* @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $stringTranslation;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Contextual links default.',
|
||||
'description' => 'Tests the contextual link default class.',
|
||||
'group' => 'Menu',
|
||||
);
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface');
|
||||
}
|
||||
|
||||
protected function setupContextualLinkDefault() {
|
||||
$this->contextualLinkDefault = new ContextualLinkDefault($this->config, $this->pluginId, $this->pluginDefinition);
|
||||
$this->contextualLinkDefault->setTranslationManager($this->stringTranslation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getTitle method without a translation context.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\LocalTaskDefault::getTitle()
|
||||
*/
|
||||
public function testGetTitle($title = 'Example') {
|
||||
$this->pluginDefinition['title'] = $title;
|
||||
$this->stringTranslation->expects($this->once())
|
||||
->method('translate')
|
||||
->with($this->pluginDefinition['title'], array(), array())
|
||||
->will($this->returnValue('Example translated'));
|
||||
|
||||
$this->setupContextualLinkDefault();
|
||||
$this->assertEquals('Example translated', $this->contextualLinkDefault->getTitle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getTitle method with a translation context.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\LocalTaskDefault::getTitle()
|
||||
*/
|
||||
public function testGetTitleWithContext() {
|
||||
$this->pluginDefinition['title'] = 'Example';
|
||||
$this->pluginDefinition['title_context'] = 'context';
|
||||
$this->stringTranslation->expects($this->once())
|
||||
->method('translate')
|
||||
->with($this->pluginDefinition['title'], array(), array('context' => $this->pluginDefinition['title_context']))
|
||||
->will($this->returnValue('Example translated with context'));
|
||||
|
||||
$this->setupContextualLinkDefault();
|
||||
$this->assertEquals('Example translated with context', $this->contextualLinkDefault->getTitle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getRouteName() method.
|
||||
*
|
||||
* @covers \Drupal\Core\Menu\ContextualLinkDefault::getRouteName()
|
||||
*/
|
||||
public function testGetRouteName($route_name = 'test_route_name') {
|
||||
$this->pluginDefinition['route_name'] = $route_name;
|
||||
$this->setupContextualLinkDefault();
|
||||
|
||||
$this->assertEquals($route_name, $this->contextualLinkDefault->getRouteName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getGroup() method.
|
||||
*
|
||||
* @covers \Drupal\Core\Menu\ContextualLinkDefault::getGroup()
|
||||
*/
|
||||
public function testGetGroup($group_name = 'test_group') {
|
||||
$this->pluginDefinition['group'] = $group_name;
|
||||
$this->setupContextualLinkDefault();
|
||||
|
||||
$this->assertEquals($group_name, $this->contextualLinkDefault->getGroup());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getOptions() method.
|
||||
*
|
||||
* @covers \Drupal\Core\Menu\ContextualLinkDefault::getOptions()
|
||||
*/
|
||||
public function testGetOptions($options = array('key' => 'value')) {
|
||||
$this->pluginDefinition['options'] = $options;
|
||||
$this->setupContextualLinkDefault();
|
||||
|
||||
$this->assertEquals($options, $this->contextualLinkDefault->getOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getWeight() method.
|
||||
*
|
||||
* @covers \Drupal\Core\Menu\ContextualLinkDefault::getWeight()
|
||||
*/
|
||||
public function testGetWeight($weight = 5) {
|
||||
$this->pluginDefinition['weight'] = $weight;
|
||||
$this->setupContextualLinkDefault();
|
||||
|
||||
$this->assertEquals($weight, $this->contextualLinkDefault->getWeight());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,410 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Menu\ContextualLinkManagerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Menu;
|
||||
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the contextual links manager.
|
||||
*
|
||||
* @group Drupal
|
||||
* @group Menu
|
||||
*
|
||||
* @see \Drupal\Core\Menu\ContextualLinkManager
|
||||
*/
|
||||
class ContextualLinkManagerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested contextual link manager.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\ContextualLinkManager
|
||||
*/
|
||||
protected $contextualLinkManager;
|
||||
|
||||
/**
|
||||
* The mocked controller resolver.
|
||||
*
|
||||
* @var \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface|\Drupal\Core\\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $controllerResolver;
|
||||
|
||||
/**
|
||||
* The mocked plugin discovery.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $pluginDiscovery;
|
||||
|
||||
/**
|
||||
* The plugin factory used in the test.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\Factory\FactoryInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
/**
|
||||
* The cache backend used in the test.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $cacheBackend;
|
||||
|
||||
/**
|
||||
* The mocked module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The mocked access manager.
|
||||
*
|
||||
* @var \Drupal\Core\Access\AccessManager|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $accessManager;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Contextual links manager.',
|
||||
'description' => 'Tests the contextual links manager.',
|
||||
'group' => 'Menu',
|
||||
);
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
$this->contextualLinkManager = $this
|
||||
->getMockBuilder('Drupal\Core\Menu\ContextualLinkManager')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(NULL)
|
||||
->getMock();
|
||||
|
||||
$this->controllerResolver = $this->getMock('Symfony\Component\HttpKernel\Controller\ControllerResolverInterface');
|
||||
$this->pluginDiscovery = $this->getMock('Drupal\Component\Plugin\Discovery\DiscoveryInterface');
|
||||
$this->factory = $this->getMock('Drupal\Component\Plugin\Factory\FactoryInterface');
|
||||
$this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
|
||||
$this->accessManager = $this->getMockBuilder('Drupal\Core\Access\AccessManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
|
||||
|
||||
$property = new \ReflectionProperty('Drupal\Core\Menu\ContextualLinkManager', 'controllerResolver');
|
||||
$property->setAccessible(TRUE);
|
||||
$property->setValue($this->contextualLinkManager, $this->controllerResolver);
|
||||
|
||||
$property = new \ReflectionProperty('Drupal\Core\Menu\ContextualLinkManager', 'discovery');
|
||||
$property->setAccessible(TRUE);
|
||||
$property->setValue($this->contextualLinkManager, $this->pluginDiscovery);
|
||||
|
||||
$property = new \ReflectionProperty('Drupal\Core\Menu\ContextualLinkManager', 'factory');
|
||||
$property->setAccessible(TRUE);
|
||||
$property->setValue($this->contextualLinkManager, $this->factory);
|
||||
|
||||
$property = new \ReflectionProperty('Drupal\Core\Menu\ContextualLinkManager', 'account');
|
||||
$property->setAccessible(TRUE);
|
||||
$property->setValue($this->contextualLinkManager, $this->account);
|
||||
|
||||
$property = new \ReflectionProperty('Drupal\Core\Menu\ContextualLinkManager', 'accessManager');
|
||||
$property->setAccessible(TRUE);
|
||||
$property->setValue($this->contextualLinkManager, $this->accessManager);
|
||||
|
||||
$language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$language_manager->expects($this->any())
|
||||
->method('getLanguage')
|
||||
->will($this->returnValue(new Language(array('id' => 'en'))));
|
||||
|
||||
$this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface');
|
||||
|
||||
$method = new \ReflectionMethod('Drupal\Core\Menu\ContextualLinkManager', 'alterInfo');
|
||||
$method->setAccessible(TRUE);
|
||||
$method->invoke($this->contextualLinkManager, $this->moduleHandler, 'contextual_links_plugins');
|
||||
|
||||
$this->contextualLinkManager->setCacheBackend($this->cacheBackend, $language_manager, 'contextual_links_plugins');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getContextualLinkPluginsByGroup method.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\ContextualLinkManager::getContextualLinkPluginsByGroup()
|
||||
*/
|
||||
public function testGetContextualLinkPluginsByGroup() {
|
||||
$definitions = array(
|
||||
'test_plugin1' => array(
|
||||
'id' => 'test_plugin1',
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'group' => 'group1',
|
||||
'route_name' => 'test_route',
|
||||
),
|
||||
'test_plugin2' => array(
|
||||
'id' => 'test_plugin2',
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'group' => 'group1',
|
||||
'route_name' => 'test_route2',
|
||||
),
|
||||
'test_plugin3' => array(
|
||||
'id' => 'test_plugin3',
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'group' => 'group2',
|
||||
'route_name' => 'test_router3',
|
||||
),
|
||||
);
|
||||
$this->pluginDiscovery->expects($this->once())
|
||||
->method('getDefinitions')
|
||||
->will($this->returnValue($definitions));
|
||||
|
||||
// Test with a non existing group.
|
||||
$result = $this->contextualLinkManager->getContextualLinkPluginsByGroup('group_non_existing');
|
||||
$this->assertEmpty($result);
|
||||
|
||||
$result = $this->contextualLinkManager->getContextualLinkPluginsByGroup('group1');
|
||||
$this->assertEquals(array('test_plugin1', 'test_plugin2'), array_keys($result));
|
||||
|
||||
$result = $this->contextualLinkManager->getContextualLinkPluginsByGroup('group2');
|
||||
$this->assertEquals(array('test_plugin3'), array_keys($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getContextualLinkPluginsByGroup method with a prefilled cache.
|
||||
*/
|
||||
public function testGetContextualLinkPluginsByGroupWithCache() {
|
||||
$definitions = array(
|
||||
'test_plugin1' => array(
|
||||
'id' => 'test_plugin1',
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'group' => 'group1',
|
||||
'route_name' => 'test_route',
|
||||
),
|
||||
'test_plugin2' => array(
|
||||
'id' => 'test_plugin2',
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'group' => 'group1',
|
||||
'route_name' => 'test_route2',
|
||||
),
|
||||
);
|
||||
|
||||
$this->cacheBackend->expects($this->once())
|
||||
->method('get')
|
||||
->with('contextual_links_plugins:en:group1')
|
||||
->will($this->returnValue((object) array('data' => $definitions)));
|
||||
|
||||
$result = $this->contextualLinkManager->getContextualLinkPluginsByGroup('group1');
|
||||
$this->assertEquals($definitions, $result);
|
||||
|
||||
// Ensure that the static cache works, so no second cache get is executed.
|
||||
|
||||
$result = $this->contextualLinkManager->getContextualLinkPluginsByGroup('group1');
|
||||
$this->assertEquals($definitions, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests processDefinition() by passing a plugin definition without a route.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\ContextualLinkManager::processDefinition()
|
||||
*
|
||||
* @expectedException \Drupal\Component\Plugin\Exception\PluginException
|
||||
*/
|
||||
public function testProcessDefinitionWithoutRoute() {
|
||||
$definition = array(
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'group' => 'example',
|
||||
'id' => 'test_plugin',
|
||||
);
|
||||
$this->contextualLinkManager->processDefinition($definition, 'test_plugin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests processDefinition() by passing a plugin definition without a group.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\ContextualLinkManager::processDefinition()
|
||||
*
|
||||
* @expectedException \Drupal\Component\Plugin\Exception\PluginException
|
||||
*/
|
||||
public function testProcessDefinitionWithoutGroup() {
|
||||
$definition = array(
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'route_name' => 'example',
|
||||
'id' => 'test_plugin',
|
||||
);
|
||||
$this->contextualLinkManager->processDefinition($definition, 'test_plugin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getContextualLinksArrayByGroup method.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\ContextualLinkManager::getContextualLinksArrayByGroup()
|
||||
*/
|
||||
public function testGetContextualLinksArrayByGroup() {
|
||||
$definitions = array(
|
||||
'test_plugin1' => array(
|
||||
'id' => 'test_plugin1',
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'title' => 'Plugin 1',
|
||||
'weight' => 0,
|
||||
'group' => 'group1',
|
||||
'route_name' => 'test_route',
|
||||
'options' => array(),
|
||||
),
|
||||
'test_plugin2' => array(
|
||||
'id' => 'test_plugin2',
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'title' => 'Plugin 2',
|
||||
'weight' => 2,
|
||||
'group' => 'group1',
|
||||
'route_name' => 'test_route2',
|
||||
'options' => array('key' => 'value'),
|
||||
),
|
||||
'test_plugin3' => array(
|
||||
'id' => 'test_plugin3',
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'title' => 'Plugin 3',
|
||||
'weight' => 5,
|
||||
'group' => 'group2',
|
||||
'route_name' => 'test_router3',
|
||||
'options' => array(),
|
||||
),
|
||||
);
|
||||
|
||||
$this->pluginDiscovery->expects($this->once())
|
||||
->method('getDefinitions')
|
||||
->will($this->returnValue($definitions));
|
||||
|
||||
$this->accessManager->expects($this->any())
|
||||
->method('checkNamedRoute')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
// Set up mocking of the plugin factory.
|
||||
$map = array();
|
||||
foreach ($definitions as $plugin_id => $definition) {
|
||||
$plugin = $this->getMock('Drupal\Core\Menu\ContextualLinkInterface');
|
||||
$plugin->expects($this->any())
|
||||
->method('getRouteName')
|
||||
->will($this->returnValue($definition['route_name']));
|
||||
$plugin->expects($this->any())
|
||||
->method('getTitle')
|
||||
->will($this->returnValue($definition['title']));
|
||||
$plugin->expects($this->any())
|
||||
->method('getWeight')
|
||||
->will($this->returnValue($definition['weight']));
|
||||
$plugin->expects($this->any())
|
||||
->method('getOptions')
|
||||
->will($this->returnValue($definition['options']));
|
||||
$map[] = array($plugin_id, array(), $plugin);
|
||||
}
|
||||
$this->factory->expects($this->any())
|
||||
->method('createInstance')
|
||||
->will($this->returnValueMap($map));
|
||||
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('alter')
|
||||
->with($this->equalTo('contextual_links'), new \PHPUnit_Framework_Constraint_Count(2), $this->equalTo('group1'), $this->equalTo(array('key' => 'value')));
|
||||
|
||||
$result = $this->contextualLinkManager->getContextualLinksArrayByGroup('group1', array('key' => 'value'));
|
||||
$this->assertCount(2, $result);
|
||||
foreach (array('test_plugin1', 'test_plugin2') as $plugin_id) {
|
||||
$definition = $definitions[$plugin_id];
|
||||
$this->assertEquals($definition['weight'], $result[$plugin_id]['weight']);
|
||||
$this->assertEquals($definition['title'], $result[$plugin_id]['title']);
|
||||
$this->assertEquals($definition['route_name'], $result[$plugin_id]['route_name']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the access checking of the getContextualLinksArrayByGroup method.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\ContextualLinkManager::getContextualLinksArrayByGroup()
|
||||
*/
|
||||
public function testGetContextualLinksArrayByGroupAccessCheck() {
|
||||
$definitions = array(
|
||||
'test_plugin1' => array(
|
||||
'id' => 'test_plugin1',
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'title' => 'Plugin 1',
|
||||
'weight' => 0,
|
||||
'group' => 'group1',
|
||||
'route_name' => 'test_route',
|
||||
'options' => array(),
|
||||
),
|
||||
'test_plugin2' => array(
|
||||
'id' => 'test_plugin2',
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'title' => 'Plugin 2',
|
||||
'weight' => 2,
|
||||
'group' => 'group1',
|
||||
'route_name' => 'test_route2',
|
||||
'options' => array('key' => 'value'),
|
||||
),
|
||||
);
|
||||
|
||||
$this->pluginDiscovery->expects($this->once())
|
||||
->method('getDefinitions')
|
||||
->will($this->returnValue($definitions));
|
||||
|
||||
$this->accessManager->expects($this->any())
|
||||
->method('checkNamedRoute')
|
||||
->will($this->returnValueMap(array(
|
||||
array('test_route', array('key' => 'value'), $this->account, NULL, TRUE),
|
||||
array('test_route2', array('key' => 'value'), $this->account, NULL, FALSE),
|
||||
)));
|
||||
|
||||
// Set up mocking of the plugin factory.
|
||||
$map = array();
|
||||
foreach ($definitions as $plugin_id => $definition) {
|
||||
$plugin = $this->getMock('Drupal\Core\Menu\ContextualLinkInterface');
|
||||
$plugin->expects($this->any())
|
||||
->method('getRouteName')
|
||||
->will($this->returnValue($definition['route_name']));
|
||||
$plugin->expects($this->any())
|
||||
->method('getTitle')
|
||||
->will($this->returnValue($definition['title']));
|
||||
$plugin->expects($this->any())
|
||||
->method('getWeight')
|
||||
->will($this->returnValue($definition['weight']));
|
||||
$plugin->expects($this->any())
|
||||
->method('getOptions')
|
||||
->will($this->returnValue($definition['options']));
|
||||
$map[] = array($plugin_id, array(), $plugin);
|
||||
}
|
||||
$this->factory->expects($this->any())
|
||||
->method('createInstance')
|
||||
->will($this->returnValueMap($map));
|
||||
|
||||
$result = $this->contextualLinkManager->getContextualLinksArrayByGroup('group1', array('key' => 'value'));
|
||||
|
||||
// Ensure that access checking was respected.
|
||||
$this->assertTrue(isset($result['test_plugin1']));
|
||||
$this->assertFalse(isset($result['test_plugin2']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the plugins alter hook.
|
||||
*/
|
||||
public function testPluginDefinitionAlter() {
|
||||
$definitions['test_plugin'] = array(
|
||||
'id' => 'test_plugin',
|
||||
'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
|
||||
'title' => 'Plugin',
|
||||
'weight' => 2,
|
||||
'group' => 'group1',
|
||||
'route_name' => 'test_route',
|
||||
'options' => array('key' => 'value'),
|
||||
);
|
||||
|
||||
$this->pluginDiscovery->expects($this->once())
|
||||
->method('getDefinitions')
|
||||
->will($this->returnValue($definitions));
|
||||
|
||||
$this->moduleHandler->expects($this->once())
|
||||
->method('alter')
|
||||
->with('contextual_links_plugins', $definitions);
|
||||
|
||||
$this->contextualLinkManager->getDefinition('test_plugin');
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue