From 16caf92d336ff71e0738d48da12ca24b49e81ccd Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org>
Date: Wed, 30 Jul 2014 11:35:35 +0100
Subject: [PATCH] =?UTF-8?q?Issue=20#1833010=20by=20G=C3=A1bor=20Hojtsy:=20?=
 =?UTF-8?q?Fixed=20Admin=20user=20language=20preference=20WSOD=20if=20ahea?=
 =?UTF-8?q?d=20of=20path=20prefixes.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../LanguageNegotiationUserAdmin.php          | 33 +++++++++++--
 .../user/src/Tests/UserAdminLanguageTest.php  | 49 +++++++++++++++++--
 2 files changed, 74 insertions(+), 8 deletions(-)

diff --git a/core/modules/user/src/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php b/core/modules/user/src/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
index cc0bfb548036..cdd269542ff0 100644
--- a/core/modules/user/src/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
+++ b/core/modules/user/src/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
@@ -7,12 +7,14 @@
 
 namespace Drupal\user\Plugin\LanguageNegotiation;
 
+use Drupal\Core\PathProcessor\PathProcessorManager;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Routing\AdminContext;
 use Drupal\language\LanguageNegotiationMethodBase;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
 use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
 
 /**
@@ -50,6 +52,13 @@ class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase impleme
    */
   protected $router;
 
+  /**
+   * The path processor manager.
+   *
+   * @var \Drupal\Core\PathProcessor\PathProcessorManager
+   */
+  protected $pathProcessorManager;
+
   /**
    * Constructs a new LanguageNegotiationUserAdmin instance.
    *
@@ -57,10 +66,13 @@ class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase impleme
    *   The admin context.
    * @param \Symfony\Component\Routing\Matcher\UrlMatcherInterface $router
    *   The router.
+   * @param \Drupal\Core\PathProcessor\PathProcessorManager $path_processor_manager
+   *   The path processor manager.
    */
-  public function __construct(AdminContext $admin_context, UrlMatcherInterface $router) {
+  public function __construct(AdminContext $admin_context, UrlMatcherInterface $router, PathProcessorManager $path_processor_manager) {
     $this->adminContext = $admin_context;
     $this->router = $router;
+    $this->pathProcessorManager = $path_processor_manager;
   }
 
   /**
@@ -69,7 +81,8 @@ public function __construct(AdminContext $admin_context, UrlMatcherInterface $ro
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
     return new static(
       $container->get('router.admin_context'),
-      $container->get('router')
+      $container->get('router'),
+      $container->get('path_processor_manager')
     );
   }
 
@@ -105,10 +118,20 @@ public function getLangcode(Request $request = NULL) {
   public function isAdminPath(Request $request) {
     $result = FALSE;
     if ($request && $this->adminContext) {
-      // If called from an event subscriber, the request may not the route info
-      // yet, so use the router to look up the path first.
+      // If called from an event subscriber, the request may not have the route
+      // object yet (it is still being built), so use the router to look up
+      // based on the path.
       if (!$route_object = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) {
-        $attributes = $this->router->match('/' . urldecode(trim($request->getPathInfo(), '/')));
+        try {
+          // Process the path as an inbound path. This will remove any language
+          // prefixes and other path components that inbound processing would
+          // clear out, so we can attempt to load the route clearly.
+          $path = $this->pathProcessorManager->processInbound(urldecode(trim($request->getPathInfo(), '/')), $request);
+          $attributes = $this->router->match('/' . $path);
+        }
+        catch (ResourceNotFoundException $e) {
+          return FALSE;
+        }
         $route_object = $attributes[RouteObjectInterface::ROUTE_OBJECT];
       }
       $result = $this->adminContext->isAdminRoute($route_object);
diff --git a/core/modules/user/src/Tests/UserAdminLanguageTest.php b/core/modules/user/src/Tests/UserAdminLanguageTest.php
index be7a6cc2d182..d2b08b75f126 100644
--- a/core/modules/user/src/Tests/UserAdminLanguageTest.php
+++ b/core/modules/user/src/Tests/UserAdminLanguageTest.php
@@ -36,7 +36,7 @@ class UserAdminLanguageTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('user', 'language');
+  public static $modules = array('user', 'language', 'language_test');
 
   public function setUp() {
     parent::setUp();
@@ -105,17 +105,60 @@ function testUserAdminLanguageConfigurationAvailableIfAdminLanguageNegotiationIs
     $this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available for regular user.');
   }
 
+  /**
+   * Tests the actual language negotiation.
+   */
+  function testActualNegotiation() {
+    $this->drupalLogin($this->adminUser);
+    $this->addCustomLanguage();
+    $this->setLanguageNegotiation();
+
+    // Even though we have admin language negotiation, so long as the user has
+    // no preference set, negotiation will fall back further.
+    $path = 'user/' . $this->adminUser->id() . '/edit';
+    $this->drupalGet($path);
+    $this->assertText('Language negotiation method: language-default');
+
+    // Set a preferred language code for the user.
+    $path = 'user/' . $this->adminUser->id() . '/edit';
+    $edit = array();
+    $edit['preferred_admin_langcode'] = 'xx';
+    $this->drupalPostForm($path, $edit, t('Save'));
+
+    // Test negotiation with the URL method first. The admin method will only
+    // be used if the URL method did not match.
+    $path = 'user/' . $this->adminUser->id() . '/edit';
+    $this->drupalGet($path);
+    $this->assertText('Language negotiation method: language-user-admin');
+    $path = 'xx/user/' . $this->adminUser->id() . '/edit';
+    $this->drupalGet($path);
+    $this->assertText('Language negotiation method: language-url');
+
+    // Test negotiation with the admin language method first. The admin method
+    // will be used at all times.
+    $this->setLanguageNegotiation(TRUE);
+    $path = 'user/' . $this->adminUser->id() . '/edit';
+    $this->drupalGet($path);
+    $this->assertText('Language negotiation method: language-user-admin');
+    $path = 'xx/user/' . $this->adminUser->id() . '/edit';
+    $this->drupalGet($path);
+    $this->assertText('Language negotiation method: language-user-admin');
+  }
+
   /**
    * Sets the User interface negotiation detection method.
    *
+   * @param bool $admin_first
+   *   Whether the admin negotiation should be first.
+   *
    * Enables the "Account preference for administration pages" language
    * detection method for the User interface language negotiation type.
    */
-  function setLanguageNegotiation() {
+  function setLanguageNegotiation($admin_first = FALSE) {
     $edit = array(
       'language_interface[enabled][language-user-admin]' => TRUE,
       'language_interface[enabled][language-url]' => TRUE,
-      'language_interface[weight][language-user-admin]' => -8,
+      'language_interface[weight][language-user-admin]' => ($admin_first ? -12 : -8),
       'language_interface[weight][language-url]' => -10,
     );
     $this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
-- 
GitLab