diff --git a/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php b/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php index 9461683c15edd69827781a8135a210471f5c55ae..557f2d9194613de3e9252d83a3cc0cfa9fd0bdc6 100644 --- a/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php +++ b/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php @@ -206,15 +206,14 @@ protected function menuLinkCheckAccess(MenuLinkInterface $instance) { $access_result = AccessResult::allowed(); } else { - // Use the definition here since that's a lot faster than creating a Url - // object that we don't need. - $definition = $instance->getPluginDefinition(); - // 'url' should only be populated for external links. - if (!empty($definition['url']) && empty($definition['route_name'])) { + $url = $instance->getUrlObject(); + + // When no route name is specified, this must be an external link. + if (!$url->isRouted()) { $access_result = AccessResult::allowed(); } else { - $access_result = $this->accessManager->checkNamedRoute($definition['route_name'], $definition['route_parameters'], $this->account, TRUE); + $access_result = $this->accessManager->checkNamedRoute($url->getRouteName(), $url->getRouteParameters(), $this->account, TRUE); } } return $access_result->cachePerPermissions(); diff --git a/core/lib/Drupal/Core/Menu/MenuLinkBase.php b/core/lib/Drupal/Core/Menu/MenuLinkBase.php index 6ecfbdf58dae3f2e4b3db1558f20b9d91e1fe5ab..8f1c112bf8e4b86bddaffad3562c41059a0b39a0 100644 --- a/core/lib/Drupal/Core/Menu/MenuLinkBase.php +++ b/core/lib/Drupal/Core/Menu/MenuLinkBase.php @@ -130,7 +130,7 @@ public function getUrlObject($title_attribute = TRUE) { $options['attributes']['title'] = $description; } if (empty($this->pluginDefinition['url'])) { - return new Url($this->pluginDefinition['route_name'], $this->pluginDefinition['route_parameters'], $options); + return new Url($this->getRouteName(), $this->getRouteParameters(), $options); } else { return Url::fromUri($this->pluginDefinition['url'], $options); diff --git a/core/modules/menu_ui/src/MenuForm.php b/core/modules/menu_ui/src/MenuForm.php index 9fc453c986fb90c7243de852c51cc8aa0d266a18..efd1451717b1aba4464e11823bbe62ed09331ddd 100644 --- a/core/modules/menu_ui/src/MenuForm.php +++ b/core/modules/menu_ui/src/MenuForm.php @@ -358,6 +358,11 @@ protected function buildOverviewTreeForm($tree, $delta) { if (!$link->isEnabled()) { $form[$id]['title']['#suffix'] = ' (' . $this->t('disabled') . ')'; } + // @todo Remove this in https://www.drupal.org/node/2568785. + elseif ($id === 'menu_plugin_id:user.logout') { + $form[$id]['title']['#suffix'] = ' (' . $this->t('<q>Log in</q> for anonymous users') . ')'; + } + // @todo Remove this in https://www.drupal.org/node/2568785. elseif (($url = $link->getUrlObject()) && $url->isRouted() && $url->getRouteName() == 'user.page') { $form[$id]['title']['#suffix'] = ' (' . $this->t('logged in users only') . ')'; } diff --git a/core/modules/menu_ui/src/Tests/MenuTest.php b/core/modules/menu_ui/src/Tests/MenuTest.php index ef79989514fa7901631fdd14eb743aa82bf38817..326db813c0f7d5ceb3b7a3db94472a282f7b92d8 100644 --- a/core/modules/menu_ui/src/Tests/MenuTest.php +++ b/core/modules/menu_ui/src/Tests/MenuTest.php @@ -893,8 +893,7 @@ private function getStandardMenuLink() { // the front page. /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */ $menu_link_manager = \Drupal::service('plugin.manager.menu.link'); - $result = $menu_link_manager->loadLinksByRoute('user.logout'); - $instance = reset($result); + $instance = $menu_link_manager->getInstance(['id' => 'user.logout']); $this->assertTrue((bool) $instance, 'Standard menu link was loaded'); return $instance; diff --git a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php index ed7b0f3ce47e83009294eb3e1b40c4dc4179c7a1..22ae7b2b40512605d659d0119c185dd048cd2dcf 100644 --- a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php +++ b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php @@ -89,7 +89,6 @@ function testPageCacheTags() { 'config:block.block.bartik_breadcrumbs', 'config:block.block.bartik_content', 'config:block.block.bartik_tools', - 'config:block.block.bartik_login', 'config:block.block.bartik_footer', 'config:block.block.bartik_help', 'config:block.block.bartik_search', @@ -106,6 +105,7 @@ function testPageCacheTags() { 'user:0', 'user:' . $author_1->id(), 'config:filter.format.basic_html', + 'config:search.settings', 'config:system.menu.account', 'config:system.menu.tools', 'config:system.menu.footer', @@ -128,7 +128,6 @@ function testPageCacheTags() { 'config:block.block.bartik_breadcrumbs', 'config:block.block.bartik_content', 'config:block.block.bartik_tools', - 'config:block.block.bartik_login', 'config:block.block.bartik_help', 'config:block.block.bartik_search', 'config:block.block.' . $block->id(), @@ -144,6 +143,7 @@ function testPageCacheTags() { 'node:' . $node_2->id(), 'user:' . $author_2->id(), 'config:filter.format.full_html', + 'config:search.settings', 'config:system.menu.account', 'config:system.menu.tools', 'config:system.menu.footer', diff --git a/core/modules/user/src/Plugin/Menu/LoginLogoutMenuLink.php b/core/modules/user/src/Plugin/Menu/LoginLogoutMenuLink.php new file mode 100644 index 0000000000000000000000000000000000000000..278545cecda895d9e75069dad3c3c3ab2eec09d5 --- /dev/null +++ b/core/modules/user/src/Plugin/Menu/LoginLogoutMenuLink.php @@ -0,0 +1,91 @@ +<?php + +/** + * @file + * Contains \Drupal\user\Plugin\Menu\MyAccountMenuLink. + */ + +namespace Drupal\user\Plugin\Menu; + +use Drupal\Core\Menu\MenuLinkDefault; +use Drupal\Core\Menu\StaticMenuLinkOverridesInterface; +use Drupal\Core\Session\AccountInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * A menu link that shows "Log in" or "Log out" as appropriate. + */ +class LoginLogoutMenuLink extends MenuLinkDefault { + + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $currentUser; + + /** + * Constructs a new LoginLogoutMenuLink. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Menu\StaticMenuLinkOverridesInterface $static_override + * The static override storage. + * @param \Drupal\Core\Session\AccountInterface $current_user + * The current user. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, StaticMenuLinkOverridesInterface $static_override, AccountInterface $current_user) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $static_override); + + $this->currentUser = $current_user; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('menu_link.static.overrides'), + $container->get('current_user') + ); + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + if ($this->currentUser->isAuthenticated()) { + return $this->t('Log out'); + } + else { + return $this->t('Log in'); + } + } + + /** + * {@inheritdoc} + */ + public function getRouteName() { + if ($this->currentUser->isAuthenticated()) { + return 'user.logout'; + } + else { + return 'user.login'; + } + } + + /** + * {@inheritdoc} + */ + public function getCacheContexts() { + return ['user.roles:authenticated']; + } + +} diff --git a/core/modules/user/src/Tests/UserAccountLinksTest.php b/core/modules/user/src/Tests/UserAccountLinksTest.php index 0a6f3d764f1fdb3abcec6f337cebe5e2f0d2275e..cb3ea8871fcfd171c08834daa06c33e95aa60dae 100644 --- a/core/modules/user/src/Tests/UserAccountLinksTest.php +++ b/core/modules/user/src/Tests/UserAccountLinksTest.php @@ -65,11 +65,13 @@ function testSecondaryMenu() { $this->drupalLogout(); $this->drupalGet('<front>'); - // For a logged-out user, expect no secondary links. - $menu = $this->xpath('//ul[@class=:menu_class]', array( + // For a logged-out user, expect the secondary menu to have a "Log in" link. + $link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', array( ':menu_class' => 'menu', + ':href' => 'user/login', + ':text' => 'Log in', )); - $this->assertEqual(count($menu), 0, 'The secondary links menu is not rendered, because none of its menu links are accessible for the anonymous user.'); + $this->assertEqual(count($link), 1, 'Log in link is in secondary menu.'); } /** diff --git a/core/modules/user/user.links.menu.yml b/core/modules/user/user.links.menu.yml index 3e015ee18af0cf306bf45aeec808b3d9686e1ee2..bceb59bd1a07d914e1b768a889f262bce4e9483f 100644 --- a/core/modules/user/user.links.menu.yml +++ b/core/modules/user/user.links.menu.yml @@ -4,10 +4,9 @@ user.page: route_name: user.page menu_name: account user.logout: - title: 'Log out' - route_name: user.logout weight: 10 menu_name: account + class: Drupal\user\Plugin\Menu\LoginLogoutMenuLink entity.user.collection: title: People route_name: entity.user.collection diff --git a/core/profiles/standard/config/install/block.block.bartik_login.yml b/core/profiles/standard/config/install/block.block.bartik_login.yml deleted file mode 100644 index 73ee47057d317ce4be9c30e84aa96e9b355412a3..0000000000000000000000000000000000000000 --- a/core/profiles/standard/config/install/block.block.bartik_login.yml +++ /dev/null @@ -1,19 +0,0 @@ -langcode: en -status: true -dependencies: - module: - - user - theme: - - bartik -id: bartik_login -theme: bartik -region: sidebar_first -weight: 0 -provider: null -plugin: user_login_block -settings: - id: user_login_block - label: 'User login' - provider: user - label_display: visible -visibility: { } diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install index 4d4416ae6d006e0e88ebfdad7887ce32a6fadaa2..fa8c7560be1b0996b12bdb76b79376f23b17fe31 100644 --- a/core/profiles/standard/standard.install +++ b/core/profiles/standard/standard.install @@ -60,6 +60,10 @@ function standard_install() { )); $shortcut->save(); + // Allow all users to use search. + user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, array('search content')); + user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, array('search content')); + // Enable the admin theme. \Drupal::configFactory()->getEditable('node.settings')->set('use_admin_theme', TRUE)->save(TRUE); } diff --git a/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php b/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php index 17702e7343966e66f92ddbcb94e18265613a9dc8..a748c5b71b0d398710c683568a49619f90f66b04 100644 --- a/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php @@ -73,6 +73,8 @@ protected function setUp() { $this->accessManager = $this->getMock('\Drupal\Core\Access\AccessManagerInterface'); $this->currentUser = $this->getMock('Drupal\Core\Session\AccountInterface'); + $this->currentUser->method('isAuthenticated') + ->willReturn(TRUE); $this->queryFactory = $this->getMockBuilder('Drupal\Core\Entity\Query\QueryFactory') ->disableOriginalConstructor() ->getMock(); @@ -97,6 +99,7 @@ protected function setUp() { * - 7 * - 6 * - 8 + * - 9 * * With link 6 being the only external link. */ @@ -110,6 +113,7 @@ protected function mockTree() { 6 => MenuLinkMock::create(array('id' => 'test.example6', 'route_name' => '', 'url' => 'https://www.drupal.org/', 'title' => 'barbar', 'parent' => '')), 7 => MenuLinkMock::create(array('id' => 'test.example7', 'route_name' => 'example7', 'title' => 'bazbaz', 'parent' => '')), 8 => MenuLinkMock::create(array('id' => 'test.example8', 'route_name' => 'example8', 'title' => 'quxqux', 'parent' => '')), + 9 => DynamicMenuLinkMock::create(array('id' => 'test.example9', 'parent' => ''))->setCurrentUser($this->currentUser), ); $this->originalTree = array(); $this->originalTree[1] = new MenuLinkTreeElement($this->links[1], FALSE, 1, FALSE, array()); @@ -123,6 +127,7 @@ protected function mockTree() { )); $this->originalTree[6] = new MenuLinkTreeElement($this->links[6], FALSE, 1, FALSE, array()); $this->originalTree[8] = new MenuLinkTreeElement($this->links[8], FALSE, 1, FALSE, array()); + $this->originalTree[9] = new MenuLinkTreeElement($this->links[9], FALSE, 1, FALSE, array()); } /** @@ -156,16 +161,17 @@ public function testGenerateIndexAndSort() { */ public function testCheckAccess() { // Those menu links that are non-external will have their access checks - // performed. 8 routes, but 1 is external, 2 already have their 'access' - // property set, and 1 is a child if an inaccessible menu link, so only 4 + // performed. 9 routes, but 1 is external, 2 already have their 'access' + // property set, and 1 is a child if an inaccessible menu link, so only 5 // calls will be made. - $this->accessManager->expects($this->exactly(4)) + $this->accessManager->expects($this->exactly(5)) ->method('checkNamedRoute') ->will($this->returnValueMap(array( array('example1', array(), $this->currentUser, TRUE, AccessResult::forbidden()), array('example2', array('foo' => 'bar'), $this->currentUser, TRUE, AccessResult::allowed()->cachePerPermissions()), array('example3', array('baz' => 'qux'), $this->currentUser, TRUE, AccessResult::neutral()), array('example5', array(), $this->currentUser, TRUE, AccessResult::allowed()), + array('user.logout', array(), $this->currentUser, TRUE, AccessResult::allowed()), ))); $this->mockTree(); @@ -227,7 +233,7 @@ public function testCheckAccess() { */ public function testCheckAccessWithLinkToAnyPagePermission() { $this->mockTree(); - $this->currentUser->expects($this->exactly(8)) + $this->currentUser->expects($this->exactly(9)) ->method('hasPermission') ->with('link to any page') ->willReturn(TRUE); @@ -243,6 +249,7 @@ public function testCheckAccessWithLinkToAnyPagePermission() { $this->assertEquals($expected_access_result, $this->originalTree[5]->subtree[7]->access); $this->assertEquals($expected_access_result, $this->originalTree[6]->access); $this->assertEquals($expected_access_result, $this->originalTree[8]->access); + $this->assertEquals($expected_access_result, $this->originalTree[9]->access); } /** @@ -253,8 +260,8 @@ public function testCheckAccessWithLinkToAnyPagePermission() { public function testFlatten() { $this->mockTree(); $tree = $this->defaultMenuTreeManipulators->flatten($this->originalTree); - $this->assertEquals(array(1, 2, 5, 6, 8), array_keys($this->originalTree)); - $this->assertEquals(array(1, 2, 5, 6, 8, 3, 4, 7), array_keys($tree)); + $this->assertEquals(array(1, 2, 5, 6, 8, 9), array_keys($this->originalTree)); + $this->assertEquals(array(1, 2, 5, 6, 8, 9, 3, 4, 7), array_keys($tree)); } /** diff --git a/core/tests/Drupal/Tests/Core/Menu/DynamicMenuLinkMock.php b/core/tests/Drupal/Tests/Core/Menu/DynamicMenuLinkMock.php new file mode 100644 index 0000000000000000000000000000000000000000..9a8d23e1b6186e59c86f87777b45e85ada076ef3 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Menu/DynamicMenuLinkMock.php @@ -0,0 +1,75 @@ +<?php + +/** + * @file + * Contains \Drupal\Tests\Core\Menu\DynamicMenuLinkMock. + */ + +namespace Drupal\Tests\Core\Menu; + +use Drupal\Core\Session\AccountInterface; + +/** + * Defines a mock implementation of a dynamic menu link used in tests only. + * + * Has a dynamic route and title. This is rather contrived, but there are valid + * use cases. + * + * @see \Drupal\user\Plugin\Menu\LoginLogoutMenuLink + */ +class DynamicMenuLinkMock extends MenuLinkMock { + + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $currentUser; + + /** + * Sets the current user. + * + * Allows the menu link to return the right title and route. + * + * @param \Drupal\Core\Session\AccountInterface $current_user + * The current user. + * + * @return $this + */ + public function setCurrentUser(AccountInterface $current_user) { + $this->currentUser = $current_user; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + if ($this->currentUser->isAuthenticated()) { + return 'Log out'; + } + else { + return 'Log in'; + } + } + + /** + * {@inheritdoc} + */ + public function getRouteName() { + if ($this->currentUser->isAuthenticated()) { + return 'user.logout'; + } + else { + return 'user.login'; + } + } + + /** + * {@inheritdoc} + */ + public function getCacheContexts() { + return ['user.roles:authenticated']; + } + +} diff --git a/core/themes/bartik/css/colors.css b/core/themes/bartik/css/colors.css index 0c722b2ffccda3ffc1481bedba721fce816c9528..41dc0546cbde21f2e6d0fc98e8dcd2b52bb6ef7c 100644 --- a/core/themes/bartik/css/colors.css +++ b/core/themes/bartik/css/colors.css @@ -50,7 +50,8 @@ a:active, .site-branding, .site-branding__text a, .site-branding a, -.region-secondary-menu .menu-item a { +.region-secondary-menu .menu-item a, +.region-secondary-menu .menu-item a.is-active { color: #fffeff; }