From 46564ee73302dcf9f95a71d7fcfe028bc8833492 Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Mon, 18 Mar 2024 13:26:17 +0000 Subject: [PATCH] Issue #2765297 by mohit_aghera, joseph.olstad, sylus, alexpott, ducktape, anmolgoyal74, heikki, kishor_kolekar, kriboogh, googletorp, dnotes, mpp, catch, Lendude, Berdir, borisson_, xjm: Return translated term name on views "Content: Has taxonomy term ID (with depth)" (cherry picked from commit 1bcc7f34e6b9479e8a868c7a5067cca8c04c757c) --- .../Plugin/views/argument/IndexTidDepth.php | 21 +- .../src/Plugin/views/argument/Taxonomy.php | 22 +- core/modules/taxonomy/taxonomy.tokens.inc | 3 +- .../argument/TaxonomyViewsArgumentTest.php | 41 +++ ...iew.taxonomy_translated_term_name_test.yml | 337 ++++++++++++++++++ .../Views/TermTranslationViewsTest.php | 132 +++++++ .../Views/ViewsArgumentDeprecationTest.php | 38 ++ 7 files changed, 582 insertions(+), 12 deletions(-) create mode 100644 core/modules/taxonomy/tests/modules/taxonomy_test/src/Plugin/views/argument/TaxonomyViewsArgumentTest.php create mode 100644 core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.taxonomy_translated_term_name_test.yml create mode 100644 core/modules/taxonomy/tests/src/Functional/Views/TermTranslationViewsTest.php create mode 100644 core/modules/taxonomy/tests/src/Kernel/Views/ViewsArgumentDeprecationTest.php diff --git a/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php index e0a80ce207f..7e1a245365a 100644 --- a/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php +++ b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php @@ -3,6 +3,7 @@ namespace Drupal\taxonomy\Plugin\views\argument; use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\taxonomy\TaxonomyIndexDepthQueryTrait; @@ -26,16 +27,26 @@ class IndexTidDepth extends ArgumentPluginBase implements ContainerFactoryPlugin /** * @var \Drupal\Core\Entity\EntityStorageInterface + * + * @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is no + * replacement. + * + * @see https://www.drupal.org/node/3427843 */ protected $termStorage; /** * {@inheritdoc} */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $termStorage) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, protected EntityStorageInterface|EntityRepositoryInterface $entityRepository) { parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->termStorage = $termStorage; + if ($entityRepository instanceof EntityStorageInterface) { + // @phpstan-ignore-next-line + $this->termStorage = $entityRepository; + @trigger_error('Calling ' . __CLASS__ . '::__construct() with the $termStorage argument as \Drupal\Core\Entity\EntityStorageInterface is deprecated in drupal:10.3.0 and it will require Drupal\Core\Entity\EntityRepositoryInterface in drupal:11.0.0. See https://www.drupal.org/node/3427843', E_USER_DEPRECATED); + $this->entityRepository = \Drupal::service('entity.repository'); + } } /** @@ -46,7 +57,7 @@ class IndexTidDepth extends ArgumentPluginBase implements ContainerFactoryPlugin $configuration, $plugin_id, $plugin_definition, - $container->get('entity_type.manager')->getStorage('taxonomy_term') + $container->get('entity.repository') ); } @@ -114,9 +125,9 @@ class IndexTidDepth extends ArgumentPluginBase implements ContainerFactoryPlugin } public function title() { - $term = $this->termStorage->load($this->argument); + $term = $this->entityRepository->getCanonical('taxonomy_term', $this->argument); if (!empty($term)) { - return $term->getName(); + return $term->label(); } // TODO review text return $this->t('No name'); diff --git a/core/modules/taxonomy/src/Plugin/views/argument/Taxonomy.php b/core/modules/taxonomy/src/Plugin/views/argument/Taxonomy.php index 02e76194fd6..3ebf992c8d6 100644 --- a/core/modules/taxonomy/src/Plugin/views/argument/Taxonomy.php +++ b/core/modules/taxonomy/src/Plugin/views/argument/Taxonomy.php @@ -2,6 +2,7 @@ namespace Drupal\taxonomy\Plugin\views\argument; +use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\views\Attribute\ViewsArgument; @@ -20,16 +21,25 @@ class Taxonomy extends NumericArgument implements ContainerFactoryPluginInterfac /** * @var \Drupal\Core\Entity\EntityStorageInterface + * + * @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is no + * replacement. + * + * @see https://www.drupal.org/node/3427843 */ protected $termStorage; /** * {@inheritdoc} */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $term_storage) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, protected EntityStorageInterface|EntityRepositoryInterface $entityRepository) { parent::__construct($configuration, $plugin_id, $plugin_definition); - - $this->termStorage = $term_storage; + if ($entityRepository instanceof EntityStorageInterface) { + // @phpstan-ignore-next-line + $this->termStorage = $this->entityRepository; + @trigger_error('Calling ' . __CLASS__ . '::__construct() with the $termStorage argument as \Drupal\Core\Entity\EntityStorageInterface is deprecated in drupal:10.3.0 and it will require Drupal\Core\Entity\EntityRepositoryInterface in drupal:11.0.0. See https://www.drupal.org/node/3427843', E_USER_DEPRECATED); + $this->entityRepository = \Drupal::service('entity.repository'); + } } /** @@ -40,7 +50,7 @@ class Taxonomy extends NumericArgument implements ContainerFactoryPluginInterfac $configuration, $plugin_id, $plugin_definition, - $container->get('entity_type.manager')->getStorage('taxonomy_term') + $container->get('entity.repository') ); } @@ -50,9 +60,9 @@ class Taxonomy extends NumericArgument implements ContainerFactoryPluginInterfac public function title() { // There might be no valid argument. if ($this->argument) { - $term = $this->termStorage->load($this->argument); + $term = $this->entityRepository->getCanonical('taxonomy_term', $this->argument); if (!empty($term)) { - return $term->getName(); + return $term->label(); } } // TODO review text diff --git a/core/modules/taxonomy/taxonomy.tokens.inc b/core/modules/taxonomy/taxonomy.tokens.inc index 9ed3a8dcd81..2f61dbed710 100644 --- a/core/modules/taxonomy/taxonomy.tokens.inc +++ b/core/modules/taxonomy/taxonomy.tokens.inc @@ -112,6 +112,7 @@ function taxonomy_tokens($type, $tokens, array $data, array $options, Bubbleable $replacements = []; if ($type == 'term' && !empty($data['term'])) { $term = $data['term']; + $term = \Drupal::service('entity.repository')->getTranslationFromContext($term, isset($options['langcode']) ? $options['langcode'] : NULL); foreach ($tokens as $name => $original) { switch ($name) { @@ -120,7 +121,7 @@ function taxonomy_tokens($type, $tokens, array $data, array $options, Bubbleable break; case 'name': - $replacements[$original] = $term->getName(); + $replacements[$original] = $term->label(); break; case 'description': diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test/src/Plugin/views/argument/TaxonomyViewsArgumentTest.php b/core/modules/taxonomy/tests/modules/taxonomy_test/src/Plugin/views/argument/TaxonomyViewsArgumentTest.php new file mode 100644 index 00000000000..ec14d55f1a0 --- /dev/null +++ b/core/modules/taxonomy/tests/modules/taxonomy_test/src/Plugin/views/argument/TaxonomyViewsArgumentTest.php @@ -0,0 +1,41 @@ +get('entity_type.manager')->getStorage('taxonomy_term'), + $container->get('entity_type.bundle.info'), + ); + } + +} diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.taxonomy_translated_term_name_test.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.taxonomy_translated_term_name_test.yml new file mode 100644 index 00000000000..919f887f121 --- /dev/null +++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.taxonomy_translated_term_name_test.yml @@ -0,0 +1,337 @@ +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.teaser + module: + - node + - taxonomy + - user +id: taxonomy_translated_term_name_test +label: 'Taxonomy Translated Term Name Test' +module: taxonomy +description: 'Content belonging to a certain taxonomy term.' +tag: default +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + access: + type: perm + options: + perm: 'access content' + cache: + type: tag + options: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: mini + options: + items_per_page: 10 + offset: 0 + id: 0 + total_pages: 0 + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + tags: + previous: ‹‹ + next: ›› + sorts: + sticky: + id: sticky + table: taxonomy_index + field: sticky + order: DESC + plugin_id: standard + relationship: none + group_type: group + admin_label: '' + exposed: false + expose: + label: '' + created: + id: created + table: taxonomy_index + field: created + order: DESC + plugin_id: date + relationship: none + group_type: group + admin_label: '' + exposed: false + expose: + label: '' + granularity: second + arguments: + term_node_tid_depth: + id: term_node_tid_depth + table: node_field_data + field: term_node_tid_depth + relationship: none + group_type: group + admin_label: '' + default_action: ignore + exception: + value: all + title_enable: false + title: All + title_enable: true + title: '{{ arguments.term_node_tid_depth }}' + default_argument_type: fixed + default_argument_options: + argument: '' + summary_options: + base_path: '' + count: true + items_per_page: 25 + override: false + summary: + sort_order: asc + number_of_records: 0 + format: default_summary + specify_validation: true + validate: + type: 'entity:taxonomy_term' + fail: 'not found' + validate_options: + access: true + operation: view + multiple: 0 + bundles: { } + depth: 2 + break_phrase: false + use_taxonomy_term_path: false + entity_type: node + plugin_id: taxonomy_index_tid_depth + filters: + langcode: + id: langcode + table: node_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + operator: in + value: + '***LANGUAGE_language_content***': '***LANGUAGE_language_content***' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + reduce: false + operator_limit_selection: false + operator_list: { } + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + plugin_id: language + entity_type: node + entity_field: langcode + status: + id: status + table: taxonomy_index + field: status + relationship: none + group_type: group + admin_label: '' + operator: '=' + value: '1' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + operator_limit_selection: false + operator_list: { } + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + plugin_id: boolean + style: + type: default + options: + grouping: { } + row_class: '' + default_row_class: true + uses_fields: false + row: + type: 'entity:node' + options: + view_mode: teaser + header: + entity_taxonomy_term: + id: entity_taxonomy_term + table: views + field: entity_taxonomy_term + relationship: none + group_type: group + admin_label: '' + empty: true + tokenize: true + target: '{{ raw_arguments.tid }}' + view_mode: full + bypass_access: false + plugin_id: entity + footer: { } + empty: { } + relationships: { } + fields: { } + display_extenders: { } + link_url: '' + link_display: page_1 + cache_metadata: + contexts: + - 'languages:language_interface' + - url + - url.query_args + - 'user.node_grants:view' + - user.permissions + max-age: -1 + tags: { } + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: 1 + display_options: + query: + type: views_query + options: { } + display_extenders: { } + path: taxonomy/test/% + cache_metadata: + max-age: -1 + contexts: { } + tags: { } + page_2: + id: page_2 + display_title: Page + display_plugin: page + position: 1 + display_options: + arguments: + tid: + id: tid + table: taxonomy_term_field_data + field: tid + relationship: term_node_tid + group_type: group + admin_label: '' + entity_type: taxonomy_term + entity_field: tid + plugin_id: taxonomy + default_action: ignore + exception: + value: all + title_enable: false + title: All + title_enable: false + title: '' + default_argument_type: fixed + default_argument_options: + argument: '' + summary_options: + base_path: '' + count: true + override: false + items_per_page: 25 + summary: + sort_order: asc + number_of_records: 0 + format: default_summary + specify_validation: false + validate: + type: none + fail: 'not found' + validate_options: { } + break_phrase: false + not: false + query: + type: views_query + options: { } + defaults: + relationships: false + arguments: false + relationships: + term_node_tid: + id: term_node_tid + table: node_field_data + field: term_node_tid + relationship: none + group_type: group + admin_label: term + entity_type: node + plugin_id: node_term_data + required: false + vids: { } + display_extenders: { } + path: taxonomy/test2/% + cache_metadata: + max-age: -1 + contexts: { } + tags: { } diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TermTranslationViewsTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TermTranslationViewsTest.php new file mode 100644 index 00000000000..77844f0b60f --- /dev/null +++ b/core/modules/taxonomy/tests/src/Functional/Views/TermTranslationViewsTest.php @@ -0,0 +1,132 @@ + 'translatedOne', + 'two' => 'translatedTwo', + 'three' => 'translatedThree', + ]; + + /** + * Created terms. + * + * @var \Drupal\taxonomy\Entity\Term[] + */ + protected $terms = []; + + /** + * {@inheritdoc} + */ + protected static $modules = ['taxonomy', 'language', 'content_translation', 'views']; + + /** + * Views used by this test. + * + * @var array + */ + public static $testViews = ['taxonomy_translated_term_name_test']; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * Language object. + * + * @var \Drupal\Core\Language\LanguageInterface|null + */ + protected $translationLanguage; + + /** + * {@inheritdoc} + */ + protected function setUp($import_test_views = TRUE, $modules = []): void { + parent::setUp($import_test_views, $modules); + $this->setupLanguages(); + $this->enableTranslation(); + $this->setUpTerms(); + $this->translationLanguage = \Drupal::languageManager()->getLanguage($this->translateToLangcode); + } + + /** + * Ensure that proper translation is returned when contextual filter. + * + * Taxonomy term: Term ID & Content: Has taxonomy term ID (with depth) + * contextual filters are enabled for two separate view modes. + */ + public function testTermsTranslationWithContextualFilter() { + $this->drupalLogin($this->rootUser); + + foreach ($this->terms as $term) { + // Test with "Content: Has taxonomy term ID (with depth)" contextual filter. + // Generate base language url and send request. + $url = Url::fromRoute('view.taxonomy_translated_term_name_test.page_1', ['arg_0' => $term->id()])->toString(); + $this->drupalGet($url); + $this->assertSession()->pageTextContains($term->label()); + + // Generate translation URL and send request. + $url = Url::fromRoute('view.taxonomy_translated_term_name_test.page_1', ['arg_0' => $term->id()], ['language' => $this->translationLanguage])->toString(); + $this->drupalGet($url); + $this->assertSession()->pageTextContains($this->termTranslationMap[$term->label()]); + + // Test with "Taxonomy term: Term ID" contextual filter. + // Generate base language url and send request. + $url = Url::fromRoute('view.taxonomy_translated_term_name_test.page_2', ['arg_0' => $term->id()])->toString(); + $this->drupalGet($url); + $this->assertSession()->pageTextContains($term->label()); + + // Generate translation URL and send request. + $url = Url::fromRoute('view.taxonomy_translated_term_name_test.page_2', ['arg_0' => $term->id()], ['language' => $this->translationLanguage])->toString(); + $this->drupalGet($url); + $this->assertSession()->pageTextContains($this->termTranslationMap[$term->label()]); + } + } + + /** + * Setup translated terms in a hierarchy. + */ + protected function setUpTerms() { + $parent_vid = 0; + foreach ($this->termTranslationMap as $name => $translation) { + + $term = $this->createTerm([ + 'name' => $name, + 'langcode' => $this->baseLangcode, + 'parent' => $parent_vid, + 'vid' => $this->vocabulary->id(), + ]); + + $term->addTranslation($this->translateToLangcode, [ + 'name' => $translation, + ]); + $term->save(); + + // Each term is nested under the last. + $parent_vid = $term->id(); + + $this->terms[] = $term; + } + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Views/ViewsArgumentDeprecationTest.php b/core/modules/taxonomy/tests/src/Kernel/Views/ViewsArgumentDeprecationTest.php new file mode 100644 index 00000000000..b11fa4979a7 --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Views/ViewsArgumentDeprecationTest.php @@ -0,0 +1,38 @@ +expectDeprecation('Calling Drupal\taxonomy\Plugin\views\argument\Taxonomy::__construct() with the $termStorage argument as \Drupal\Core\Entity\EntityStorageInterface is deprecated in drupal:10.3.0 and it will require Drupal\Core\Entity\EntityRepositoryInterface in drupal:11.0.0. See https://www.drupal.org/node/3427843'); + $plugin = \Drupal::service('plugin.manager.views.argument')->createInstance('taxonomy_views_argument_test', []); + $this->assertInstanceOf(TaxonomyViewsArgumentTest::class, $plugin); + } + +}