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 6acabfa696029f264f1e941fa04947b6c160de99..23a0085367ba843d7b195259866a1973d4d11e79 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 68124ab88023e60cf1b6212064bdcfbbff84fabb..2676189c12084199793edfccbbd8316ad8198bbe 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 a0fcb6c726b97bc6cc4ce3c246331bdacbf512d9..7d1f63e38c1f19a8e058ecad188c5acc01c513b5 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 cde82e51e396ad0205511a3811d929aef013c7ba..20d6dad58a7566a76722fe8810c3b0ac1a8fab33 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 fddd7fa3ff32556fd1ec7ed25fe4aef3f2405e84..f0e4c5bca546bc10842c7803ee2d7280fa2a261f 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 a4b6a0e2b988e0962728863e2e7a11c518ec64b4..8c09c23a4b4996711503ca09592aab21c44f82e0 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 eb964521afab2e62642ac2bdb36c5b2ec3816245..8acb1ad2979fada0db44af508bab6470879beb67 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 0000000000000000000000000000000000000000..90ab86dc33ed7cc27969ff38bbe71a3cb19e53a3 --- /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 69fdc81a1fd19fe001c4c1766216d0984cdb7311..4c25b4cd4ac639a05de6abb899ab461d8f9aed5e 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 6b1b5e360c26133d8920f41f5e7ef82d3e5dde69..c2019bb8c2d9b8b66f7c32c32fc6d5b6e963563c 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 c64a7c150f4a2231fca2a0f0ef46e673868a70c9..4b2ae97e81c1ee37451176fb6c4bfc03a20fcbd9 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 e2812f99e6924d5c2ca907186801bf2a4a9a075a..148bf98e0298686c058e832b243e4e420a6f15ee 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 7155b25d824978d0f42a86887ce519a3d93f9d92..bd1051f9446aa2e3e5eb0c3eb9daf8da2d05c119 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}