Created
April 20, 2017 17:12
-
-
Save edwardchan/5acb6d3d00fbcb521c55b1ae1dedfed6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php | |
index a68c3c8..cd89b77 100644 | |
--- a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php | |
+++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php | |
@@ -6,6 +6,7 @@ | |
use Drupal\Core\Form\FormStateInterface; | |
use Drupal\taxonomy\Entity\Term; | |
use Drupal\taxonomy\TermStorageInterface; | |
+use Drupal\taxonomy\VocabularyInterface; | |
use Drupal\taxonomy\VocabularyStorageInterface; | |
use Drupal\views\ViewExecutable; | |
use Drupal\views\Plugin\views\display\DisplayPluginBase; | |
@@ -78,11 +79,16 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o | |
parent::init($view, $display, $options); | |
if (!empty($this->definition['vocabulary'])) { | |
- $this->options['vid'] = $this->definition['vocabulary']; | |
+ $this->options['vids'] = [$this->definition['vocabulary']]; | |
+ } | |
+ if (empty($this->options['vids']) && !empty($this->options['vid'])) { | |
+ $this->options['vids'] = [$this->options['vid']]; | |
} | |
} | |
- public function hasExtraOptions() { return TRUE; } | |
+ public function hasExtraOptions() { | |
+ return TRUE; | |
+ } | |
/** | |
* {@inheritdoc} | |
@@ -96,13 +102,16 @@ protected function defineOptions() { | |
$options['type'] = ['default' => 'textfield']; | |
$options['limit'] = ['default' => TRUE]; | |
- $options['vid'] = ['default' => '']; | |
+ $options['vids'] = ['default' => []]; | |
$options['hierarchy'] = ['default' => FALSE]; | |
$options['error_message'] = ['default' => TRUE]; | |
return $options; | |
} | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) { | |
$vocabularies = $this->vocabularyStorage->loadMultiple(); | |
$options = []; | |
@@ -112,18 +121,19 @@ public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) { | |
if ($this->options['limit']) { | |
// We only do this when the form is displayed. | |
- if (empty($this->options['vid'])) { | |
+ if (empty($this->options['vids'])) { | |
$first_vocabulary = reset($vocabularies); | |
- $this->options['vid'] = $first_vocabulary->id(); | |
+ $this->options['vids'] = $first_vocabulary->id(); | |
} | |
if (empty($this->definition['vocabulary'])) { | |
- $form['vid'] = [ | |
- '#type' => 'radios', | |
+ $form['vids'] = [ | |
+ '#type' => 'checkboxes', | |
'#title' => $this->t('Vocabulary'), | |
'#options' => $options, | |
'#description' => $this->t('Select which vocabulary to show terms for in the regular options.'), | |
- '#default_value' => $this->options['vid'], | |
+ '#default_value' => $this->options['vids'], | |
+ '#required' => TRUE, | |
]; | |
} | |
} | |
@@ -131,7 +141,10 @@ public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) { | |
$form['type'] = [ | |
'#type' => 'radios', | |
'#title' => $this->t('Selection type'), | |
- '#options' => ['select' => $this->t('Dropdown'), 'textfield' => $this->t('Autocomplete')], | |
+ '#options' => [ | |
+ 'select' => $this->t('Dropdown'), | |
+ 'textfield' => $this->t('Autocomplete') | |
+ ], | |
'#default_value' => $this->options['type'], | |
]; | |
@@ -147,9 +160,32 @@ 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))); | |
+ } | |
+ | |
protected function valueForm(&$form, FormStateInterface $form_state) { | |
- $vocabulary = $this->vocabularyStorage->load($this->options['vid']); | |
- if (empty($vocabulary) && $this->options['limit']) { | |
+ $vocabularies = $this->vocabularyStorage->loadMultiple($this->options['vids']); | |
+ $vocabulary = reset($vocabularies); | |
+ if (empty($vocabularies) && $this->options['limit']) { | |
$form['markup'] = [ | |
'#markup' => '<div class="js-form-item form-item">' . $this->t('An invalid vocabulary is selected. Please change it in the options.') . '</div>', | |
]; | |
@@ -158,8 +194,11 @@ protected function valueForm(&$form, FormStateInterface $form_state) { | |
if ($this->options['type'] == 'textfield') { | |
$terms = $this->value ? Term::loadMultiple(($this->value)) : []; | |
+ $vocabulary_labels = array_map(function (VocabularyInterface $vocabulary) { | |
+ return $vocabulary->label(); | |
+ }, $vocabularies); | |
$form['value'] = [ | |
- '#title' => $this->options['limit'] ? $this->t('Select terms from vocabulary @voc', ['@voc' => $vocabulary->label()]) : $this->t('Select terms'), | |
+ '#title' => $this->options['limit'] ? $this->t('Select terms from vocabularies @vocs', array('@vocs' => implode(', ', $vocabulary_labels))) : $this->t('Select terms'), | |
'#type' => 'textfield', | |
'#default_value' => EntityAutocomplete::getEntityLabels($terms), | |
]; | |
@@ -167,7 +206,7 @@ protected function valueForm(&$form, FormStateInterface $form_state) { | |
if ($this->options['limit']) { | |
$form['value']['#type'] = 'entity_autocomplete'; | |
$form['value']['#target_type'] = 'taxonomy_term'; | |
- $form['value']['#selection_settings']['target_bundles'] = [$vocabulary->id()]; | |
+ $form['value']['#selection_settings']['target_bundles'] = array_keys($vocabularies); | |
$form['value']['#tags'] = TRUE; | |
$form['value']['#process_default_value'] = FALSE; | |
} | |
@@ -180,7 +219,11 @@ protected function valueForm(&$form, FormStateInterface $form_state) { | |
if ($tree) { | |
foreach ($tree as $term) { | |
$choice = new \stdClass(); | |
- $choice->option = [$term->id() => str_repeat('-', $term->depth) . \Drupal::entityManager()->getTranslationFromContext($term)->label()]; | |
+ $choice->option = [ | |
+ $term->id() => str_repeat('-', $term->depth) . \Drupal::entityManager() | |
+ ->getTranslationFromContext($term) | |
+ ->label() | |
+ ]; | |
$options[] = $choice; | |
} | |
} | |
@@ -198,7 +241,9 @@ protected function valueForm(&$form, FormStateInterface $form_state) { | |
} | |
$terms = Term::loadMultiple($query->execute()); | |
foreach ($terms as $term) { | |
- $options[$term->id()] = \Drupal::entityManager()->getTranslationFromContext($term)->label(); | |
+ $options[$term->id()] = \Drupal::entityManager() | |
+ ->getTranslationFromContext($term) | |
+ ->label(); | |
} | |
} | |
@@ -363,7 +408,9 @@ public function adminSummary() { | |
$this->value = array_filter($this->value); | |
$terms = Term::loadMultiple($this->value); | |
foreach ($terms as $term) { | |
- $this->valueOptions[$term->id()] = \Drupal::entityManager()->getTranslationFromContext($term)->label(); | |
+ $this->valueOptions[$term->id()] = \Drupal::entityManager() | |
+ ->getTranslationFromContext($term) | |
+ ->label(); | |
} | |
} | |
return parent::adminSummary(); | |
@@ -387,9 +434,10 @@ public function getCacheContexts() { | |
*/ | |
public function calculateDependencies() { | |
$dependencies = parent::calculateDependencies(); | |
- | |
- $vocabulary = $this->vocabularyStorage->load($this->options['vid']); | |
- $dependencies[$vocabulary->getConfigDependencyKey()][] = $vocabulary->getConfigDependencyName(); | |
+ $vocabularies = $this->vocabularyStorage->loadMultiple($this->options['vids']); | |
+ foreach ($vocabularies as $vocabulary) { | |
+ $dependencies[$vocabulary->getConfigDependencyKey()][] = $vocabulary->getConfigDependencyName(); | |
+ } | |
foreach ($this->termStorage->loadMultiple($this->options['value']) as $term) { | |
$dependencies[$term->getConfigDependencyKey()][] = $term->getConfigDependencyName(); | |
diff --git a/core/modules/taxonomy/src/Tests/Views/TaxonomyIndexTidUiTest.php b/core/modules/taxonomy/src/Tests/Views/TaxonomyIndexTidUiTest.php | |
index efa5191..d89fd79 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; | |
@@ -24,14 +25,23 @@ class TaxonomyIndexTidUiTest extends UITestBase { | |
* | |
* @var array | |
*/ | |
- public static $testViews = ['test_filter_taxonomy_index_tid', 'test_taxonomy_term_name']; | |
+ public static $testViews = [ | |
+ 'test_filter_taxonomy_index_tid', | |
+ 'test_taxonomy_term_name' | |
+ ]; | |
/** | |
* Modules to enable. | |
* | |
* @var array | |
*/ | |
- public static $modules = ['node', 'taxonomy', 'views', 'views_ui', 'taxonomy_test_views']; | |
+ public static $modules = [ | |
+ 'node', | |
+ 'taxonomy', | |
+ 'views', | |
+ 'views_ui', | |
+ 'taxonomy_test_views' | |
+ ]; | |
/** | |
* A nested array of \Drupal\taxonomy\TermInterface objects. | |
@@ -46,13 +56,30 @@ class TaxonomyIndexTidUiTest extends UITestBase { | |
protected function setUp() { | |
parent::setUp(); | |
- $this->adminUser = $this->drupalCreateUser(['administer taxonomy', 'administer views']); | |
+ $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' => 'tags', | |
- 'name' => 'Tags', | |
+ '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) { | |
// Setup a hierarchy which looks like this: | |
// term 0.0 | |
@@ -61,22 +88,19 @@ 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), ['taxonomy_test_views']); | |
- Vocabulary::create([ | |
- 'vid' => 'empty_vocabulary', | |
- 'name' => 'Empty Vocabulary', | |
- ])->save(); | |
+ return $terms; | |
} | |
/** | |
@@ -124,7 +148,93 @@ public function testFilterUI() { | |
'user', | |
], | |
]; | |
- $this->assertIdentical($expected, $view->calculateDependencies()->getDependencies()); | |
+ $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[operator]' => 'and', | |
+ '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)); | |
} | |
/** | |
diff --git a/core/modules/taxonomy/taxonomy.post_update.php b/core/modules/taxonomy/taxonomy.post_update.php | |
index e69de29..ed7cb4a 100644 | |
--- a/core/modules/taxonomy/taxonomy.post_update.php | |
+++ b/core/modules/taxonomy/taxonomy.post_update.php | |
@@ -0,0 +1,39 @@ | |
+<?php | |
+ | |
+/** | |
+ * @addtogroup updates-8.3.0 | |
+ * @{ | |
+ */ | |
+ | |
+/** | |
+ * Update views using taxonomy_index_tid to use vids instead of vid. | |
+ */ | |
+function taxonomy_post_update_taxonomy_index_filter_multiple_vocabularies() { | |
+ // Load all views. | |
+ $views = \Drupal::entityTypeManager()->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 | |
index e69de29..b4c8bb7 100644 | |
--- 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 | |
@@ -0,0 +1,19 @@ | |
+<?php | |
+ | |
+/** | |
+ * @file | |
+ * Test fixture. | |
+ */ | |
+ | |
+use Drupal\Core\Database\Database; | |
+use Drupal\Core\Serialization\Yaml; | |
+ | |
+$connection = Database::getConnection(); | |
+ | |
+$connection->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(); | |
\ No newline at end of file | |
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 ab5e1d8..ed6f358 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 a5c5828..0fe8db8 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 856543a..0ca41c9 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/views/views.module b/core/modules/views/views.module | |
index f9bd098..d82cd75 100644 | |
--- a/core/modules/views/views.module | |
+++ b/core/modules/views/views.module | |
@@ -13,6 +13,7 @@ | |
use Drupal\Core\Routing\RouteMatchInterface; | |
use Drupal\Core\Url; | |
use Drupal\views\Plugin\Derivative\ViewsLocalTask; | |
+use Drupal\views\ViewEntityInterface; | |
use Drupal\views\ViewExecutable; | |
use Drupal\views\Entity\View; | |
use Drupal\views\Render\ViewsRenderPipelineMarkup; | |
@@ -29,7 +30,10 @@ function views_help($route_name, RouteMatchInterface $route_match) { | |
$output .= '<h3>' . t('About') . '</h3>'; | |
$output .= '<p>' . t('The Views module provides a back end to fetch information from content, user accounts, taxonomy terms, and other entities from the database and present it to the user as a grid, HTML list, table, unformatted list, etc. The resulting displays are known generally as <em>views</em>.') . '</p>'; | |
$output .= '<p>' . t('For more information, see the <a href=":views">online documentation for the Views module</a>.', [':views' => 'https://www.drupal.org/documentation/modules/views']) . '</p>'; | |
- $output .= '<p>' . t('In order to create and modify your own views using the administration and configuration user interface, you will need to enable either the Views UI module in core or a contributed module that provides a user interface for Views. See the <a href=":views-ui">Views UI module help page</a> for more information.', [':views-ui' => (\Drupal::moduleHandler()->moduleExists('views_ui')) ? \Drupal::url('help.page', ['name' => 'views_ui']) : '#']) . '</p>'; | |
+ $output .= '<p>' . t('In order to create and modify your own views using the administration and configuration user interface, you will need to enable either the Views UI module in core or a contributed module that provides a user interface for Views. See the <a href=":views-ui">Views UI module help page</a> for more information.', [ | |
+ ':views-ui' => (\Drupal::moduleHandler() | |
+ ->moduleExists('views_ui')) ? \Drupal::url('help.page', ['name' => 'views_ui']) : '#' | |
+ ]) . '</p>'; | |
$output .= '<h3>' . t('Uses') . '</h3>'; | |
$output .= '<dl>'; | |
$output .= '<dt>' . t('Adding functionality to administrative pages') . '</dt>'; | |
@@ -91,8 +95,13 @@ function views_theme($existing, $type, $theme, $path) { | |
// Our extra version of pager from pager.inc | |
$hooks['views_mini_pager'] = $base + [ | |
- 'variables' => ['tags' => [], 'quantity' => 9, 'element' => 0, 'parameters' => []], | |
- ]; | |
+ 'variables' => [ | |
+ 'tags' => [], | |
+ 'quantity' => 9, | |
+ 'element' => 0, | |
+ 'parameters' => [] | |
+ ], | |
+ ]; | |
$variables = [ | |
// For displays, we pass in a dummy array as the first parameter, since | |
@@ -113,22 +122,42 @@ function views_theme($existing, $type, $theme, $path) { | |
'attachment_before' => [], | |
'attachment_after' => [], | |
], | |
- 'style' => ['view' => NULL, 'options' => NULL, 'rows' => NULL, 'title' => NULL], | |
- 'row' => ['view' => NULL, 'options' => NULL, 'row' => NULL, 'field_alias' => NULL], | |
+ 'style' => [ | |
+ 'view' => NULL, | |
+ 'options' => NULL, | |
+ 'rows' => NULL, | |
+ 'title' => NULL | |
+ ], | |
+ 'row' => [ | |
+ 'view' => NULL, | |
+ 'options' => NULL, | |
+ 'row' => NULL, | |
+ 'field_alias' => NULL | |
+ ], | |
'exposed_form' => ['view' => NULL, 'options' => NULL], | |
'pager' => [ | |
- 'view' => NULL, 'options' => NULL, | |
- 'tags' => [], 'quantity' => 9, 'element' => 0, 'parameters' => [] | |
+ 'view' => NULL, | |
+ 'options' => NULL, | |
+ 'tags' => [], | |
+ 'quantity' => 9, | |
+ 'element' => 0, | |
+ 'parameters' => [] | |
], | |
]; | |
// Default view themes | |
$hooks['views_view_field'] = $base + [ | |
- 'variables' => ['view' => NULL, 'field' => NULL, 'row' => NULL], | |
- ]; | |
+ 'variables' => ['view' => NULL, 'field' => NULL, 'row' => NULL], | |
+ ]; | |
$hooks['views_view_grouping'] = $base + [ | |
- 'variables' => ['view' => NULL, 'grouping' => NULL, 'grouping_level' => NULL, 'rows' => NULL, 'title' => NULL], | |
- ]; | |
+ 'variables' => [ | |
+ 'view' => NULL, | |
+ 'grouping' => NULL, | |
+ 'grouping_level' => NULL, | |
+ 'rows' => NULL, | |
+ 'title' => NULL | |
+ ], | |
+ ]; | |
// Only display, pager, row, and style plugins can provide theme hooks. | |
$plugin_types = [ | |
@@ -140,7 +169,8 @@ function views_theme($existing, $type, $theme, $path) { | |
]; | |
$plugins = []; | |
foreach ($plugin_types as $plugin_type) { | |
- $plugins[$plugin_type] = Views::pluginManager($plugin_type)->getDefinitions(); | |
+ $plugins[$plugin_type] = Views::pluginManager($plugin_type) | |
+ ->getDefinitions(); | |
} | |
$module_handler = \Drupal::moduleHandler(); | |
@@ -211,12 +241,12 @@ function views_theme($existing, $type, $theme, $path) { | |
} | |
$hooks['views_form_views_form'] = $base + [ | |
- 'render element' => 'form', | |
- ]; | |
+ 'render element' => 'form', | |
+ ]; | |
$hooks['views_exposed_form'] = $base + [ | |
- 'render element' => 'form', | |
- ]; | |
+ 'render element' => 'form', | |
+ ]; | |
return $hooks; | |
} | |
@@ -237,9 +267,10 @@ function views_preprocess_node(&$variables) { | |
// If a node is being rendered in a view, and the view does not have a path, | |
// prevent drupal from accidentally setting the $page variable: | |
if (!empty($variables['view']->current_display) | |
- && $variables['page'] | |
- && $variables['view_mode'] == 'full' | |
- && !$variables['view']->display_handler->hasPath()) { | |
+ && $variables['page'] | |
+ && $variables['view_mode'] == 'full' | |
+ && !$variables['view']->display_handler->hasPath() | |
+ ) { | |
$variables['page'] = FALSE; | |
} | |
} | |
@@ -361,18 +392,25 @@ function views_add_contextual_links(&$render_element, $location, $display_id, ar | |
if (!isset($view_element)) { | |
$view_element = $render_element; | |
} | |
- $view_element['#cache_properties'] = ['view_id', 'view_display_show_admin_links', 'view_display_plugin_id']; | |
+ $view_element['#cache_properties'] = [ | |
+ 'view_id', | |
+ 'view_display_show_admin_links', | |
+ 'view_display_plugin_id' | |
+ ]; | |
$view_id = $view_element['#view_id']; | |
$show_admin_links = $view_element['#view_display_show_admin_links']; | |
$display_plugin_id = $view_element['#view_display_plugin_id']; | |
// Do not do anything if the view is configured to hide its administrative | |
// links or if the Contextual Links module is not enabled. | |
- if (\Drupal::moduleHandler()->moduleExists('contextual') && $show_admin_links) { | |
+ if (\Drupal::moduleHandler() | |
+ ->moduleExists('contextual') && $show_admin_links | |
+ ) { | |
// Also do not do anything if the display plugin has not defined any | |
// contextual links that are intended to be displayed in the requested | |
// location. | |
- $plugin = Views::pluginManager('display')->getDefinition($display_plugin_id); | |
+ $plugin = Views::pluginManager('display') | |
+ ->getDefinition($display_plugin_id); | |
// If contextual_links_locations are not set, provide a sane default. (To | |
// avoid displaying any contextual links at all, a display plugin can still | |
// set 'contextual_links_locations' to, e.g., {""}.) | |
@@ -617,7 +655,8 @@ function views_pre_render_views_form_views_form($element) { | |
$replace[] = isset($element[$field_name][$row_id]) ? drupal_render($element[$field_name][$row_id]) : ''; | |
} | |
// Add in substitutions from hook_views_form_substitutions(). | |
- $substitutions = \Drupal::moduleHandler()->invokeAll('views_form_substitutions'); | |
+ $substitutions = \Drupal::moduleHandler() | |
+ ->invokeAll('views_form_substitutions'); | |
foreach ($substitutions as $placeholder => $substitution) { | |
$search[] = Html::escape($placeholder); | |
// Ensure that any replacements made are safe to make. | |
@@ -828,3 +867,30 @@ function views_view_delete(EntityInterface $entity) { | |
} | |
} | |
} | |
+ | |
+/** | |
+ * Implements hook_view_presave(). | |
+ * | |
+ * Provides a BC layer for modules providing old configurations. | |
+ */ | |
+function views_view_presave(ViewEntityInterface $view) { | |
+ foreach ($view->get('display') as $display_name => $display) { | |
+ if (isset($display['display_options']['filters'])) { | |
+ foreach ($display['display_options']['filters'] as $filter_name => $filter) { | |
+ if (isset($filter['plugin_id']) && $filter['plugin_id'] === 'boolean') { | |
+ $new_value = FALSE; | |
+ // Update all boolean and integer values to strings. | |
+ if ($filter['value'] === TRUE || $filter['value'] === 1) { | |
+ $new_value = '1'; | |
+ } | |
+ elseif ($filter['value'] === FALSE || $filter['value'] === 0) { | |
+ $new_value = '0'; | |
+ } | |
+ if ($new_value !== FALSE) { | |
+ $view->set("display.$display_name.display_options.filters.$filter_name.value", $new_value); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+} | |
diff --git a/core/modules/views_ui/src/Form/Ajax/ConfigHandlerExtra.php b/core/modules/views_ui/src/Form/Ajax/ConfigHandlerExtra.php | |
index a79d031..36f2df3 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); | |
+ } | |
} | |
/** |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment