From 9b3b1c6b5529d1ff5ed8073e5c3d7b412ce4b7ae Mon Sep 17 00:00:00 2001
From: nod_ <nod_@598310.no-reply.drupal.org>
Date: Thu, 23 Jan 2025 15:24:53 +0100
Subject: [PATCH] Issue #3488293 by oily, maneesha binish, plopesc, m4olivei,
 pameeela: Help link always appears in navigation

---
 .../src/Plugin/Block/NavigationLinkBlock.php  |   4 +
 .../Functional/NavigationLinkBlockTest.php    | 164 ++++++++++++++----
 2 files changed, 139 insertions(+), 29 deletions(-)

diff --git a/core/modules/navigation/src/Plugin/Block/NavigationLinkBlock.php b/core/modules/navigation/src/Plugin/Block/NavigationLinkBlock.php
index 0043e54d5dd9..044c595dace4 100644
--- a/core/modules/navigation/src/Plugin/Block/NavigationLinkBlock.php
+++ b/core/modules/navigation/src/Plugin/Block/NavigationLinkBlock.php
@@ -246,6 +246,10 @@ public function build(): array {
     // Ensure that user has access to link before rendering it.
     try {
       $url = Url::fromUri($config['uri']);
+      // Internal routes must exist.
+      if (!$url->isExternal() && !$url->isRouted()) {
+        return $build;
+      }
       $access = $url->access(NULL, TRUE);
       if (!$access->isAllowed()) {
         // Cacheable dependency is explicitly added when access is not granted.
diff --git a/core/modules/navigation/tests/src/Functional/NavigationLinkBlockTest.php b/core/modules/navigation/tests/src/Functional/NavigationLinkBlockTest.php
index 58388edf450e..0f385c5f1caa 100644
--- a/core/modules/navigation/tests/src/Functional/NavigationLinkBlockTest.php
+++ b/core/modules/navigation/tests/src/Functional/NavigationLinkBlockTest.php
@@ -21,7 +21,7 @@ class NavigationLinkBlockTest extends PageCacheTagsTestBase {
   /**
    * {@inheritdoc}
    */
-  protected static $modules = ['navigation', 'test_page_test', 'block'];
+  protected static $modules = ['navigation', 'test_page_test', 'entity_test'];
 
   /**
    * {@inheritdoc}
@@ -53,42 +53,24 @@ protected function setUp(): void {
       'access administration pages',
       'administer site configuration',
       'access navigation',
+      'view test entity',
     ]);
 
     // Create additional users to test caching modes.
     $this->normalUser = $this->drupalCreateUser([
       'access navigation',
     ]);
-
-    // Add programmatically a link block to the navigation.
-    $section_storage_manager = \Drupal::service('plugin.manager.layout_builder.section_storage');
-    $cacheability = new CacheableMetadata();
-    $contexts = [
-      'navigation' => new Context(ContextDefinition::create('string'), 'navigation'),
-    ];
-    /** @var \Drupal\layout_builder\SectionListInterface $section_list */
-    $section_list = $section_storage_manager->findByContext($contexts, $cacheability);
-    $section = $section_list->getSection(0);
-
-    $section->appendComponent(new SectionComponent(\Drupal::service('uuid')->generate(), 'content', [
-      'id' => 'navigation_link',
-      'label' => 'Admin Main Page',
-      'label_display' => '0',
-      'provider' => 'navigation',
-      'context_mapping' => [],
-      'title' => 'Navigation Settings',
-      'uri' => 'internal:/admin/config/user-interface/navigation/settings',
-      'icon_class' => 'admin-link',
-    ]));
-
-    $section_list->save();
   }
 
   /**
    * Test output of the link navigation with regards to caching and contents.
    */
-  public function testNavigationLinkBlock(): void {
-
+  public function testNavigationLinkBlockCache(): void {
+    $label = 'Admin Main Page';
+    $link_title = 'Navigation Settings';
+    $link_uri = '/admin/config/user-interface/navigation/settings';
+    $link_icon = 'admin-link';
+    $this->appendNavigationLinkBlock($label, $link_title, 'internal:' . $link_uri, $link_icon);
     // Verify some basic cacheability metadata. Ensures that we're not doing
     // anything so egregious as to upset expected caching behavior. In this
     // case, as an anonymous user, we should have zero effect on the page.
@@ -102,8 +84,10 @@ public function testNavigationLinkBlock(): void {
     $this->verifyDynamicPageCache($test_page_url, 'MISS');
     $this->verifyDynamicPageCache($test_page_url, 'HIT');
     // We should not see the admin page link in the page.
-    $link_selector = '.admin-toolbar__item .toolbar-button--icon--admin-link';
+    $link_selector = '.admin-toolbar__item .toolbar-button--icon--' . $link_icon;
     $this->assertSession()->elementNotExists('css', $link_selector);
+    $this->assertSession()->pageTextNotContains($link_title);
+    $this->assertSession()->pageTextNotContains($label);
 
     // Login as a different user, UI should update.
     $this->drupalLogin($this->adminUser);
@@ -112,14 +96,136 @@ public function testNavigationLinkBlock(): void {
     $this->drupalGet(Url::fromRoute('navigation.settings'));
     $this->assertSession()->statusCodeEquals(200);
     $this->assertSession()->elementExists('css', $link_selector);
+    $this->assertSession()->pageTextContains($link_title);
+    $this->assertSession()->pageTextContains($label);
     $this->assertSession()
-      ->elementTextContains('css', $link_selector, 'Navigation Settings');
+      ->elementTextContains('css', $link_selector, $link_title);
     // The link should link to the admin page.
     $link = $this->getSession()->getPage()->find('named', [
       'link',
-      'Navigation Settings',
+      $link_title,
     ]);
     $this->assertStringContainsString('/admin/config/user-interface/navigation/settings', $link->getAttribute('href'));
   }
 
+  /**
+   * Test block visibility based on the link access logic.
+   */
+  public function testNavigationLinkBlockVisibility(): void {
+    // Add a link to an external URL.
+    $external_label = 'External Link Block';
+    $external_link_title = 'Link to example';
+    $this->appendNavigationLinkBlock($external_label, $external_link_title, 'http://example.com', 'external');
+    // Create an entity and create a link to it.
+    $entity_type_manager = \Drupal::entityTypeManager();
+    $entity_test_storage = $entity_type_manager->getStorage('entity_test');
+    $entity_test_link = $entity_test_storage->create(['name' => 'test']);
+    $entity_test_link->save();
+    $entity_label = 'Entity Link BLock';
+    $entity_link_title = 'Link to entity';
+    $this->appendNavigationLinkBlock($entity_label, $entity_link_title, 'entity:entity_test/' . $entity_test_link->id(), 'entity');
+    // Link to admin page.
+    $admin_label = 'Admin Main Page';
+    $admin_link_title = 'Navigation Settings';
+    $this->appendNavigationLinkBlock($admin_label, $admin_link_title, 'internal:/admin/config/user-interface/navigation/settings', 'admin');
+    // Link to generic internal page (Help Link).
+    $help_label = 'Help Block';
+    $help_link_title = 'Link to help';
+    $this->appendNavigationLinkBlock($help_label, $help_link_title, 'internal:/admin/help', 'internal');
+
+    // Admin user should be capable to access to all the links but the internal
+    // one, since Help module is not enabled.
+    $test_page_url = Url::fromRoute('test_page_test.test_page');
+    $this->drupalLogin($this->adminUser);
+    $this->drupalGet($test_page_url);
+
+    $this->assertSession()->pageTextContains($external_label);
+    $this->assertSession()->pageTextContains($external_link_title);
+    $this->assertSession()->pageTextContains($entity_label);
+    $this->assertSession()->pageTextContains($entity_link_title);
+    $this->assertSession()->pageTextContains($admin_label);
+    $this->assertSession()->pageTextContains($admin_link_title);
+    $this->assertSession()->pageTextNotContains($help_label);
+    $this->assertSession()->pageTextNotContains($help_link_title);
+
+    // Normal user should not have access only to the external link.
+    $this->drupalLogin($this->normalUser);
+    $this->drupalGet($test_page_url);
+
+    $this->assertSession()->pageTextContains($external_label);
+    $this->assertSession()->pageTextContains($external_link_title);
+    $this->assertSession()->pageTextNotContains($entity_label);
+    $this->assertSession()->pageTextNotContains($entity_link_title);
+    $this->assertSession()->pageTextNotContains($admin_label);
+    $this->assertSession()->pageTextNotContains($admin_link_title);
+    $this->assertSession()->pageTextNotContains($help_label);
+    $this->assertSession()->pageTextNotContains($help_link_title);
+
+    // Enable Help module and grant permissions to admin user.
+    // Admin user should be capable to access to all the links
+    \Drupal::service('module_installer')->install(['help']);
+    $this->adminUser->addRole($this->drupalCreateRole(['access help pages']))->save();
+
+    $this->drupalLogin($this->adminUser);
+    $this->drupalGet($test_page_url);
+
+    $this->assertSession()->pageTextContains($external_label);
+    $this->assertSession()->pageTextContains($external_link_title);
+    $this->assertSession()->pageTextContains($entity_label);
+    $this->assertSession()->pageTextContains($entity_link_title);
+    $this->assertSession()->pageTextContains($admin_label);
+    $this->assertSession()->pageTextContains($admin_link_title);
+    $this->assertSession()->pageTextContains($help_label);
+    $this->assertSession()->pageTextContains($help_link_title);
+
+    // Normal user should not have access only to the external link.
+    $this->drupalLogin($this->normalUser);
+    $this->drupalGet($test_page_url);
+
+    $this->assertSession()->pageTextContains($external_label);
+    $this->assertSession()->pageTextContains($external_link_title);
+    $this->assertSession()->pageTextNotContains($entity_label);
+    $this->assertSession()->pageTextNotContains($entity_link_title);
+    $this->assertSession()->pageTextNotContains($admin_label);
+    $this->assertSession()->pageTextNotContains($admin_link_title);
+    $this->assertSession()->pageTextNotContains($help_label);
+    $this->assertSession()->pageTextNotContains($help_link_title);
+  }
+
+  /**
+   * Adds a Navigation Link Block to the sidebar.
+   *
+   * @param string $label
+   *   The block label.
+   * @param string $link_title
+   *   The link title.
+   * @param string $link_uri
+   *   The link uri.
+   * @param string $link_icon
+   *   The link icon CSS class.
+   */
+  protected function appendNavigationLinkBlock(string $label, string $link_title, string $link_uri, string $link_icon): void {
+    $section_storage_manager = \Drupal::service('plugin.manager.layout_builder.section_storage');
+    $cacheability = new CacheableMetadata();
+    $contexts = [
+      'navigation' => new Context(ContextDefinition::create('string'), 'navigation'),
+    ];
+    /** @var \Drupal\layout_builder\SectionListInterface $section_list */
+    $section_list = $section_storage_manager->findByContext($contexts, $cacheability);
+    $section = $section_list->getSection(0);
+
+    $section->appendComponent(new SectionComponent(\Drupal::service('uuid')->generate(), 'content', [
+      'id' => 'navigation_link',
+      'label' => $label,
+      'label_display' => '1',
+      'provider' => 'navigation',
+      'context_mapping' => [],
+      'title' => $link_title,
+      'uri' => $link_uri,
+      'icon_class' => $link_icon,
+    ]));
+
+    $section_list->save();
+  }
+
 }
-- 
GitLab