From ebeda1098402cbd6b034b808436fa4929193b167 Mon Sep 17 00:00:00 2001
From: Patrick Fey <14024-feyp@users.noreply.drupalcode.org>
Date: Sat, 20 Jul 2024 19:52:38 +0000
Subject: [PATCH] Issue #3458373 by FeyP, lelkneralfaro, dshields: [10.3
 Regression] Menu containing language switcher link non-editable

---
 language_switcher_menu.info.yml               |  2 +-
 language_switcher_menu.services.yml           |  1 +
 src/Form/SettingsForm.php                     |  8 ++-
 src/LanguageLinkAccessMenuTreeManipulator.php |  7 +-
 src/Plugin/Menu/LanguageSwitcherLink.php      |  2 +-
 .../test_language_switcher_menu.info.yml      |  5 ++
 .../test_language_switcher_menu.module        | 20 ++++++
 .../Functional/LanguageSwitcherMenuTest.php   | 72 ++++++++++++++++---
 8 files changed, 103 insertions(+), 14 deletions(-)
 create mode 100644 tests/modules/test_language_switcher_menu/test_language_switcher_menu.info.yml
 create mode 100644 tests/modules/test_language_switcher_menu/test_language_switcher_menu.module

diff --git a/language_switcher_menu.info.yml b/language_switcher_menu.info.yml
index e942428..c2b613f 100644
--- a/language_switcher_menu.info.yml
+++ b/language_switcher_menu.info.yml
@@ -4,7 +4,7 @@ description: "Allows you to add language switcher links to a menu."
 configure: language_switcher_menu.configure
 
 type: module
-core_version_requirement: ^9 || ^10
+core_version_requirement: ^10.1
 php: 7.4
 
 dependencies:
diff --git a/language_switcher_menu.services.yml b/language_switcher_menu.services.yml
index 1fd2257..ebb4c1d 100644
--- a/language_switcher_menu.services.yml
+++ b/language_switcher_menu.services.yml
@@ -5,4 +5,5 @@ services:
       - '@access_manager'
       - '@current_user'
       - '@entity_type.manager'
+      - '@module_handler'
       - '@path.validator'
diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php
index 2b7a8ea..6db2a9a 100644
--- a/src/Form/SettingsForm.php
+++ b/src/Form/SettingsForm.php
@@ -3,6 +3,7 @@
 namespace Drupal\language_switcher_menu\Form;
 
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\Form\FormStateInterface;
@@ -49,6 +50,8 @@ class SettingsForm extends ConfigFormBase {
    *
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   The factory for configuration objects.
+   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager
+   *   The typed config manager.
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
    *   The entity type manager.
    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
@@ -58,8 +61,8 @@ class SettingsForm extends ConfigFormBase {
    * @param \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_form_selector
    *   The menu parent form selector.
    */
-  public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, LanguageManagerInterface $language_manager, MenuLinkManagerInterface $menu_link_manager, MenuParentFormSelectorInterface $menu_parent_form_selector) {
-    parent::__construct($config_factory);
+  public function __construct(ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config_manager, EntityTypeManagerInterface $entity_type_manager, LanguageManagerInterface $language_manager, MenuLinkManagerInterface $menu_link_manager, MenuParentFormSelectorInterface $menu_parent_form_selector) {
+    parent::__construct($config_factory, $typed_config_manager);
     $this->entityTypeManager = $entity_type_manager;
     $this->languageManager = $language_manager;
     $this->menuLinkManager = $menu_link_manager;
@@ -74,6 +77,7 @@ class SettingsForm extends ConfigFormBase {
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('config.factory'),
+      $container->get('config.typed'),
       $container->get('entity_type.manager'),
       $container->get('language_manager'),
       $container->get('plugin.manager.menu.link'),
diff --git a/src/LanguageLinkAccessMenuTreeManipulator.php b/src/LanguageLinkAccessMenuTreeManipulator.php
index 58e2294..67985ba 100644
--- a/src/LanguageLinkAccessMenuTreeManipulator.php
+++ b/src/LanguageLinkAccessMenuTreeManipulator.php
@@ -5,6 +5,7 @@ namespace Drupal\language_switcher_menu;
 use Drupal\Core\Access\AccessManagerInterface;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Menu\DefaultMenuLinkTreeManipulators;
 use Drupal\Core\Menu\MenuLinkInterface;
 use Drupal\Core\Path\PathValidatorInterface;
@@ -35,11 +36,13 @@ class LanguageLinkAccessMenuTreeManipulator extends DefaultMenuLinkTreeManipulat
    *   The current user.
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
    *   The entity type manager.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
    * @param \Drupal\Core\Path\PathValidatorInterface $path_validator
    *   The path validator.
    */
-  public function __construct(AccessManagerInterface $access_manager, AccountInterface $account, EntityTypeManagerInterface $entity_type_manager, PathValidatorInterface $path_validator) {
-    parent::__construct($access_manager, $account, $entity_type_manager);
+  public function __construct(AccessManagerInterface $access_manager, AccountInterface $account, EntityTypeManagerInterface $entity_type_manager, ModuleHandlerInterface $module_handler, PathValidatorInterface $path_validator) {
+    parent::__construct($access_manager, $account, $entity_type_manager, $module_handler);
     $this->pathValidator = $path_validator;
   }
 
diff --git a/src/Plugin/Menu/LanguageSwitcherLink.php b/src/Plugin/Menu/LanguageSwitcherLink.php
index 8c7a46f..746f8d6 100644
--- a/src/Plugin/Menu/LanguageSwitcherLink.php
+++ b/src/Plugin/Menu/LanguageSwitcherLink.php
@@ -173,7 +173,7 @@ class LanguageSwitcherLink extends MenuLinkDefault {
    */
   public function getRouteName() {
     $link = $this->getLink();
-    return isset($link['url']) ? $link['url']->getRouteName() : '';
+    return isset($link['url']) ? $link['url']->getRouteName() : '<nolink>';
   }
 
   /**
diff --git a/tests/modules/test_language_switcher_menu/test_language_switcher_menu.info.yml b/tests/modules/test_language_switcher_menu/test_language_switcher_menu.info.yml
new file mode 100644
index 0000000..597733a
--- /dev/null
+++ b/tests/modules/test_language_switcher_menu/test_language_switcher_menu.info.yml
@@ -0,0 +1,5 @@
+name: "Test Language Switcher Menu"
+description: "Support testing of Language Switcher Menu."
+type: module
+package: Testing
+core_version_requirement: ^10.1
diff --git a/tests/modules/test_language_switcher_menu/test_language_switcher_menu.module b/tests/modules/test_language_switcher_menu/test_language_switcher_menu.module
new file mode 100644
index 0000000..3d79e43
--- /dev/null
+++ b/tests/modules/test_language_switcher_menu/test_language_switcher_menu.module
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Hook implementations by Test Language Switcher Menu module.
+ */
+
+use Drupal\Core\Url;
+
+/**
+ * Implements hook_language_switch_links_alter().
+ */
+function test_language_switcher_menu_language_switch_links_alter(array &$links, $type, Url $url) {
+  if (!\Drupal::state()->get('test_language_switcher_menu.remove_current_language_switch_link')) {
+    return;
+  }
+  // Unset language switch link for current language.
+  $current_language = \Drupal::languageManager()->getCurrentLanguage()->getId();
+  unset($links[$current_language]);
+}
diff --git a/tests/src/Functional/LanguageSwitcherMenuTest.php b/tests/src/Functional/LanguageSwitcherMenuTest.php
index 24fa669..5072e75 100644
--- a/tests/src/Functional/LanguageSwitcherMenuTest.php
+++ b/tests/src/Functional/LanguageSwitcherMenuTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\language_switcher_menu\Functional;
 
+use Drupal\Component\Utility\DeprecationHelper;
 use Drupal\Core\Url;
 use Drupal\Tests\BrowserTestBase;
 
@@ -24,6 +25,7 @@ class LanguageSwitcherMenuTest extends BrowserTestBase {
     'menu_ui',
     'language_switcher_menu',
     'menu_test',
+    'test_language_switcher_menu',
   ];
 
   /**
@@ -58,6 +60,7 @@ class LanguageSwitcherMenuTest extends BrowserTestBase {
     ]);
     $this->users['admin_menu_regular'] = $this->drupalCreateUser([
       'administer menu',
+      'view language_switcher_menu links',
     ]);
     $this->users['access_content'] = $this->drupalCreateUser([
       'access content',
@@ -65,6 +68,9 @@ class LanguageSwitcherMenuTest extends BrowserTestBase {
     $this->users['view_links'] = $this->drupalCreateUser([
       'view language_switcher_menu links',
     ]);
+
+    // Initialize state for test language switcher module.
+    \Drupal::state()->set('test_language_switcher_menu.remove_current_language_switch_link', FALSE);
   }
 
   /**
@@ -182,6 +188,42 @@ class LanguageSwitcherMenuTest extends BrowserTestBase {
       '/fr' => 'Home',
     ], 'The menu links are correct in a non-standard language.');
 
+    // Assert that main menu management page doesn't show a white page.
+    $this->drupalLogin($this->users['admin_menu_regular']);
+    $this->drupalGet('admin/structure/menu/manage/main');
+    $this->assertSession()->statusCodeEquals(200);
+
+    // Assert that menu switch links are visible.
+    $this->assertMenuLinks([
+      '/admin/structure/menu/manage/main' => 'English',
+      '/fr/admin/structure/menu/manage/main' => 'French',
+      '/' => 'Home',
+    ], 'Both language links are shown.');
+
+    // Enable removal of current language switch link in test module.
+    \Drupal::state()->set('test_language_switcher_menu.remove_current_language_switch_link', TRUE);
+
+    // Assert that main menu management page still doesn't show a white page.
+    $this->drupalGet('admin/structure/menu/manage/main');
+    $this->assertSession()->statusCodeEquals(200);
+
+    // Assert that menu switch link in current language has been removed.
+    $this->assertMenuLinks([
+      '/fr/admin/structure/menu/manage/main' => 'French',
+      '/' => 'Home',
+    ], 'The language link for the current language is not shown.');
+
+    // Disable removal of current language switch link in test module.
+    \Drupal::state()->set('test_language_switcher_menu.remove_current_language_switch_link', FALSE);
+
+    // Assert that menu switch links are visible.
+    $this->drupalGet('admin/structure/menu/manage/main');
+    $this->assertMenuLinks([
+      '/admin/structure/menu/manage/main' => 'English',
+      '/fr/admin/structure/menu/manage/main' => 'French',
+      '/' => 'Home',
+    ], 'Both language links are visible in the menu.');
+
     // Reconfigure to disable.
     $this->drupalLogin($this->users['admin_user']);
     $edit = [
@@ -229,17 +271,31 @@ class LanguageSwitcherMenuTest extends BrowserTestBase {
     $this->assertSession()->pageTextNotContains('Error message');
     $this->assertSession()->pageTextNotContains("The path '<current>' is inaccessible.");
     $this->assertSession()->pageTextContains('The menu link has been saved.');
-    $this->assertMenuLinks([
-      '/' => 'Home',
-      '/admin/structure/menu/item/1/edit' => 'link_current',
-    ], 'Custom link targeting <current> is visible in menu with permission to link to any page.');
+    DeprecationHelper::backwardsCompatibleCall(
+      currentVersion: \Drupal::VERSION,
+      deprecatedVersion: '10.3',
+      currentCallable: fn() => $this->assertMenuLinks([
+        '/' => 'Home',
+      ], 'Custom link targeting <current> is not visible in menu with permission to link to any page.'),
+      deprecatedCallable: fn() => $this->assertMenuLinks([
+        '/' => 'Home',
+        '/admin/structure/menu/item/1/edit' => 'link_current',
+      ], 'Custom link targeting <current> is visible in menu with permission to link to any page.'),
+    );
 
     $this->drupalGet('');
     $this->assertSession()->statusCodeEquals(200);
-    $this->assertMenuLinks([
-      '/' => 'Home',
-      '/user/' . $this->users['admin_menu_any']->id() => 'link_current',
-    ], 'Custom link targeting <current> is visible in menu with permission to link to any page.');
+    DeprecationHelper::backwardsCompatibleCall(
+      currentVersion: \Drupal::VERSION,
+      deprecatedVersion: '10.3',
+      currentCallable: fn() => $this->assertMenuLinks([
+        '/' => 'Home',
+      ], 'Custom link targeting <current> is not visible in menu with permission to link to any page.'),
+      deprecatedCallable: fn() => $this->assertMenuLinks([
+        '/' => 'Home',
+        '/user/' . $this->users['admin_menu_any']->id() => 'link_current',
+      ], 'Custom link targeting <current> is visible in menu with permission to link to any page.'),
+    );
 
     $this->drupalLogin($this->users['view_links']);
     $this->assertMenuLinks([
-- 
GitLab