From 8a79e0fe9f716f17992b00c5d3df2a7e251d296d Mon Sep 17 00:00:00 2001
From: catch <catch@35733.no-reply.drupal.org>
Date: Thu, 29 Feb 2024 13:30:30 +0000
Subject: [PATCH] =?UTF-8?q?Issue=20#2473989=20by=20srishtiiee,=20narendraR?=
 =?UTF-8?q?,=20dawehner,=20kunal.sachdev,=20ravi.shankar,=20lauriii,=20Pea?=
 =?UTF-8?q?cog,=20smustgrave,=20swentel,=20martin107,=20Sonal.Sangale,=20s?=
 =?UTF-8?q?haal,=20baikho,=20amateescu,=20plach,=20cpj,=20G=C3=A1bor=20Hoj?=
 =?UTF-8?q?tsy,=20jhodgdon,=20quietone,=20webchick,=20aspilicious,=20catch?=
 =?UTF-8?q?,=20xjm,=20alexpott:=20Lack=20of=20dynamic=20language=20field?=
 =?UTF-8?q?=20/=20filter=20makes=20shipping=20core=20views=20hard=20to=20b?=
 =?UTF-8?q?e=20both=20compatible=20with=20mono=20and=20multilingual?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

(cherry picked from commit 74851391d8202aad7d075c149c44104bd655ec9f)
---
 .../FieldFormatter/LanguageFormatter.php      | 12 +++-
 .../src/Functional/Views/CommentAdminTest.php | 14 ++++
 .../src/Functional/MediaOverviewPageTest.php  |  8 +++
 .../config/optional/views.view.content.yml    | 66 +++++++++++++++++++
 .../tests/src/Functional/NodeAdminTest.php    | 59 +++++++++++++++++
 .../Kernel/Views/NodeViewsFieldAccessTest.php |  3 +
 core/modules/views/src/EntityViewsData.php    |  2 +-
 .../src/Plugin/views/field/FieldLanguage.php  | 25 +++++++
 .../Plugin/views/filter/LanguageFilter.php    | 12 +++-
 .../PaginationAJAXTest.php                    |  6 +-
 .../src/Kernel/Entity/EntityViewsDataTest.php |  2 +-
 .../EntityTestViewsFieldAccessTest.php        |  3 +
 .../Handler/FieldFieldAccessTestBase.php      |  2 +-
 13 files changed, 203 insertions(+), 11 deletions(-)
 create mode 100644 core/modules/views/src/Plugin/views/field/FieldLanguage.php

diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/LanguageFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/LanguageFormatter.php
index 6acabfa69602..23a0085367ba 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/LanguageFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/LanguageFormatter.php
@@ -117,9 +117,15 @@ protected function viewValue(FieldItemInterface $item) {
     // storage by LanguageManager::getLanguages()) or in its native language
     // name. That only depends on formatter settings and no language condition.
     $languages = $this->getSetting('native_language') ? $this->languageManager->getNativeLanguages(LanguageInterface::STATE_ALL) : $this->languageManager->getLanguages(LanguageInterface::STATE_ALL);
-    return [
-      '#plain_text' => $item->language && isset($languages[$item->language->getId()]) ? $languages[$item->language->getId()]->getName() : '',
-    ];
+    // \Drupal\Core\Language\LanguageInterface::LANGCODE_NOT_SPECIFIED
+    // and \Drupal\Core\Language\LanguageInterface::LANGCODE_NOT_APPLICABLE are
+    // not returned from the language manager above.
+    $value = [];
+    if (isset($item->language)) {
+      $name = isset($languages[$item->language->getId()]) ? $languages[$item->language->getId()]->getName() : $item->language->getId();
+      $value = ['#plain_text' => $name];
+    }
+    return $value;
   }
 
 }
diff --git a/core/modules/comment/tests/src/Functional/Views/CommentAdminTest.php b/core/modules/comment/tests/src/Functional/Views/CommentAdminTest.php
index 68124ab88023..2676189c1208 100644
--- a/core/modules/comment/tests/src/Functional/Views/CommentAdminTest.php
+++ b/core/modules/comment/tests/src/Functional/Views/CommentAdminTest.php
@@ -9,6 +9,7 @@
 use Drupal\comment\CommentInterface;
 use Drupal\comment\Entity\Comment;
 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
+use Drupal\language\Entity\ConfigurableLanguage;
 use Drupal\Tests\comment\Functional\CommentTestBase as CommentBrowserTestBase;
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\Unicode;
@@ -27,11 +28,24 @@ class CommentAdminTest extends CommentBrowserTestBase {
    */
   protected $defaultTheme = 'stark';
 
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'language',
+  ];
+
   /**
    * {@inheritdoc}
    */
   protected function setUp(): void {
     parent::setUp();
+    // Make the site multilingual to have a working language field handler.
+    ConfigurableLanguage::create([
+      'id' => 'es',
+      'title' => 'Spanish title',
+      'label' => 'Spanish label',
+    ])->save();
     \Drupal::service('module_installer')->install(['views']);
     $view = Views::getView('comment');
     $view->storage->enable()->save();
diff --git a/core/modules/media/tests/src/Functional/MediaOverviewPageTest.php b/core/modules/media/tests/src/Functional/MediaOverviewPageTest.php
index a0fcb6c726b9..7d1f63e38c1f 100644
--- a/core/modules/media/tests/src/Functional/MediaOverviewPageTest.php
+++ b/core/modules/media/tests/src/Functional/MediaOverviewPageTest.php
@@ -4,6 +4,7 @@
 
 namespace Drupal\Tests\media\Functional;
 
+use Drupal\language\Entity\ConfigurableLanguage;
 use Drupal\media\Entity\Media;
 use Drupal\user\Entity\Role;
 use Drupal\user\RoleInterface;
@@ -20,11 +21,18 @@ class MediaOverviewPageTest extends MediaFunctionalTestBase {
    */
   protected $defaultTheme = 'stark';
 
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['language'];
+
   /**
    * {@inheritdoc}
    */
   protected function setUp(): void {
     parent::setUp();
+    // Make the site multilingual to have a working language field handler.
+    ConfigurableLanguage::create(['id' => 'es', 'title' => 'Spanish title', 'label' => 'Spanish label'])->save();
     $this->drupalLogin($this->nonAdminUser);
   }
 
diff --git a/core/modules/node/config/optional/views.view.content.yml b/core/modules/node/config/optional/views.view.content.yml
index cde82e51e396..20d6dad58a75 100644
--- a/core/modules/node/config/optional/views.view.content.yml
+++ b/core/modules/node/config/optional/views.view.content.yml
@@ -193,6 +193,72 @@ display:
               past_format: '@interval ago'
               granularity: 2
               refresh: 60
+        langcode:
+          id: langcode
+          table: node_field_data
+          field: langcode
+          relationship: none
+          group_type: group
+          admin_label: ''
+          entity_type: node
+          entity_field: langcode
+          plugin_id: field
+          label: 'Language'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          click_sort_column: value
+          type: language
+          settings:
+            link_to_entity: false
+            native_language: false
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: 0
+          delta_offset: 0
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
         operations:
           id: operations
           table: node
diff --git a/core/modules/node/tests/src/Functional/NodeAdminTest.php b/core/modules/node/tests/src/Functional/NodeAdminTest.php
index fddd7fa3ff32..f0e4c5bca546 100644
--- a/core/modules/node/tests/src/Functional/NodeAdminTest.php
+++ b/core/modules/node/tests/src/Functional/NodeAdminTest.php
@@ -5,6 +5,7 @@
 namespace Drupal\Tests\node\Functional;
 
 use Drupal\Core\Database\Database;
+use Drupal\language\Entity\ConfigurableLanguage;
 use Drupal\user\RoleInterface;
 
 /**
@@ -229,6 +230,64 @@ public function testContentAdminPages() {
       $this->assertSession()->linkByHrefExists('node/' . $node->id() . '/edit');
       $this->assertSession()->linkByHrefExists('node/' . $node->id() . '/delete');
     }
+    // Ensure that the language table column and the language exposed filter are
+    // not visible on monolingual sites.
+    $this->assertSession()->fieldNotExists('langcode');
+    $this->assertEquals(0, count($this->cssSelect('td.views-field-langcode')));
+    $this->assertEquals(0, count($this->cssSelect('td.views-field-langcode')));
+  }
+
+  /**
+   * Tests content overview for a multilingual site.
+   */
+  public function testContentAdminPageMultilingual() {
+    $this->drupalLogin($this->adminUser);
+
+    \Drupal::service('module_installer')->install(['language']);
+    ConfigurableLanguage::create([
+      'id' => 'es',
+      'label' => 'Spanish',
+    ])->save();
+
+    $this->drupalCreateNode(['type' => 'page', 'title' => 'English title'])
+      ->addTranslation('es')
+      ->setTitle('Spanish title')
+      ->save();
+
+    $this->drupalGet('admin/content');
+
+    // Ensure that both the language table column as well as the language
+    // exposed filter are visible on multilingual sites.
+    $this->assertSession()->fieldExists('langcode');
+    $this->assertEquals(2, count($this->cssSelect('td.views-field-langcode')));
+    $this->assertEquals(2, count($this->cssSelect('td.views-field-langcode')));
+
+    $this->assertSession()->pageTextContains('English title');
+    $this->assertSession()->pageTextContains('Spanish title');
+
+    $this->drupalGet('admin/content', ['query' => ['langcode' => '***LANGUAGE_site_default***']]);
+    $this->assertSession()->pageTextContains('English title');
+    $this->assertSession()->pageTextNotContains('Spanish title');
+
+    $this->drupalGet('admin/content', ['query' => ['langcode' => 'en']]);
+    $this->assertSession()->pageTextContains('English title');
+    $this->assertSession()->pageTextNotContains('Spanish title');
+
+    $this->drupalGet('admin/content', ['query' => ['langcode' => 'und']]);
+    $this->assertSession()->pageTextNotContains('English title');
+    $this->assertSession()->pageTextNotContains('Spanish title');
+
+    $this->drupalGet('admin/content', ['query' => ['langcode' => 'zxx']]);
+    $this->assertSession()->pageTextNotContains('English title');
+    $this->assertSession()->pageTextNotContains('Spanish title');
+
+    $this->drupalGet('admin/content', ['query' => ['langcode' => html_entity_decode('***LANGUAGE_language_interface***')]]);
+    $this->assertSession()->pageTextContains('English title');
+    $this->assertSession()->pageTextNotContains('Spanish title');
+
+    $this->drupalGet('es/admin/content', ['query' => ['langcode' => html_entity_decode('***LANGUAGE_language_interface***')]]);
+    $this->assertSession()->pageTextNotContains('English title');
+    $this->assertSession()->pageTextContains('Spanish title');
   }
 
 }
diff --git a/core/modules/node/tests/src/Kernel/Views/NodeViewsFieldAccessTest.php b/core/modules/node/tests/src/Kernel/Views/NodeViewsFieldAccessTest.php
index a4b6a0e2b988..8c09c23a4b49 100644
--- a/core/modules/node/tests/src/Kernel/Views/NodeViewsFieldAccessTest.php
+++ b/core/modules/node/tests/src/Kernel/Views/NodeViewsFieldAccessTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\node\Kernel\Views;
 
+use Drupal\language\Entity\ConfigurableLanguage;
 use Drupal\node\Entity\Node;
 use Drupal\node\Entity\NodeType;
 use Drupal\user\Entity\User;
@@ -26,6 +27,8 @@ protected function setUp($import_test_views = TRUE): void {
     parent::setUp($import_test_views);
 
     $this->installEntitySchema('node');
+    // Make the site multilingual to have a working language field handler.
+    ConfigurableLanguage::create(['id' => 'es', 'title' => 'Spanish title', 'label' => 'Spanish label'])->save();
   }
 
   /**
diff --git a/core/modules/views/src/EntityViewsData.php b/core/modules/views/src/EntityViewsData.php
index eb964521afab..8acb1ad2979f 100644
--- a/core/modules/views/src/EntityViewsData.php
+++ b/core/modules/views/src/EntityViewsData.php
@@ -509,7 +509,7 @@ protected function mapSingleFieldViewsData($table, $field_name, $field_type, $co
         break;
 
       case 'language':
-        $views_field['field']['id'] = 'field';
+        $views_field['field']['id'] = 'field_language';
         $views_field['argument']['id'] = 'language';
         $views_field['filter']['id'] = 'language';
         $views_field['sort']['id'] = 'standard';
diff --git a/core/modules/views/src/Plugin/views/field/FieldLanguage.php b/core/modules/views/src/Plugin/views/field/FieldLanguage.php
new file mode 100644
index 000000000000..90ab86dc33ed
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/field/FieldLanguage.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Displays the language of an entity.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @ViewsField("field_language")
+ */
+class FieldLanguage extends EntityField {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(AccountInterface $account): bool {
+    // No point in displaying the language field on monolingual sites,
+    // as only one language value is available.
+    return $this->languageManager->isMultilingual() && parent::access($account);
+  }
+
+}
diff --git a/core/modules/views/src/Plugin/views/filter/LanguageFilter.php b/core/modules/views/src/Plugin/views/filter/LanguageFilter.php
index 69fdc81a1fd1..4c25b4cd4ac6 100644
--- a/core/modules/views/src/Plugin/views/filter/LanguageFilter.php
+++ b/core/modules/views/src/Plugin/views/filter/LanguageFilter.php
@@ -5,6 +5,7 @@
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Session\AccountInterface;
 use Drupal\views\Plugin\views\PluginBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -71,8 +72,8 @@ public function getValueOptions() {
    * {@inheritdoc}
    */
   public function query() {
-    // Don't filter by language in case the site is not multilingual, because
-    // there is no point in doing so.
+    // No point in displaying the language filter on monolingual sites,
+    // as only one language value is available.
     if (!$this->languageManager->isMultilingual()) {
       return;
     }
@@ -80,4 +81,11 @@ public function query() {
     parent::query();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function access(AccountInterface $account): bool {
+    return $this->languageManager->isMultilingual() && parent::access($account);
+  }
+
 }
diff --git a/core/modules/views/tests/src/FunctionalJavascript/PaginationAJAXTest.php b/core/modules/views/tests/src/FunctionalJavascript/PaginationAJAXTest.php
index 6b1b5e360c26..c2019bb8c2d9 100644
--- a/core/modules/views/tests/src/FunctionalJavascript/PaginationAJAXTest.php
+++ b/core/modules/views/tests/src/FunctionalJavascript/PaginationAJAXTest.php
@@ -107,7 +107,7 @@ public function testBasicPagination() {
     $this->assertNoDuplicateAssetsOnPage();
 
     // Test that no unwanted parameters are added to the URL.
-    $this->assertEquals('?status=All&type=All&title=&langcode=All&items_per_page=5&order=changed&sort=asc&page=2', $link->getAttribute('href'));
+    $this->assertEquals('?status=All&type=All&title=&items_per_page=5&order=changed&sort=asc&page=2', $link->getAttribute('href'));
 
     $this->clickLink('Go to page 3');
     $session_assert->assertWaitOnAjaxRequest();
@@ -191,7 +191,7 @@ public function testDefaultFilterPagination() {
     $this->assertNoDuplicateAssetsOnPage();
 
     // Test that no unwanted parameters are added to the URL.
-    $this->assertEquals('?status=All&type=All&title=default_value&langcode=All&items_per_page=5&order=changed&sort=asc&page=0', $link->getAttribute('href'));
+    $this->assertEquals('?status=All&type=All&title=default_value&items_per_page=5&order=changed&sort=asc&page=0', $link->getAttribute('href'));
 
     // Set the title filter to empty string using the exposed pager.
     $page->fillField('title', '');
@@ -211,7 +211,7 @@ public function testDefaultFilterPagination() {
     $this->assertNoDuplicateAssetsOnPage();
 
     // Test that no unwanted parameters are added to the URL.
-    $this->assertEquals('?status=All&type=All&title=&langcode=All&items_per_page=5&page=0', $link->getAttribute('href'));
+    $this->assertEquals('?status=All&type=All&title=&items_per_page=5&page=0', $link->getAttribute('href'));
 
     // Navigate back to the first page.
     $this->clickLink('Go to first page');
diff --git a/core/modules/views/tests/src/Kernel/Entity/EntityViewsDataTest.php b/core/modules/views/tests/src/Kernel/Entity/EntityViewsDataTest.php
index c64a7c150f4a..4b2ae97e81c1 100644
--- a/core/modules/views/tests/src/Kernel/Entity/EntityViewsDataTest.php
+++ b/core/modules/views/tests/src/Kernel/Entity/EntityViewsDataTest.php
@@ -771,7 +771,7 @@ protected function assertNumericField(array $data): void {
    * @internal
    */
   protected function assertLanguageField(array $data): void {
-    $this->assertEquals('field', $data['field']['id']);
+    $this->assertEquals('field_language', $data['field']['id']);
     $this->assertEquals('language', $data['filter']['id']);
     $this->assertEquals('language', $data['argument']['id']);
     $this->assertEquals('standard', $data['sort']['id']);
diff --git a/core/modules/views/tests/src/Kernel/Handler/EntityTestViewsFieldAccessTest.php b/core/modules/views/tests/src/Kernel/Handler/EntityTestViewsFieldAccessTest.php
index e2812f99e692..148bf98e0298 100644
--- a/core/modules/views/tests/src/Kernel/Handler/EntityTestViewsFieldAccessTest.php
+++ b/core/modules/views/tests/src/Kernel/Handler/EntityTestViewsFieldAccessTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\views\Kernel\Handler;
 
 use Drupal\entity_test\Entity\EntityTest;
+use Drupal\language\Entity\ConfigurableLanguage;
 
 /**
  * Tests base field access in Views for the entity_test entity.
@@ -23,6 +24,8 @@ protected function setUp($import_test_views = TRUE): void {
     parent::setUp($import_test_views);
 
     $this->installEntitySchema('entity_test');
+    // Make the site multilingual to have a working language field handler.
+    ConfigurableLanguage::create(['id' => 'es', 'title' => 'Spanish title', 'label' => 'Spanish label'])->save();
   }
 
   public function testEntityTestFields() {
diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldFieldAccessTestBase.php b/core/modules/views/tests/src/Kernel/Handler/FieldFieldAccessTestBase.php
index 7155b25d8249..bd1051f9446a 100644
--- a/core/modules/views/tests/src/Kernel/Handler/FieldFieldAccessTestBase.php
+++ b/core/modules/views/tests/src/Kernel/Handler/FieldFieldAccessTestBase.php
@@ -30,7 +30,7 @@ abstract class FieldFieldAccessTestBase extends ViewsKernelTestBase {
   /**
    * {@inheritdoc}
    */
-  protected static $modules = ['user'];
+  protected static $modules = ['user', 'language'];
 
   /**
    * {@inheritdoc}
-- 
GitLab