diff --git a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php index 1c8a408..bd0c953 100644 --- a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php +++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php @@ -107,6 +107,9 @@ protected function defineOptions() { return $options; } + /** + * {@inheritdoc} + */ public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) { $vocabularies = $this->vocabularyStorage->loadMultiple(); $options = array(); @@ -128,6 +131,7 @@ public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) { '#options' => $options, '#description' => $this->t('Select which vocabulary to show terms for in the regular options.'), '#default_value' => $this->options['vids'], + '#required' => TRUE, ); } } @@ -154,6 +158,21 @@ public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) { /** * {@inheritdoc} */ + public function validateExtraOptionsForm($form, FormStateInterface $form_state) { + parent::validateExtraOptionsForm($form, $form_state); + + // Don't allow to select more than one vocabulary when 'select' is chosen. + $vids = $form_state->getValue(['options', 'vids']); + $selection_type = $form_state->getValue(['options', 'type']); + if ($selection_type === 'select' && count($vids) > 1) { + $form_state->setErrorByName('options][vids', $this->t("You cannot select more than one vocabulary, when choosing 'Dropdown' as selection type.")); + } + } + + + /** + * {@inheritdoc} + */ public function submitExtraOptionsForm($form, FormStateInterface $form_state) { $vids = $form_state->getValue(['options', 'vids']); $form_state->setValue(['options', 'vids'], array_keys(array_filter($vids))); diff --git a/core/modules/taxonomy/src/Tests/Views/TaxonomyIndexTidUiTest.php b/core/modules/taxonomy/src/Tests/Views/TaxonomyIndexTidUiTest.php index 7dff50a..95dfe6b 100644 --- a/core/modules/taxonomy/src/Tests/Views/TaxonomyIndexTidUiTest.php +++ b/core/modules/taxonomy/src/Tests/Views/TaxonomyIndexTidUiTest.php @@ -3,6 +3,7 @@ namespace Drupal\taxonomy\Tests\Views; use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait; +use Drupal\node\Entity\Node; use Drupal\taxonomy\Entity\Term; use Drupal\taxonomy\Entity\Vocabulary; use Drupal\views\Tests\ViewTestData; @@ -49,9 +50,27 @@ protected function setUp() { $this->adminUser = $this->drupalCreateUser(['administer taxonomy', 'administer views']); $this->drupalLogin($this->adminUser); + $this->terms = $this->createVocabularyAndTerms('tags'); + ViewTestData::createTestViews(get_class($this), array('taxonomy_test_views')); + + Vocabulary::create([ + 'vid' => 'empty_vocabulary', + 'name' => 'Empty Vocabulary', + ])->save(); + } + + /** + * Creates a vocabulary and terms for it. + * + * @param string $vocab_id + * The vocabulary ID. + * + * @return \Drupal\taxonomy\TermInterface[][] + */ + protected function createVocabularyAndTerms($vocab_id) { Vocabulary::create([ - 'vid' => 'tags', - 'name' => 'Tags', + 'vid' => $vocab_id, + 'name' => $vocab_id, ])->save(); // Setup a hierarchy which looks like this: @@ -61,22 +80,18 @@ protected function setUp() { // term 2.0 // - term 2.1 // - term 2.2 + $terms = []; for ($i = 0; $i < 3; $i++) { for ($j = 0; $j <= $i; $j++) { - $this->terms[$i][$j] = $term = Term::create([ - 'vid' => 'tags', + $terms[$i][$j] = $term = Term::create([ + 'vid' => $vocab_id, 'name' => "Term $i.$j", 'parent' => isset($terms[$i][0]) ? $terms[$i][0]->id() : 0, ]); $term->save(); } } - ViewTestData::createTestViews(get_class($this), array('taxonomy_test_views')); - - Vocabulary::create([ - 'vid' => 'empty_vocabulary', - 'name' => 'Empty Vocabulary', - ])->save(); + return $terms; } /** @@ -127,6 +142,77 @@ public function testFilterUI() { $this->assertIdentical($expected, $view->calculateDependencies()->getDependencies()); } + public function testFilterUIWithMoreVocabularies() { + $terms2 = $this->createVocabularyAndTerms('tags2'); + + $node_type = $this->drupalCreateContentType(['type' => 'page']); + + // Create the tag field itself. + $field_name = 'taxonomy_tags'; + $this->createEntityReferenceField('node', $node_type->id(), $field_name, NULL, 'taxonomy_term'); + + $node0 = $this->drupalCreateNode([ + 'type' => 'page', + 'taxonomy_tags' => $this->terms[0][0]->id(), + ]); + $node0->save(); + $node1 = $this->drupalCreateNode([ + 'type' => 'page', + 'taxonomy_tags' => $terms2[0][0]->id(), + ]); + $node1->save(); + $node2 = $this->drupalCreateNode([ + 'type' => 'page', + 'taxonomy_tags' => [$this->terms[0][0]->id(), $terms2[0][0]->id()], + ]); + $node2->save(); + + $edit = [ + 'options[vids][tags]' => TRUE, + 'options[vids][tags2]' => TRUE, + 'options[type]' => 'select', + ]; + $this->drupalPostForm('admin/structure/views/nojs/handler-extra/test_filter_taxonomy_index_tid/default/filter/tid', $edit, 'Apply'); + $this->assertText("You cannot select more than one vocabulary, when choosing 'Dropdown' as selection type."); + + $edit = [ + 'options[vids][tags]' => TRUE, + 'options[vids][tags2]' => TRUE, + 'options[type]' => 'textfield', + ]; + $this->drupalPostForm('admin/structure/views/nojs/handler-extra/test_filter_taxonomy_index_tid/default/filter/tid', $edit, 'Apply'); + $this->drupalPostForm('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/default/filter/tid', [], 'Expose filter'); + $edit = ['options[value]' => '', 'options[reduce_duplicates]' => TRUE]; + $this->drupalPostForm(NULL, $edit, 'Apply'); + $this->drupalPostForm(NULL, [], 'Save'); + + $this->drupalGet('test-filter-taxonomy-index-tid', ['query' => ['tid' => '']]); + $xpath = $this->xpath('//div[@class="view-content"]//a'); + $this->assertIdentical(3, count($xpath)); + + $this->drupalGet('test-filter-taxonomy-index-tid', ['query' => ['tid' => "t1 ({$this->terms[0][0]->id()})"]]); + $xpath = $this->xpath('//div[@class="view-content"]//a'); + $this->assertIdentical(2, count($xpath)); + $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node0->toUrl()->toString()]); + $this->assertIdentical(1, count($xpath)); + $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node2->toUrl()->toString()]); + $this->assertIdentical(1, count($xpath)); + + $this->drupalGet('test-filter-taxonomy-index-tid', ['query' => ['tid' => "t2 ({$terms2[0][0]->id()})"]]); + $xpath = $this->xpath('//div[@class="view-content"]//a'); + $this->assertIdentical(2, count($xpath)); + $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node1->toUrl()->toString()]); + $this->assertIdentical(1, count($xpath)); + $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node2->toUrl()->toString()]); + $this->assertIdentical(1, count($xpath)); + + $this->drupalGet('test-filter-taxonomy-index-tid', ['query' => ['tid' => "t1 ({$this->terms[0][0]->id()}), t2 ({$terms2[0][0]->id()})"]]); + $xpath = $this->xpath('//div[@class="view-content"]//a'); + $this->assertIdentical(1, count($xpath)); + $xpath = $this->xpath('//div[@class="view-content"]//a[@href=:href]', [':href' => $node2->toUrl()->toString()]); + $this->assertIdentical(1, count($xpath)); + } + /** * Tests exposed taxonomy filters. */ diff --git a/core/modules/taxonomy/src/Tests/Views/Update/TaxonomyIndexTidUpdateTest.php b/core/modules/taxonomy/src/Tests/Views/Update/TaxonomyIndexTidUpdateTest.php new file mode 100644 index 0000000..ea3669a --- /dev/null +++ b/core/modules/taxonomy/src/Tests/Views/Update/TaxonomyIndexTidUpdateTest.php @@ -0,0 +1,41 @@ +databaseDumpFiles = [ + __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz', + __DIR__ . '/../../../../tests/fixtures/update/taxonomy-index-filter-multiple-vocabularies.php', + ]; + } + + /** + * Tests that field handlers are updated properly. + */ + public function testPostUpdateTaxonomyIndexFilterMultipleVocabularies() { + $this->runUpdates(); + + // Load and initialize our test view. + $view = View::load('test_filter_taxonomy_index_tid_pre_update'); + $data = $view->toArray(); + // Check that the field is using the expected base table. + $this->assertEqual(['tags'], $data['display']['default']['display_options']['filters']['tid']['vids']); + $this->assertTrue(empty($data['display']['default']['display_options']['filters']['tid']['vid'])); + } + +} diff --git a/core/modules/taxonomy/taxonomy.post_update.php b/core/modules/taxonomy/taxonomy.post_update.php new file mode 100644 index 0000000..2f4c98e --- /dev/null +++ b/core/modules/taxonomy/taxonomy.post_update.php @@ -0,0 +1,41 @@ +getStorage('view')->loadMultiple(); + + /* @var \Drupal\views\Entity\View[] $views */ + foreach ($views as $view) { + $save = FALSE; + $displays = $view->get('display'); + foreach (array_keys($displays) as $display_id) { + $display =& $view->getDisplay($display_id); + if (!empty($display['display_options']['filters'])) { + foreach ($display['display_options']['filters'] as &$filter) { + if ($filter['plugin_id'] === 'taxonomy_index_tid' && empty($filter['vids']) && !empty($filter['vid'])) { + $filter['vids'] = [$filter['vid']]; + unset($filter['vid']); + $save = TRUE; + } + } + } + } + if ($save) { + $view->save(); + } + } + +} + +/** + * @} End of "addtogroup updates-8.3.0 + */ + diff --git a/core/modules/taxonomy/tests/fixtures/update/taxonomy-index-filter-multiple-vocabularies.php b/core/modules/taxonomy/tests/fixtures/update/taxonomy-index-filter-multiple-vocabularies.php new file mode 100644 index 0000000..5e77c1d --- /dev/null +++ b/core/modules/taxonomy/tests/fixtures/update/taxonomy-index-filter-multiple-vocabularies.php @@ -0,0 +1,19 @@ +insert('config') + ->fields(array( + 'collection' => '', + 'name' => 'views.view.test_filter_taxonomy_index_tid_pre_update', + 'data' => serialize(Yaml::decode(file_get_contents('core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_pre_update.yml'))), + )) + ->execute(); diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml index 028d6d5..14e2732 100644 --- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml +++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml @@ -166,7 +166,8 @@ display: reduce_duplicates: false type: select limit: true - vid: tags + vids: + - tags hierarchy: true error_message: true plugin_id: taxonomy_index_tid diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid__non_existing_dependency.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid__non_existing_dependency.yml index 46c4a339..4377280 100644 --- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid__non_existing_dependency.yml +++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid__non_existing_dependency.yml @@ -167,7 +167,8 @@ display: reduce_duplicates: false type: select limit: true - vid: tags + vids: + - tags hierarchy: true error_message: true plugin_id: taxonomy_index_tid diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_depth.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_depth.yml index da366c5..333bafd 100644 --- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_depth.yml +++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_depth.yml @@ -165,7 +165,8 @@ display: reduce_duplicates: false type: select limit: true - vid: views_testing_tags + vids: + - views_testing_tags hierarchy: true depth: -2 error_message: true diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_pre_update.yml similarity index 97% copy from core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml copy to core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_pre_update.yml index 028d6d5..112a7ac 100644 --- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml +++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_pre_update.yml @@ -5,8 +5,8 @@ dependencies: - node - taxonomy - user -id: test_filter_taxonomy_index_tid -label: test_filter_taxonomy_index_tid +id: test_filter_taxonomy_index_tid_pre_update +label: test_filter_taxonomy_index_tid_pre_update module: views description: '' tag: '' diff --git a/core/modules/views_ui/src/Form/Ajax/ConfigHandlerExtra.php b/core/modules/views_ui/src/Form/Ajax/ConfigHandlerExtra.php index 0c10fb1..f2df045 100644 --- a/core/modules/views_ui/src/Form/Ajax/ConfigHandlerExtra.php +++ b/core/modules/views_ui/src/Form/Ajax/ConfigHandlerExtra.php @@ -93,6 +93,10 @@ public function buildForm(array $form, FormStateInterface $form_state) { */ public function validateForm(array &$form, FormStateInterface $form_state) { $form_state->get('handler')->validateExtraOptionsForm($form['options'], $form_state); + + if ($form_state->getErrors()) { + $form_state->set('rerender', TRUE); + } } /**