summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel Catchpole2017-09-04 14:13:49 (GMT)
committerNathaniel Catchpole2017-09-04 14:13:49 (GMT)
commit8210ebe0ae25d4b2107808d691b9c487bcbbb009 (patch)
tree644392852ed9c9ea6428a7f561f0062ade6271d4
parent4f24c9169b81403271a6f07d61a1dcfc5f778660 (diff)
Issue #2687773 by mikeker, sukanya.ramakrishnan, dawehner, amateescu, mroycroft, Lukas von Blarer, Lendude: Using checkboxes with an is
-rw-r--r--core/modules/views/src/Plugin/views/filter/ManyToOne.php3
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_checkboxes.yml155
-rw-r--r--core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php168
-rw-r--r--core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php42
-rw-r--r--core/modules/views/tests/themes/views_test_checkboxes_theme/views_test_checkboxes_theme.theme7
5 files changed, 332 insertions, 43 deletions
diff --git a/core/modules/views/src/Plugin/views/filter/ManyToOne.php b/core/modules/views/src/Plugin/views/filter/ManyToOne.php
index e678dee..86eb1f3 100644
--- a/core/modules/views/src/Plugin/views/filter/ManyToOne.php
+++ b/core/modules/views/src/Plugin/views/filter/ManyToOne.php
@@ -129,6 +129,9 @@ class ManyToOne extends InOperator {
if (empty($this->value)) {
return;
}
+ // Form API returns unchecked options in the form of option_id => 0. This
+ // breaks the generated query for "is all of" filters so we remove them.
+ $this->value = array_filter($this->value, 'static::arrayFilterZero');
$this->helper->addFilter();
}
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_checkboxes.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_checkboxes.yml
new file mode 100644
index 0000000..26b1d32
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_checkboxes.yml
@@ -0,0 +1,155 @@
+langcode: en
+status: true
+dependencies:
+ config:
+ - taxonomy.vocabulary.test_exposed_checkboxes
+ module:
+ - node
+ - taxonomy
+id: test_exposed_form_checkboxes
+label: ''
+module: views
+description: ''
+tag: ''
+base_table: node_field_data
+base_field: nid
+core: '8'
+display:
+ default:
+ display_options:
+ access:
+ type: none
+ cache:
+ type: tag
+ exposed_form:
+ options:
+ reset_button: true
+ type: basic
+ filters:
+ type:
+ id: type
+ table: node_field_data
+ field: type
+ relationship: none
+ group_type: group
+ admin_label: ''
+ operator: in
+ value: { }
+ group: 1
+ exposed: true
+ expose:
+ operator_id: type_op
+ label: 'Content: Type'
+ description: 'Exposed description'
+ use_operator: false
+ operator: ''
+ identifier: type
+ required: false
+ remember: false
+ multiple: true
+ remember_roles:
+ authenticated: authenticated
+ anonymous: '0'
+ administrator: '0'
+ reduce: false
+ 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: in_operator
+ entity_type: node
+ entity_field: type
+ tid:
+ id: tid
+ table: taxonomy_index
+ field: tid
+ relationship: none
+ group_type: group
+ admin_label: ''
+ operator: and
+ value: { }
+ group: 1
+ exposed: true
+ expose:
+ operator_id: tid_op
+ label: 'Has taxonomy term'
+ description: ''
+ use_operator: false
+ operator: tid_op
+ identifier: tid
+ required: false
+ remember: false
+ multiple: true
+ remember_roles:
+ authenticated: authenticated
+ anonymous: '0'
+ administrator: '0'
+ reduce: false
+ is_grouped: false
+ group_info:
+ label: ''
+ description: ''
+ identifier: ''
+ optional: true
+ widget: select
+ multiple: false
+ remember: false
+ default_group: All
+ default_group_multiple: { }
+ group_items: { }
+ reduce_duplicates: false
+ type: select
+ limit: true
+ vid: test_exposed_checkboxes
+ hierarchy: false
+ error_message: true
+ plugin_id: taxonomy_index_tid
+ pager:
+ type: full
+ query:
+ options:
+ query_comment: ''
+ type: views_query
+ style:
+ type: default
+ row:
+ type: 'entity:node'
+ display_extenders: { }
+ display_plugin: default
+ display_title: Master
+ id: default
+ position: 0
+ cache_metadata:
+ max-age: -1
+ contexts:
+ - 'languages:language_interface'
+ - url
+ - url.query_args
+ - user
+ - 'user.node_grants:view'
+ tags: { }
+ page_1:
+ display_options:
+ path: test_exposed_form_checkboxes
+ display_extenders: { }
+ display_plugin: page
+ display_title: Page
+ id: page_1
+ position: 0
+ cache_metadata:
+ max-age: -1
+ contexts:
+ - 'languages:language_interface'
+ - url
+ - url.query_args
+ - user
+ - 'user.node_grants:view'
+ tags: { }
diff --git a/core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php b/core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php
new file mode 100644
index 0000000..80584d7
--- /dev/null
+++ b/core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php
@@ -0,0 +1,168 @@
+<?php
+
+namespace Drupal\Tests\views\Functional\Plugin;
+
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
+use Drupal\taxonomy\Entity\Term;
+use Drupal\taxonomy\Entity\Vocabulary;
+use Drupal\Tests\views\Functional\ViewTestBase;
+use Drupal\views\Tests\ViewTestData;
+use Drupal\views\Views;
+
+/**
+ * Tests exposed forms functionality.
+ *
+ * @group views
+ */
+class ExposedFormCheckboxesTest extends ViewTestBase {
+
+ use EntityReferenceTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $testViews = ['test_exposed_form_checkboxes'];
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['node', 'views_ui', 'taxonomy'];
+
+ /**
+ * Test terms.
+ *
+ * @var array
+ */
+ public $terms = [];
+
+ /**
+ * Vocabulary for testing checkbox options.
+ *
+ * @var \Drupal\taxonomy\Entity\Vocabulary
+ */
+ public $vocabulary;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp(FALSE);
+
+ // Create a vocabulary and entity reference field so we can test the "is all
+ // of" filter operator. Must be done ahead of the view import so the
+ // vocabulary is in place to meet the view dependencies.
+ $vocabulary = Vocabulary::create([
+ 'name' => 'test_exposed_checkboxes',
+ 'vid' => 'test_exposed_checkboxes',
+ 'nodes' => ['article' => 'article'],
+ ]);
+ $vocabulary->save();
+ $this->vocabulary = $vocabulary;
+
+ ViewTestData::createTestViews(self::class, ['views_test_config']);
+ $this->enableViewsTestModule();
+
+ // Create two content types.
+ $this->drupalCreateContentType(['type' => 'article']);
+ $this->drupalCreateContentType(['type' => 'page']);
+
+ // Create some random nodes: 5 articles, one page.
+ for ($i = 0; $i < 5; $i++) {
+ $this->drupalCreateNode(['type' => 'article']);
+ }
+ $this->drupalCreateNode(['type' => 'page']);
+ }
+
+ /**
+ * Tests overriding the default render option with checkboxes.
+ */
+ public function testExposedFormRenderCheckboxes() {
+ // Use a test theme to convert multi-select elements into checkboxes.
+ \Drupal::service('theme_handler')->install(['views_test_checkboxes_theme']);
+ $this->config('system.theme')
+ ->set('default', 'views_test_checkboxes_theme')
+ ->save();
+
+ // Only display 5 items per page so we can test that paging works.
+ $view = Views::getView('test_exposed_form_checkboxes');
+ $display = &$view->storage->getDisplay('default');
+ $display['display_options']['pager']['options']['items_per_page'] = 5;
+
+ $view->save();
+ $this->drupalGet('test_exposed_form_checkboxes');
+
+ $actual = $this->xpath('//form//input[@type="checkbox" and @name="type[article]"]');
+ $this->assertEqual(count($actual), 1, 'Article option renders as a checkbox.');
+ $actual = $this->xpath('//form//input[@type="checkbox" and @name="type[page]"]');
+ $this->assertEqual(count($actual), 1, 'Page option renders as a checkbox');
+
+ // Ensure that all results are displayed.
+ $rows = $this->xpath("//div[contains(@class, 'views-row')]");
+ $this->assertEqual(count($rows), 5, '5 rows are displayed by default on the first page when no options are checked.');
+
+ $this->clickLink('Page 2');
+ $rows = $this->xpath("//div[contains(@class, 'views-row')]");
+ $this->assertEqual(count($rows), 1, '1 row is displayed by default on the second page when no options are checked.');
+ $this->assertNoText('An illegal choice has been detected. Please contact the site administrator.');
+ }
+
+ /**
+ * Tests that "is all of" filters work with checkboxes.
+ */
+ public function testExposedIsAllOfFilter() {
+ foreach (['Term 1', 'Term 2', 'Term 3'] as $term_name) {
+ // Add a few terms to the new vocabulary.
+ $term = Term::create([
+ 'name' => $term_name,
+ 'vid' => $this->vocabulary->id(),
+ ]);
+ $term->save();
+ $this->terms[] = $term;
+ }
+
+ // Create a field.
+ $field_name = Unicode::strtolower($this->randomMachineName());
+ $handler_settings = [
+ 'target_bundles' => [
+ $this->vocabulary->id() => $this->vocabulary->id(),
+ ],
+ 'auto_create' => FALSE,
+ ];
+ $this->createEntityReferenceField('node', 'article', $field_name, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
+
+ // Add some test nodes.
+ $this->createNode([
+ 'type' => 'article',
+ $field_name => [$this->terms[0]->id(), $this->terms[1]->id()],
+ ]);
+ $this->createNode([
+ 'type' => 'article',
+ $field_name => [$this->terms[0]->id(), $this->terms[2]->id()],
+ ]);
+
+ // Use a test theme to convert multi-select elements into checkboxes.
+ \Drupal::service('theme_handler')->install(['views_test_checkboxes_theme']);
+ $this->config('system.theme')
+ ->set('default', 'views_test_checkboxes_theme')
+ ->save();
+
+ $this->drupalGet('test_exposed_form_checkboxes');
+
+ // Ensure that all results are displayed.
+ $rows = $this->xpath("//div[contains(@class, 'views-row')]");
+ $this->assertEqual(count($rows), 8, 'All rows are displayed by default on the first page when no options are checked.');
+ $this->assertNoText('An illegal choice has been detected. Please contact the site administrator.');
+
+ // Select one option and ensure we still have results.
+ $tid = $this->terms[0]->id();
+ $this->drupalPostForm(NULL, ["tid[$tid]" => $tid], t('Apply'));
+
+ // Ensure only nodes tagged with $tid are displayed.
+ $rows = $this->xpath("//div[contains(@class, 'views-row')]");
+ $this->assertEqual(count($rows), 2, 'Correct rows are displayed when a tid is selected.');
+ $this->assertNoText('An illegal choice has been detected. Please contact the site administrator.');
+ }
+
+}
diff --git a/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php b/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
index 134f7b5..220dc24 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
@@ -192,48 +192,6 @@ class ExposedFormTest extends ViewTestBase {
}
/**
- * Tests overriding the default render option with checkboxes.
- */
- public function testExposedFormRenderCheckboxes() {
- // Make sure we have at least two options for node type.
- $this->drupalCreateContentType(['type' => 'page']);
- $this->drupalCreateNode(['type' => 'page']);
-
- // Use a test theme to convert multi-select elements into checkboxes.
- \Drupal::service('theme_handler')->install(['views_test_checkboxes_theme']);
- $this->config('system.theme')
- ->set('default', 'views_test_checkboxes_theme')
- ->save();
-
- // Set the "type" filter to multi-select.
- $view = Views::getView('test_exposed_form_buttons');
- $filter = $view->getHandler('page_1', 'filter', 'type');
- $filter['expose']['multiple'] = TRUE;
- $view->setHandler('page_1', 'filter', 'type', $filter);
-
- // Only display 5 items per page so we can test that paging works.
- $display = &$view->storage->getDisplay('default');
- $display['display_options']['pager']['options']['items_per_page'] = 5;
-
- $view->save();
- $this->drupalGet('test_exposed_form_buttons');
-
- $actual = $this->xpath('//form//input[@type="checkbox" and @name="type[article]"]');
- $this->assertEqual(count($actual), 1, 'Article option renders as a checkbox.');
- $actual = $this->xpath('//form//input[@type="checkbox" and @name="type[page]"]');
- $this->assertEqual(count($actual), 1, 'Page option renders as a checkbox');
-
- // Ensure that all results are displayed.
- $rows = $this->xpath("//div[contains(@class, 'views-row')]");
- $this->assertEqual(count($rows), 5, '5 rows are displayed by default on the first page when no options are checked.');
-
- $this->clickLink('Page 2');
- $rows = $this->xpath("//div[contains(@class, 'views-row')]");
- $this->assertEqual(count($rows), 1, '1 row is displayed by default on the second page when no options are checked.');
- $this->assertNoText('An illegal choice has been detected. Please contact the site administrator.');
- }
-
- /**
* Tests the exposed block functionality.
*/
public function testExposedBlock() {
diff --git a/core/modules/views/tests/themes/views_test_checkboxes_theme/views_test_checkboxes_theme.theme b/core/modules/views/tests/themes/views_test_checkboxes_theme/views_test_checkboxes_theme.theme
index cab52ac..393c6cb 100644
--- a/core/modules/views/tests/themes/views_test_checkboxes_theme/views_test_checkboxes_theme.theme
+++ b/core/modules/views/tests/themes/views_test_checkboxes_theme/views_test_checkboxes_theme.theme
@@ -11,5 +11,10 @@ use Drupal\Core\Form\FormStateInterface;
* Changes an exposed "type" filter from a multi-select to checkboxes.
*/
function views_test_checkboxes_theme_form_views_exposed_form_alter(&$form, FormStateInterface $form_state) {
- $form['type']['#type'] = 'checkboxes';
+ if (isset($form['type'])) {
+ $form['type']['#type'] = 'checkboxes';
+ }
+ if (isset($form['tid'])) {
+ $form['tid']['#type'] = 'checkboxes';
+ }
}