Commit 00fdcd39 authored by alexpott's avatar alexpott

Issue #1908756 by tim.plunkett: Separate Action Links (MENU_LOCAL_ACTION) from hook_menu().

parent bb45304a
......@@ -194,6 +194,14 @@
*/
define('MENU_LOCAL_ACTION', MENU_IS_LOCAL_TASK | MENU_IS_LOCAL_ACTION | MENU_VISIBLE_IN_BREADCRUMB);
/**
* Menu type -- A task specific to the parent, which is never rendered.
*
* Sibling local tasks are not rendered themselves, but affect the breadcrumb
* trail and need their sibling tasks rendered as tabs.
*/
define('MENU_SIBLING_LOCAL_TASK', MENU_IS_LOCAL_TASK | MENU_IS_LOCAL_ACTION | MENU_VISIBLE_IN_BREADCRUMB);
/**
* @} End of "defgroup menu_item_types".
*/
......@@ -2217,8 +2225,24 @@ function menu_secondary_local_tasks() {
/**
* Returns the rendered local actions at the current level.
*/
function menu_local_actions() {
function menu_get_local_actions() {
$links = menu_local_tasks();
$router_item = menu_get_item();
// @todo Consider storing the results of hook_local_actions() in a static.
foreach (Drupal::moduleHandler()->invokeAll('local_actions') as $route_info) {
if (in_array($router_item['route_name'], $route_info['appears_on'])) {
$route_path = _menu_router_translate_route($route_info['route_name']);
$action_router_item = menu_get_item($route_path);
$links['actions'][$route_path] = array(
'#theme' => 'menu_local_action',
'#link' => array(
'title' => $route_info['title'],
'href' => $route_path,
),
'#access' => $action_router_item['access'],
);
}
}
return $links['actions'];
}
......@@ -2688,8 +2712,9 @@ function menu_router_build($save = FALSE) {
// so the path of the route takes precedence.
if (isset($router_items[$path]['route_name'])) {
$router_item = $router_items[$path];
$router_item = _menu_router_merge_route($router_item, $path);
$new_path = $router_item['path'];
$router_item['page callback'] = 'USES_ROUTE';
$router_item['access callback'] = TRUE;
$new_path = _menu_router_translate_route($router_item['route_name']);
unset($router_items[$path]);
$router_items[$new_path] = $router_item;
$path = $new_path;
......@@ -2708,32 +2733,20 @@ function menu_router_build($save = FALSE) {
}
/**
* Merges a legacy menu router item with its referenced route.
* Translates a route name to its router item path.
*
* @param array $router_item
* The router item to modify.
* @param string $path
* The path this router item is for.
* @param string $route_name
* The route name to translate.
*
* @return array
* The modified router item, including the path pattern from the route in the
* 'path' key.
* @return string
* The translated path pattern from the route.
*/
function _menu_router_merge_route(array $router_item, $path) {
$router_item['path'] = $path;
$route_provider = drupal_container()->get('router.route_provider');
$route = $route_provider->getRouteByName($router_item['route_name']);
$router_item['path'] = trim($route->getPattern(), '/');
$router_item['page callback'] = 'USES_ROUTE';
$router_item['access callback'] = TRUE;
function _menu_router_translate_route($route_name) {
$route = Drupal::service('router.route_provider')->getRouteByName($route_name);
$path = trim($route->getPattern(), '/');
// Translate placeholders, e.g. {foo} -> %.
$router_item['path'] = preg_replace('/{' . DRUPAL_PHP_FUNCTION_PATTERN . '}/', '%', $router_item['path']);
return $router_item;
return preg_replace('/{' . DRUPAL_PHP_FUNCTION_PATTERN . '}/', '%', $path);
}
/**
......
......@@ -2900,7 +2900,7 @@ function template_preprocess_page(&$variables) {
$variables['logo'] = theme_get_setting('logo.url');
$variables['main_menu'] = theme_get_setting('features.main_menu') ? menu_main_menu() : array();
$variables['secondary_menu'] = theme_get_setting('features.secondary_menu') ? menu_secondary_menu() : array();
$variables['action_links'] = menu_local_actions();
$variables['action_links'] = menu_get_local_actions();
$variables['site_name'] = (theme_get_setting('features.name') ? check_plain($site_config->get('name')) : '');
$variables['site_slogan'] = (theme_get_setting('features.slogan') ? filter_xss_admin($site_config->get('slogan')) : '');
$variables['tabs'] = menu_local_tabs();
......
<?php
/**
* @file
* Contains \Drupal\system\Tests\Menu\LocalActionTest.
*/
namespace Drupal\system\Tests\Menu;
use Drupal\simpletest\WebTestBase;
/**
* Tests local actions.
*/
class LocalActionTest extends WebTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('menu_test');
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Local actions',
'description' => 'Tests local actions derived from router and added/altered via hooks.',
'group' => 'Menu',
);
}
/**
* Tests appearance of local actions.
*/
public function testLocalAction() {
$this->drupalGet('menu-test-local-action');
// Ensure that both menu and route based actions are shown.
$this->assertLocalAction(array(
'menu-test-local-action/hook_menu' => 'My hook_menu action',
'menu-test-local-action/routing' => 'My routing action',
));
}
/**
* Asserts local actions in the page output.
*
* @param array $actions
* A list of expected action link titles, keyed by the hrefs.
*/
protected function assertLocalAction(array $actions) {
$elements = $this->xpath('//a[contains(@class, :class)]', array(
':class' => 'button-action',
));
$index = 0;
foreach ($actions as $href => $title) {
$this->assertEqual((string) $elements[$index], $title);
$this->assertEqual($elements[$index]['href'], url($href));
$index++;
}
}
}
......@@ -864,6 +864,31 @@ function hook_menu() {
return $items;
}
/**
* Define route-based local actions.
*
* Instead of using MENU_LOCAL_ACTION in hook_menu(), implement
* hook_local_actions().
*
* @return array
* An associative array containing the following keys:
* - route_name: The machine name of the local action route.
* - title: The title of the local action.
* - appears_on: An array of route names for this action to be display on.
*/
function hook_local_actions() {
return array(
array(
'route_name' => 'mymodule.route.action',
'title' => t('Perform local action'),
'appears_on' => array(
'mymodule.other_route',
'mymodule.other_other_route',
),
),
);
}
/**
* Alter the data being saved to the {menu_router} table after hook_menu is invoked.
*
......
......@@ -416,9 +416,36 @@ function menu_test_menu() {
'type' => MENU_LOCAL_TASK,
);
$items['menu-test-local-action'] = array(
'title' => 'Local action parent',
'route_name' => 'menu_test_local_action1',
);
$items['menu-test-local-action/hook_menu'] = array(
'title' => 'My hook_menu action',
'route_name' => 'menu_test_local_action2',
'weight' => -10,
'type' => MENU_LOCAL_ACTION,
);
return $items;
}
/**
* Implements hook_local_actions().
*/
function menu_test_local_actions() {
return array(
array(
'route_name' => 'menu_test_local_action3',
'title' => t('My routing action'),
'appears_on' => array(
'menu_test_local_action1',
),
),
);
}
/**
* Implements hook_menu_local_tasks().
*/
......
......@@ -16,3 +16,24 @@ menu_router_test3:
_content: '\Drupal\menu_test\TestControllers::test2'
requirements:
_access: 'FALSE'
menu_test_local_action1:
pattern: '/menu-test-local-action'
defaults:
_content: '\Drupal\menu_test\TestControllers::test1'
requirements:
_access: 'TRUE'
menu_test_local_action2:
pattern: '/menu-test-local-action/hook_menu'
defaults:
_content: '\Drupal\menu_test\TestControllers::test2'
requirements:
_access: 'TRUE'
menu_test_local_action3:
pattern: '/menu-test-local-action/routing'
defaults:
_content: '\Drupal\menu_test\TestControllers::test2'
requirements:
_access: 'TRUE'
......@@ -31,9 +31,8 @@ function views_ui_menu() {
);
$items['admin/structure/views/add'] = array(
'title' => 'Add new view',
'route_name' => 'views_ui.add',
'type' => MENU_LOCAL_ACTION,
'type' => MENU_SIBLING_LOCAL_TASK,
);
$items['admin/structure/views/settings'] = array(
......@@ -113,6 +112,21 @@ function views_ui_entity_info(&$entity_info) {
);
}
/**
* Implements hook_local_actions().
*/
function views_ui_local_actions() {
return array(
array(
'route_name' => 'views_ui.add',
'title' => t('Add new view'),
'appears_on' => array(
'views_ui.list',
),
),
);
}
/**
* Implements hook_theme().
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment