core/core.services.yml | 9 +- core/includes/menu.inc | 26 ++++++ .../MenuRouterRebuildSubscriber.php | 99 ++++++++++++++++++++++ .../EventSubscriber/RouterRebuildSubscriber.php | 66 +-------------- core/lib/Drupal/Core/Menu/MenuLinkTree.php | 67 ++++++++++++--- .../src/Tests/ContentTranslationSettingsTest.php | 2 +- .../src/Controller/EntityDisplayModeController.php | 4 +- .../menu_link_content/menu_link_content.install | 11 ++- core/modules/menu_ui/menu_ui.module | 3 +- .../menu_ui/src/Controller/MenuController.php | 30 ++++++- .../modules/menu_ui/src/Form/MenuLinkResetForm.php | 2 +- core/modules/menu_ui/src/MenuForm.php | 27 ++++-- .../menu_ui/src/Tests/MenuCacheTagsTest.php | 2 +- core/modules/menu_ui/src/Tests/MenuTest.php | 4 +- core/modules/menu_ui/src/Tests/MenuWebTestBase.php | 7 +- .../system/src/Controller/SystemController.php | 4 +- core/modules/system/src/Tests/Menu/LinksTest.php | 8 +- .../system/src/Tests/Menu/MenuRouterTest.php | 6 +- core/modules/system/src/Tests/System/AdminTest.php | 3 +- core/modules/system/templates/menu-tree.html.twig | 2 +- core/modules/toolbar/toolbar.module | 19 +++-- .../user/src/Plugin/Menu/MyAccountMenuLink.php | 6 +- .../user/src/Tests/UserAccountLinksTests.php | 3 +- .../views/src/Plugin/Derivative/ViewsMenuLink.php | 8 +- .../src/Plugin/Menu/Form/ViewsMenuLinkForm.php | 11 ++- .../views/src/Plugin/Menu/ViewsMenuLink.php | 13 ++- core/modules/views/src/Tests/Wizard/MenuTest.php | 11 ++- core/modules/views/src/ViewExecutable.php | 4 +- 28 files changed, 315 insertions(+), 142 deletions(-) diff --git a/core/core.services.yml b/core/core.services.yml index 70eefe4..fd9028c 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -277,7 +277,7 @@ services: arguments: ['@menu.tree_storage', '@menu_link.static.overrides', '@module_handler'] menu.link_tree: class: Drupal\Core\Menu\MenuLinkTree - arguments: ['@menu.tree_storage', '@plugin.manager.menu.link', '@router.route_provider', '@menu.active_trail', '@controller_resolver'] + arguments: ['@menu.tree_storage', '@plugin.manager.menu.link', '@router.route_provider', '@menu.active_trail', '@controller_resolver', '@cache.menu', '@current_route_match'] menu.default_tree_manipulators: class: Drupal\Core\Menu\DefaultMenuLinkTreeManipulators arguments: ['@access_manager', '@current_user'] @@ -443,7 +443,12 @@ services: arguments: ['@router.dumper', '@lock', '@event_dispatcher', '@module_handler', '@controller_resolver', '@state'] router.rebuild_subscriber: class: Drupal\Core\EventSubscriber\RouterRebuildSubscriber - arguments: ['@router.builder', '@lock', '@plugin.manager.menu.link'] + arguments: ['@router.builder'] + tags: + - { name: event_subscriber } + menu.rebuild_subscriber: + class: Drupal\Core\EventSubscriber\MenuRouterRebuildSubscriber + arguments: ['@lock', '@plugin.manager.menu.link'] tags: - { name: event_subscriber } path.alias_storage: diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 5914496..2e31ef3 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -287,6 +287,32 @@ * links to display icons next to them. * - Pass the menu tree to \Drupal\menu_link\MenuTree::build(), this will build * a renderable array. + * + * Combined, that would look like this: + * @code + * $menu_tree = \Drupal::service('menu.link_tree'); + * $menu_name = 'my_menu'; + * + * // Build the typical default set of menu tree parameters. + * $parameters = $menu_tree->getCurrentRouteMenuTreeParameters($menu_name); + * + * // Load the tree based on this set of parameters. + * $tree = $menu_tree->load($menu_name, $parameters); + * + * // Transform the tree using the manipulators you want. + * $manipulators = array( + * // Only show links that are accessible for the current user. + * array('callable' => 'menu.default_tree_manipulators:checkAccess'), + * // Use the default sorting of menu links. + * array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'), + * ); + * $tree = $menu_tree->transform($tree, $manipulators); + * + * // Finally, build a renderable array from the transformed tree. + * $menu = $menu_tree->build($tree); + * + * $menu_html = drupal_render($menu); + * @endcode */ /** diff --git a/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php new file mode 100644 index 0000000..ba2cc7b --- /dev/null +++ b/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php @@ -0,0 +1,99 @@ +lock = $lock; + $this->menuLinkManager = $menu_link_manager; + } + + /** + * Rebuilds the menu links and deletes the local_task cache tag. + * + * @param \Symfony\Component\EventDispatcher\Event $event + * The event object. + */ + public function onRouterRebuild(Event $event) { + $this->menuLinksRebuild(); + Cache::deleteTags(array('local_task' => 1)); + } + + /** + * Perform menu-specific rebuilding. + */ + protected function menuLinksRebuild() { + if ($this->lock->acquire(__FUNCTION__)) { + $transaction = db_transaction(); + try { + // Ensure the menu links are up to date. + $this->menuLinkManager->rebuild(); + // Ignore any database replicas temporarily. + db_ignore_replica(); + } + catch (\Exception $e) { + $transaction->rollback(); + watchdog_exception('menu', $e); + } + + $this->lock->release(__FUNCTION__); + } + else { + // Wait for another request that is already doing this work. + // We choose to block here since otherwise the router item may not + // be available during routing resulting in a 404. + $this->lock->wait(__FUNCTION__); + } + } + + /** + * {@inheritdoc} + */ + static function getSubscribedEvents() { + $events[RoutingEvents::FINISHED][] = array('onRouterRebuild', 200); + return $events; + } + +} + diff --git a/core/lib/Drupal/Core/EventSubscriber/RouterRebuildSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RouterRebuildSubscriber.php index e27d836..69f49e4 100644 --- a/core/lib/Drupal/Core/EventSubscriber/RouterRebuildSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/RouterRebuildSubscriber.php @@ -18,7 +18,7 @@ use Symfony\Component\HttpKernel\KernelEvents; /** - * Rebuilds the default menu links and runs menu-specific code if necessary. + * Rebuilds the router if needed at the end of the request. */ class RouterRebuildSubscriber implements EventSubscriberInterface { @@ -28,31 +28,13 @@ class RouterRebuildSubscriber implements EventSubscriberInterface { protected $routeBuilder; /** - * @var \Drupal\Core\Lock\LockBackendInterface - */ - protected $lock; - - /** - * The menu link plugin manager - * - * @var \Drupal\Core\Menu\MenuLinkManagerInterface $menuLinkManager - */ - protected $menuLinkManager; - - /** * Constructs the RouterRebuildSubscriber object. * * @param \Drupal\Core\Routing\RouteBuilderInterface $route_builder * The route builder. - * @param \Drupal\Core\Lock\LockBackendInterface $lock - * The lock backend. - * @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager - * The menu link plugin manager. */ - public function __construct(RouteBuilderInterface $route_builder, LockBackendInterface $lock, MenuLinkManagerInterface $menu_link_manager) { + public function __construct(RouteBuilderInterface $route_builder) { $this->routeBuilder = $route_builder; - $this->lock = $lock; - $this->menuLinkManager = $menu_link_manager; } /** @@ -66,52 +48,10 @@ public function onKernelTerminate(PostResponseEvent $event) { } /** - * Rebuilds the menu links and deletes the local_task cache tag. - * - * @param \Symfony\Component\EventDispatcher\Event $event - * The event object. - */ - public function onRouterRebuild(Event $event) { - $this->menuLinksRebuild(); - Cache::deleteTags(array('local_task' => 1)); - } - - /** - * Perform menu-specific rebuilding. - */ - protected function menuLinksRebuild() { - if ($this->lock->acquire(__FUNCTION__)) { - $transaction = db_transaction(); - try { - // Ensure the menu links are up to date. - $this->menuLinkManager->rebuild(); - // Ignore any database replicas temporarily. - db_ignore_replica(); - } - catch (\Exception $e) { - $transaction->rollback(); - watchdog_exception('menu', $e); - } - - $this->lock->release(__FUNCTION__); - } - else { - // Wait for another request that is already doing this work. - // We choose to block here since otherwise the router item may not - // be available during routing resulting in a 404. - $this->lock->wait(__FUNCTION__); - } - } - - /** - * Registers the methods in this class that should be listeners. - * - * @return array - * An array of event listener definitions. + * {@inheritdoc} */ static function getSubscribedEvents() { $events[KernelEvents::TERMINATE][] = array('onKernelTerminate', 200); - $events[RoutingEvents::FINISHED][] = array('onRouterRebuild', 200); return $events; } diff --git a/core/lib/Drupal/Core/Menu/MenuLinkTree.php b/core/lib/Drupal/Core/Menu/MenuLinkTree.php index 8d09c2c..aae6efd 100644 --- a/core/lib/Drupal/Core/Menu/MenuLinkTree.php +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php @@ -8,7 +8,9 @@ namespace Drupal\Core\Menu; use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Controller\ControllerResolverInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteProviderInterface; /** @@ -45,6 +47,20 @@ class MenuLinkTree implements MenuLinkTreeInterface { protected $controllerResolver; /** + * The cache backend. + * + * @var \Drupal\Core\Cache\CacheBackendInterface + */ + protected $cache; + + /** + * The current route match. + * + * @var \Drupal\Core\Routing\RouteMatchInterface + */ + protected $routeMatch; + + /** * Constructs a \Drupal\Core\Menu\MenuLinkTree object. * * @param \Drupal\Core\Menu\MenuTreeStorageInterface $tree_storage @@ -57,31 +73,56 @@ class MenuLinkTree implements MenuLinkTreeInterface { * The active menu trail service. * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver * The controller resolver. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache + * The cache backend. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The current route match. */ - public function __construct(MenuTreeStorageInterface $tree_storage, MenuLinkManagerInterface $menu_link_manager, RouteProviderInterface $route_provider, MenuActiveTrailInterface $menu_active_trail, ControllerResolverInterface $controller_resolver) { + public function __construct(MenuTreeStorageInterface $tree_storage, MenuLinkManagerInterface $menu_link_manager, RouteProviderInterface $route_provider, MenuActiveTrailInterface $menu_active_trail, ControllerResolverInterface $controller_resolver, CacheBackendInterface $cache, RouteMatchInterface $route_match) { $this->treeStorage = $tree_storage; $this->menuLinkManager = $menu_link_manager; $this->routeProvider = $route_provider; $this->menuActiveTrail = $menu_active_trail; $this->controllerResolver = $controller_resolver; + // @todo Remove these two in https://www.drupal.org/node/1805054. + $this->cache = $cache; + $this->routeMatch = $route_match; } /** * {@inheritdoc} */ public function getCurrentRouteMenuTreeParameters($menu_name) { - $active_trail = $this->menuActiveTrail->getActiveTrailIds($menu_name); - - $parameters = new MenuTreeParameters(); - $parameters->setActiveTrail($active_trail) - // We want links in the active trail to be expanded. - ->addExpandedParents($active_trail) - // We marked the links in the active trail to be expanded, but we also - // want their descendants that have the "expanded" flag enabled to be - // expanded. - ->addExpandedParents($this->treeStorage->getExpanded($menu_name, $active_trail)); - - return $parameters; + static $cached_parameters = array(); + + // @todo Remove this non-static caching in https://www.drupal.org/node/1805054. + $route_parameters = $this->routeMatch->getRawParameters()->all(); + ksort($route_parameters); + $cid = 'current-route-parameters:' . $menu_name . ':route:' . $this->routeMatch->getRouteName() . ':route_parameters:' . serialize($route_parameters); + + if (!isset($cached_parameters[$menu_name])) { + $cache = $this->cache->get($cid); + if ($cache && $cache->data) { + $parameters = $cache->data; + } + else { + $active_trail = $this->menuActiveTrail->getActiveTrailIds($menu_name); + + $parameters = new MenuTreeParameters(); + $parameters->setActiveTrail($active_trail) + // We want links in the active trail to be expanded. + ->addExpandedParents($active_trail) + // We marked the links in the active trail to be expanded, but we also + // want their descendants that have the "expanded" flag enabled to be + // expanded. + ->addExpandedParents($this->treeStorage->getExpanded($menu_name, $active_trail)); + + $this->cache->set($cid, $parameters, CacheBackendInterface::CACHE_PERMANENT, array('menu' => $menu_name)); + } + $cached_parameters[$menu_name] = $parameters; + } + + return $cached_parameters[$menu_name]; } /** diff --git a/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php b/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php index 8b48499..e0e876e 100644 --- a/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php +++ b/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php @@ -44,7 +44,7 @@ function setUp() { * Tests that the settings UI works as expected. */ function testSettingsUI() { - // Check for the content_translation_menu_links_alter() changes. + // Check for the content_translation_menu_links_discovered_alter() changes. $this->drupalGet('admin/config'); $this->assertLink('Content language and translation'); $this->assertText('Configure language and translation support for content.'); diff --git a/core/modules/entity/src/Controller/EntityDisplayModeController.php b/core/modules/entity/src/Controller/EntityDisplayModeController.php index 9067f38..af31044 100644 --- a/core/modules/entity/src/Controller/EntityDisplayModeController.php +++ b/core/modules/entity/src/Controller/EntityDisplayModeController.php @@ -27,7 +27,7 @@ public function viewModeTypeSelection() { if ($entity_type->isFieldable() && $entity_type->hasViewBuilderClass()) { $entity_types[$entity_type_id] = array( 'title' => $entity_type->getLabel(), - 'url' => Url::createFromPath('admin/structure/display-modes/view/add/' . $entity_type_id), + 'url' => new Url('entity.view_mode_add_type', array('entity_type_id' => $entity_type_id)), 'localized_options' => array(), ); } @@ -50,7 +50,7 @@ public function formModeTypeSelection() { if ($entity_type->isFieldable() && $entity_type->hasFormClasses()) { $entity_types[$entity_type_id] = array( 'title' => $entity_type->getLabel(), - 'url' => Url::createFromPath('admin/structure/display-modes/form/add/' . $entity_type_id), + 'url' => new Url('entity.form_mode_add_type', array('entity_type_id' => $entity_type_id)), 'localized_options' => array(), ); } diff --git a/core/modules/menu_link_content/menu_link_content.install b/core/modules/menu_link_content/menu_link_content.install index adaf6dd..31fa93f 100644 --- a/core/modules/menu_link_content/menu_link_content.install +++ b/core/modules/menu_link_content/menu_link_content.install @@ -9,8 +9,15 @@ * Implements hook_uninstall(). */ function menu_link_content_uninstall() { - // Call the manager and delete all the plugins. - $uuids = db_query('SELECT uuid FROM {menu_link_content}')->fetchCol(); + // Find all the entities and then call the manager and delete all the plugins. + $query = \Drupal::entityQueryAggregate('menu_link_content'); + $query->groupBy('uuid'); + $result = $query->execute(); + $uuids = array(); + foreach ($result as $row) { + $uuids[] = $row['uuid']; + } + /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */ $menu_link_manager = \Drupal::service('plugin.manager.menu.link'); foreach ($uuids as $uuid) { diff --git a/core/modules/menu_ui/menu_ui.module b/core/modules/menu_ui/menu_ui.module index 109340e..d320826 100644 --- a/core/modules/menu_ui/menu_ui.module +++ b/core/modules/menu_ui/menu_ui.module @@ -445,7 +445,6 @@ function menu_ui_form_node_type_form_alter(&$form, $form_state) { $type = $form_state['controller']->getEntity(); if ($type->id()) { $config_values = \Drupal::config('menu.entity.node.' . $type->id())->get(); - //drupal_set_message(print_r($config_values,1)); } else { $config_values = array( @@ -542,7 +541,7 @@ function menu_ui_system_breadcrumb_alter(array &$breadcrumb, RouteMatchInterface if (($menu_link instanceof MenuLinkInterface)) { // Add a link to the menu admin screen. $menu = entity_load('menu', $menu_link->getMenuName()); - $breadcrumb[] = Drupal::l($menu->label(), 'menu_ui.menu_edit', array('menu' => $menu->id())); + $breadcrumb[] = \Drupal::l($menu->label(), 'menu_ui.menu_edit', array('menu' => $menu->id())); } } } diff --git a/core/modules/menu_ui/src/Controller/MenuController.php b/core/modules/menu_ui/src/Controller/MenuController.php index f8b1580..95788bd 100644 --- a/core/modules/menu_ui/src/Controller/MenuController.php +++ b/core/modules/menu_ui/src/Controller/MenuController.php @@ -9,7 +9,9 @@ use Drupal\Component\Utility\Xss; use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\Menu\MenuParentFormSelectorInterface; use Drupal\system\MenuInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -19,6 +21,30 @@ class MenuController extends ControllerBase { /** + * The menu parent form service. + * + * @var \Drupal\Core\Menu\MenuParentFormSelectorInterface + */ + protected $menuParentSelector; + + /** + * Creates a new MenuController object. + * + * @param \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_form + * The menu parent form service. + */ + public function __construct(MenuParentFormSelectorInterface $menu_parent_form) { + $this->menuParentSelector = $menu_parent_form; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('menu.parent_form_selector')); + } + + /** * Gets all the available menus and menu items as a JavaScript array. * * @param \Symfony\Component\HttpFoundation\Request $request @@ -34,9 +60,7 @@ public function getParentOptions(Request $request) { $available_menus[$menu] = $menu; } } - /** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */ - $menu_parent_selector = \Drupal::service('menu.parent_form_selector'); - $options = $menu_parent_selector->getParentSelectOptions('', $available_menus); + $options = $this->menuParentSelector->getParentSelectOptions('', $available_menus); return new JsonResponse($options); } diff --git a/core/modules/menu_ui/src/Form/MenuLinkResetForm.php b/core/modules/menu_ui/src/Form/MenuLinkResetForm.php index 2b727e3..4b47fce 100644 --- a/core/modules/menu_ui/src/Form/MenuLinkResetForm.php +++ b/core/modules/menu_ui/src/Form/MenuLinkResetForm.php @@ -34,7 +34,7 @@ class MenuLinkResetForm extends ConfirmFormBase { protected $link; /** - * Constructs a MenuLinkEditForm object. + * Constructs a MenuLinkResetForm object. * * @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager * The menu link manager. diff --git a/core/modules/menu_ui/src/MenuForm.php b/core/modules/menu_ui/src/MenuForm.php index 37c8265..5dcdd8b 100644 --- a/core/modules/menu_ui/src/MenuForm.php +++ b/core/modules/menu_ui/src/MenuForm.php @@ -16,6 +16,8 @@ use Drupal\Core\Menu\MenuLinkManagerInterface; use Drupal\Core\Menu\MenuTreeParameters; use Drupal\Core\Render\Element; +use Drupal\Core\Routing\UrlGeneratorTrait; +use Drupal\Core\Utility\LinkGeneratorInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -45,6 +47,13 @@ class MenuForm extends EntityForm { protected $menuTree; /** + * The link generator. + * + * @var \Drupal\Core\Utility\LinkGeneratorInterface + */ + protected $linkGenerator; + + /** * The overview tree form. * * @var array @@ -60,11 +69,14 @@ class MenuForm extends EntityForm { * The menu link manager. * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree * The menu tree service. + * @param \Drupal\Core\Utility\LinkGeneratorInterface $link_generator + * The link generator. */ - public function __construct(QueryFactory $entity_query_factory, MenuLinkManagerInterface $menu_link_manager, MenuLinkTreeInterface $menu_tree) { + public function __construct(QueryFactory $entity_query_factory, MenuLinkManagerInterface $menu_link_manager, MenuLinkTreeInterface $menu_tree, LinkGeneratorInterface $link_generator) { $this->entityQueryFactory = $entity_query_factory; $this->menuLinkManager = $menu_link_manager; $this->menuTree = $menu_tree; + $this->linkGenerator = $link_generator; } /** @@ -74,7 +86,8 @@ public static function create(ContainerInterface $container) { return new static( $container->get('entity.query'), $container->get('plugin.manager.menu.link'), - $container->get('menu.link_tree') + $container->get('menu.link_tree'), + $container->get('link_generator') ); } @@ -168,7 +181,7 @@ public function save(array $form, array &$form_state) { $status = $menu->save(); - $edit_link = \Drupal::linkGenerator()->generateFromUrl($this->t('Edit'), $this->entity->urlInfo()); + $edit_link = $this->linkGenerator->generateFromUrl($this->t('Edit'), $this->entity->urlInfo()); if ($status == SAVED_UPDATED) { drupal_set_message($this->t('Menu %label has been updated.', array('%label' => $menu->label()))); watchdog('menu', 'Menu %label has been updated.', array('%label' => $menu->label()), WATCHDOG_NOTICE, $edit_link); @@ -225,9 +238,9 @@ protected function buildOverviewForm(array &$form, array &$form_state) { $delta = max($count($tree), 50); $form = array_merge($form, $this->buildOverviewTreeForm($tree, $delta)); - $destination = $this->getUrlGenerator()->generateFromRoute('menu_ui.menu_edit', array('menu' => $this->entity->id())); - $link = $destination = $this->getUrlGenerator()->generateFromRoute('menu_ui.menu_add', array(), array('query' => array('destination' => $destination))); - $form['#empty_text'] = $this->t('There are no menu links yet. Add link.', array('@link' => $link)); + $destination = $this->getUrlGenerator()->getPathFromRoute('menu_ui.menu_edit', array('menu' => $this->entity->id())); + $url = $destination = $this->url('menu_link_content.link_add', array('menu' => $this->entity->id()), array('query' => array('destination' => $destination))); + $form['#empty_text'] = $this->t('There are no menu links yet. Add link.', array('@url' => $url)); return $form; } @@ -252,7 +265,7 @@ protected function buildOverviewTreeForm($tree, $delta) { $id = 'menu_plugin_id:' . $link->getPluginId(); $form[$id]['#item'] = $element; $form[$id]['#attributes'] = $link->isHidden() ? array('class' => array('menu-disabled')) : array('class' => array('menu-enabled')); - $form[$id]['title']['#markup'] = \Drupal::linkGenerator()->generateFromUrl($link->getTitle(), $link->getUrlObject(), $link->getOptions()); + $form[$id]['title']['#markup'] = $this->linkGenerator->generateFromUrl($link->getTitle(), $link->getUrlObject(), $link->getOptions()); if ($link->isHidden()) { $form[$id]['title']['#markup'] .= ' (' . $this->t('disabled') . ')'; } diff --git a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php index ee2734e..bd97e94 100644 --- a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php +++ b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php @@ -38,7 +38,7 @@ public function testMenuBlock() { )); $menu->save(); /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */ - $menu_link_manager = $this->container->get('plugin.manager.menu.link'); + $menu_link_manager = \Drupal::service('plugin.manager.menu.link'); // Move a link into the new menu. $menu_link = $menu_link_manager->updateDefinition('test_page_test.test_page', array('menu_name' => 'llama', 'parent' => '')); $block = $this->drupalPlaceBlock('system_menu_block:llama', array('label' => 'Llama', 'provider' => 'system', 'region' => 'footer')); diff --git a/core/modules/menu_ui/src/Tests/MenuTest.php b/core/modules/menu_ui/src/Tests/MenuTest.php index 6a24274..a2b4071 100644 --- a/core/modules/menu_ui/src/Tests/MenuTest.php +++ b/core/modules/menu_ui/src/Tests/MenuTest.php @@ -81,7 +81,7 @@ function testMenu() { // Verify that the menu links rebuild is idempotent and leaves the same // number of links in the table. /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */ - $menu_link_manager = $this->container->get('plugin.manager.menu.link'); + $menu_link_manager = \Drupal::service('plugin.manager.menu.link'); $before_count = $menu_link_manager->countMenuLinks(NULL); $menu_link_manager->rebuild(); $after_count = $menu_link_manager->countMenuLinks(NULL); @@ -729,7 +729,7 @@ private function getStandardMenuLink() { // Retrieve menu link id of the Log out menu link, which will always be on // the front page. /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */ - $menu_link_manager = $this->container->get('plugin.manager.menu.link'); + $menu_link_manager = \Drupal::service('plugin.manager.menu.link'); $result = $menu_link_manager->loadLinksByRoute('user.logout'); $instance = reset($result); diff --git a/core/modules/menu_ui/src/Tests/MenuWebTestBase.php b/core/modules/menu_ui/src/Tests/MenuWebTestBase.php index 9c4b627..6069197 100644 --- a/core/modules/menu_ui/src/Tests/MenuWebTestBase.php +++ b/core/modules/menu_ui/src/Tests/MenuWebTestBase.php @@ -32,7 +32,7 @@ function assertMenuLink($menu_plugin_id, array $expected_item) { // Retrieve menu link. /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */ - $menu_link_manager = $this->container->get('plugin.manager.menu.link'); + $menu_link_manager = \Drupal::service('plugin.manager.menu.link'); $menu_link_manager->resetDefinitions(); // Reset the static load cache. \Drupal::entityManager()->getStorage('menu_link_content')->resetCache(); @@ -43,9 +43,8 @@ function assertMenuLink($menu_plugin_id, array $expected_item) { // Pull the path from the menu link content. if (strpos($menu_plugin_id, 'menu_link_content') === 0) { list(, $uuid) = explode(':', $menu_plugin_id, 2); - $links = \Drupal::entityManager()->getStorage('menu_link_content')->loadByProperties(array('uuid' => $uuid)); - /** @var \Drupal\menu_link_content\Entity\MenuLinkContent $link */ - $entity = reset($links); + /** @var \Drupal\menu_link_content\Entity\MenuLinkContent $entity */ + $entity = \Drupal::entityManager()->loadEntityByUuid('menu_link_content', $uuid); } if (isset($expected_item['children'])) { diff --git a/core/modules/system/src/Controller/SystemController.php b/core/modules/system/src/Controller/SystemController.php index ba4833a..8881e9f 100644 --- a/core/modules/system/src/Controller/SystemController.php +++ b/core/modules/system/src/Controller/SystemController.php @@ -118,6 +118,7 @@ public function overview($link_id) { if ($this->systemManager->checkRequirements() && $this->currentUser()->hasPermission('administer site configuration')) { drupal_set_message($this->t('One or more problems were detected with your Drupal installation. Check the status report for more information.', array('@status' => url('admin/reports/status'))), 'error'); } + // Load all menu links below it. $parameters = new MenuTreeParameters(); $parameters->setRoot($link_id)->excludeRoot()->setTopLevelOnly()->excludeHiddenLinks(); $tree = $this->menuLinkTree->load(NULL, $parameters); @@ -127,7 +128,6 @@ public function overview($link_id) { ); $tree = $this->menuLinkTree->transform($tree, $manipulators); $blocks = array(); - // Load all menu links below it. foreach ($tree as $key => $element) { $link = $element->link; $block['title'] = $link->getTitle(); @@ -138,8 +138,6 @@ public function overview($link_id) { ); if (!empty($block['content']['#content'])) { - // Prepare for sorting as in function _menu_tree_check_access(). - // The weight is offset so it is always positive, with a uniform 5-digits. $blocks[$key] = $block; } } diff --git a/core/modules/system/src/Tests/Menu/LinksTest.php b/core/modules/system/src/Tests/Menu/LinksTest.php index c9d4f10..feb1907 100644 --- a/core/modules/system/src/Tests/Menu/LinksTest.php +++ b/core/modules/system/src/Tests/Menu/LinksTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Menu; +use Drupal\Component\Utility\String; use Drupal\locale\TranslationString; use Drupal\simpletest\WebTestBase; @@ -39,7 +40,7 @@ class LinksTest extends WebTestBase { public function setUp() { parent::setUp(); - $this->menuLinkManager = $this->container->get('plugin.manager.menu.link'); + $this->menuLinkManager = \Drupal::service('plugin.manager.menu.link'); entity_create('menu', array( 'id' => 'menu_test', @@ -119,7 +120,7 @@ function assertMenuLinkParents($links, $expected_hierarchy) { $menu_link_plugin = $this->menuLinkManager->createInstance($links[$id]); $expected_parent = isset($links[$parent]) ? $links[$parent] : ''; - $this->assertEqual($menu_link_plugin->getParent(), $expected_parent, format_string('Menu link %id has parent of %parent, expected %expected_parent.', array('%id' => $id, '%parent' => $menu_link_plugin->getParent(), '%expected_parent' => $expected_parent))); + $this->assertEqual($menu_link_plugin->getParent(), $expected_parent, String::format('Menu link %id has parent of %parent, expected %expected_parent.', array('%id' => $id, '%parent' => $menu_link_plugin->getParent(), '%expected_parent' => $expected_parent))); } } @@ -145,9 +146,8 @@ function testMenuLinkReparenting($module = 'menu_test') { /* @var \Drupal\Core\Menu\MenuLinkInterface $menu_link_plugin */ $this->menuLinkManager->updateDefinition($links['child-1'], array('parent' => $links['child-2'])); // Verify that the entity was updated too. - /* @var \Drupal\Core\Menu\MenuLinkInterface $menu_link_plugin */ $menu_link_plugin = $this->menuLinkManager->createInstance($links['child-1']); - $entity = entity_load_by_uuid('menu_link_content', $menu_link_plugin->getDerivativeId()); + $entity = \Drupal::entityManager()->loadEntityByUuid('menu_link_content', $menu_link_plugin->getDerivativeId()); $this->assertEqual($entity->getParentId(), $links['child-2']); $expected_hierarchy = array( diff --git a/core/modules/system/src/Tests/Menu/MenuRouterTest.php b/core/modules/system/src/Tests/Menu/MenuRouterTest.php index 5587aa5..8fe80e9 100644 --- a/core/modules/system/src/Tests/Menu/MenuRouterTest.php +++ b/core/modules/system/src/Tests/Menu/MenuRouterTest.php @@ -111,7 +111,7 @@ protected function doTestMenuName() { $admin_user = $this->drupalCreateUser(array('administer site configuration')); $this->drupalLogin($admin_user); /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */ - $menu_link_manager = $this->container->get('plugin.manager.menu.link'); + $menu_link_manager = \Drupal::service('plugin.manager.menu.link'); $menu_links = $menu_link_manager->loadLinksByRoute('menu_test.menu_name_test'); $menu_link = reset($menu_links); $this->assertEqual($menu_link->getMenuName(), 'original', 'Menu name is "original".'); @@ -133,7 +133,7 @@ protected function doTestMenuLinkDefaultsAlter() { // Check that machine name does not need to be defined since it is already // set as the key of each menu link. /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */ - $menu_link_manager = $this->container->get('plugin.manager.menu.link'); + $menu_link_manager = \Drupal::service('plugin.manager.menu.link'); $menu_links = $menu_link_manager->loadLinksByRoute('menu_test.custom'); $menu_link = reset($menu_links); $this->assertEqual($menu_link->getPluginId(), 'menu_test.custom', 'Menu links added at hook_menu_link_defaults_alter() obtain the machine name from the $links key.'); @@ -149,7 +149,7 @@ protected function doTestMenuLinkDefaultsAlter() { */ protected function doTestMenuHierarchy() { /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */ - $menu_link_manager = $this->container->get('plugin.manager.menu.link'); + $menu_link_manager = \Drupal::service('plugin.manager.menu.link'); $menu_links = $menu_link_manager->loadLinksByRoute('menu_test.hierarchy_parent'); $parent_link = reset($menu_links); $menu_links = $menu_link_manager->loadLinksByRoute('menu_test.hierarchy_parent.child'); diff --git a/core/modules/system/src/Tests/System/AdminTest.php b/core/modules/system/src/Tests/System/AdminTest.php index 35233c2..f5609c7 100644 --- a/core/modules/system/src/Tests/System/AdminTest.php +++ b/core/modules/system/src/Tests/System/AdminTest.php @@ -124,8 +124,7 @@ function testAdminPages() { * @return \Drupal\Core\Menu\MenuLinkInterface[] */ protected function getTopLevelMenuLinks() { - /** @var \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree */ - $menu_tree = $this->container->get('menu.link_tree'); + $menu_tree = \Drupal::menuTree(); // The system.admin link is normally the parent of all top-level admin links. $parameters = new MenuTreeParameters(); diff --git a/core/modules/system/templates/menu-tree.html.twig b/core/modules/system/templates/menu-tree.html.twig index 5560086..03acca6 100644 --- a/core/modules/system/templates/menu-tree.html.twig +++ b/core/modules/system/templates/menu-tree.html.twig @@ -35,6 +35,6 @@ {%- endif -%} {%- endif -%} - {{ tree }} + {{ tree }} {%- endif %} diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module index c486c9b..14397ad 100644 --- a/core/modules/toolbar/toolbar.module +++ b/core/modules/toolbar/toolbar.module @@ -437,9 +437,9 @@ function toolbar_prerender_toolbar_administration_tray(array $element) { * The manipulated menu link tree. */ function toolbar_menu_navigation_links(array $tree) { - foreach ($tree as $key => $element) { - if (!empty($element->subtree)) { - toolbar_menu_navigation_links($tree[$key]->subtree); + foreach ($tree as $element) { + if ($element->subtree) { + toolbar_menu_navigation_links($element->subtree); } // Make sure we have a path specific ID in place, so we can attach icons @@ -457,10 +457,11 @@ function toolbar_menu_navigation_links(array $tree) { // Get the non-localized title to make the icon class. $definition = $link->getPluginDefinition(); - $tree[$key]->options['attributes']['id'] = 'toolbar-link-' . $id; - $tree[$key]->options['attributes']['class'][] = 'toolbar-icon'; - $tree[$key]->options['attributes']['class'][] = 'toolbar-icon-' . strtolower(str_replace(' ', '-', $definition['title'])); - $tree[$key]->options['attributes']['title'] = String::checkPlain($link->getDescription()); + $element->options['attributes']['id'] = 'toolbar-link-' . $id; + $element->options['attributes']['class'][] = 'toolbar-icon'; + // @todo Change to use the plugin ID as class as titles might change. + $element->options['attributes']['class'][] = 'toolbar-icon-' . strtolower(str_replace(' ', '-', $definition['title'])); + $element->options['attributes']['title'] = String::checkPlain($link->getDescription()); } return $tree; } @@ -483,13 +484,15 @@ function toolbar_get_rendered_subtrees() { foreach ($tree as $element) { /** @var \Drupal\Core\Menu\MenuLinkInterface $item */ $link = $element->link; - if (!empty($element->subtree)) { + if ($element->subtree) { $subtree = $menu_tree->build($element->subtree); $output = drupal_render($subtree); } else { $output = ''; } + // Many routes have dots as route name, while some special ones like + // have <> characters in them. $id = str_replace(array('.', '<', '>'), array('-', '', '' ), $link->getUrlObject()->getRouteName()); $subtrees[$id] = $output; diff --git a/core/modules/user/src/Plugin/Menu/MyAccountMenuLink.php b/core/modules/user/src/Plugin/Menu/MyAccountMenuLink.php index ea0b4d9..bcbdebe 100644 --- a/core/modules/user/src/Plugin/Menu/MyAccountMenuLink.php +++ b/core/modules/user/src/Plugin/Menu/MyAccountMenuLink.php @@ -21,9 +21,9 @@ public function isHidden() { // The path 'user' must be accessible for anonymous users, but only visible // for authenticated users. Authenticated users should see "My account", but // anonymous users should not see it at all. - // @todo - re-write this as a link to user.view with dynamic route - // parameters to affect access since hidden should not be dynamic. - // https://www.drupal.org/node/2306991 + // @todo Re-write this as a link to user.view with dynamic route + // parameters to affect access since hidden should not be dynamic. + // https://www.drupal.org/node/2306991 return $this->pluginDefinition['hidden'] || \Drupal::currentUser()->isAnonymous(); } diff --git a/core/modules/user/src/Tests/UserAccountLinksTests.php b/core/modules/user/src/Tests/UserAccountLinksTests.php index f13b25a..e602120 100644 --- a/core/modules/user/src/Tests/UserAccountLinksTests.php +++ b/core/modules/user/src/Tests/UserAccountLinksTests.php @@ -62,8 +62,7 @@ function testSecondaryMenu() { $this->drupalGet(''); // For a logged-out user, expect no secondary links. - /** @var \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree */ - $menu_tree = $this->container->get('menu.link_tree'); + $menu_tree = \Drupal::menuTree(); $tree = $menu_tree->load('account', new MenuTreeParameters()); $manipulators = array( array('callable' => 'menu.default_tree_manipulators:checkAccess'), diff --git a/core/modules/views/src/Plugin/Derivative/ViewsMenuLink.php b/core/modules/views/src/Plugin/Derivative/ViewsMenuLink.php index fa7aa02..66fbaa9 100644 --- a/core/modules/views/src/Plugin/Derivative/ViewsMenuLink.php +++ b/core/modules/views/src/Plugin/Derivative/ViewsMenuLink.php @@ -34,13 +34,15 @@ public function getDerivativeDefinitions($base_plugin_definition) { // @todo Decide what to do with all the crazy logic in views_menu_alter() in // https://drupal.org/node/2107533. $links = array(); + // @todo Replaces uses_hook_menu with a different annotation. $views = Views::getApplicableViews('uses_hook_menu'); foreach ($views as $data) { /** @var \Drupal\views\ViewExecutable $view */ list($view, $display_id) = $data; - $result = $view->executeHookMenuLinks($display_id); - foreach ($result as $link_id => $link) { - $links[$link_id] = $link + $base_plugin_definition; + if ($result = $view->executeHookMenuLinks($display_id)) { + foreach ($result as $link_id => $link) { + $links[$link_id] = $link + $base_plugin_definition; + } } } diff --git a/core/modules/views/src/Plugin/Menu/Form/ViewsMenuLinkForm.php b/core/modules/views/src/Plugin/Menu/Form/ViewsMenuLinkForm.php index a6313b2..cfb3d9e 100644 --- a/core/modules/views/src/Plugin/Menu/Form/ViewsMenuLinkForm.php +++ b/core/modules/views/src/Plugin/Menu/Form/ViewsMenuLinkForm.php @@ -10,6 +10,14 @@ use Drupal\Core\Menu\Form\MenuLinkDefaultForm; use Drupal\views\Plugin\Menu\ViewsMenuLink; +/** + * Provides an edit form for the views. + * + * This provides the feature to change the title, in contrast to the static + * menu link form. + * + * @see \Drupal\views\Plugin\Menu\ViewsMenuLink + */ class ViewsMenuLinkForm extends MenuLinkDefaultForm { /** @@ -21,6 +29,7 @@ public function buildConfigurationForm(array $form, array &$form_state) { $form['title'] = array( '#type' => 'textfield', '#title' => $this->t('Title'), + // @todo how do we ensure that view is not loaded with a translation? '#default_value' => $this->menuLink->getTitle(), ); @@ -31,7 +40,7 @@ public function buildConfigurationForm(array $form, array &$form_state) { $id = $view->storage->id(); $label = $view->storage->label(); if ($this->moduleHandler->moduleExists('views_ui')) { - $message = $this->t('This link is provided by the Views module. The path can be changed by editing the view !editlink.', array('!editlink' => \Drupal::l($label, 'views_ui.edit', array('view' => $id)))); + $message = $this->t('This link is provided by the Views module. The path can be changed by editing the view @label', array('@url' => \Drupal::url('views_ui.edit', array('view' => $id)), '@label' => $label)); } else { $message = $this->t('This link is provided by the Views module from view %label.', array('%label' => $label)); diff --git a/core/modules/views/src/Plugin/Menu/ViewsMenuLink.php b/core/modules/views/src/Plugin/Menu/ViewsMenuLink.php index 9464bd8..a57fc9c 100644 --- a/core/modules/views/src/Plugin/Menu/ViewsMenuLink.php +++ b/core/modules/views/src/Plugin/Menu/ViewsMenuLink.php @@ -133,10 +133,15 @@ public function updateLink(array $new_definition_values, $persist) { if ($persist) { $view = $this->loadView(); $display = &$view->storage->getDisplay($view->current_display); - $display['display_options']['menu']['title'] = $new_definition_values['title']; - // @todo Note: This triggers a full rebuild of everything, even we just - // changed the title. - $view->storage->save(); + // Just save the title to the original view. + // @todo What do we do about the other properties, like weight, + // description and menu name. + if ($display['display_options']['menu']['title'] != $new_definition_values['title']) { + $display['display_options']['menu']['title'] = $new_definition_values['title']; + // @todo Note: This triggers a full rebuild of everything, even we just + // changed the title. + $view->storage->save(); + } } // Update the definition. $this->pluginDefinition = $overrides + $this->pluginDefinition; diff --git a/core/modules/views/src/Tests/Wizard/MenuTest.php b/core/modules/views/src/Tests/Wizard/MenuTest.php index 2fe1805..7281f9b 100644 --- a/core/modules/views/src/Tests/Wizard/MenuTest.php +++ b/core/modules/views/src/Tests/Wizard/MenuTest.php @@ -6,6 +6,7 @@ */ namespace Drupal\views\Tests\Wizard; +use Drupal\Component\Utility\String; /** * Tests the ability of the views wizard to put views in a menu. @@ -40,9 +41,13 @@ function testMenus() { // Make sure the link is associated with the main menu. /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */ - $menu_link_manager = $this->container->get('plugin.manager.menu.link'); - $link = $menu_link_manager->getDefinition('views_view:views.' . $view['id'] . '.page_1'); - $this->assertEqual($link['route_name'], 'view.' . $view['id'] . '.page_1', t('Found a link to %path in the main menu', array('%path' => $view['page[path]']))); + $menu_link_manager = \Drupal::service('plugin.manager.menu.link'); + /** @var \Drupal\Core\Menu\MenuLinkInterface $link */ + $link = $menu_link_manager->createInstance('views_view:views.' . $view['id'] . '.page_1'); + $url = $link->getUrlObject(); + $this->assertEqual($url->getRouteName(), 'view.' . $view['id'] . '.page_1', String::format('Found a link to %path in the main menu', array('%path' => $view['page[path]']))); + $metadata = $link->getMetaData(); + $this->assertEqual(array('view_id' => $view['id'], 'display_id' => 'page_1'), $metadata); } } diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php index 7f01574..08e876b 100644 --- a/core/modules/views/src/ViewExecutable.php +++ b/core/modules/views/src/ViewExecutable.php @@ -1513,10 +1513,10 @@ public function attachDisplays() { * * @param string $display_id * A display ID. - * @internal param array $links An array of default menu link items passed from* An array of default menu link items passed from - * views_menu_link_defaults_alter(). * * @return array|bool + * The generated menu links for this view and display, FALSE if the call + * to ::setDisplay failed. */ public function executeHookMenuLinks($display_id = NULL) { // Prepare the view with the information we have. This was probably already