From 4f8d15426c48bbc5d2c237f23ff4d680e69ed428 Mon Sep 17 00:00:00 2001 From: Nathan Rogers Date: Fri, 28 Jul 2023 15:58:50 -0600 Subject: [PATCH 1/4] Skip auto processing on required empty text exposed filters. --- .../exposed_form/ExposedFormPluginBase.php | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php index d986a97d0574..18308f57d227 100644 --- a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php +++ b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php @@ -253,13 +253,32 @@ public function exposedFormAlter(&$form, FormStateInterface $form_state) { '#weight' => 10, ]; - // Get an array of exposed filters, keyed by identifier option. + // Get an array of exposed filters and exposed required text filters, + // keyed by identifier option. $exposed_filters = []; + $text_filter_plugin_ids = ['string', 'combine']; + $exposed_required_text_filters = []; foreach ($this->view->filter as $id => $handler) { if ($handler->canExpose() && $handler->isExposed() && !empty($handler->options['expose']['identifier'])) { + if ($handler->options['expose']['required'] && in_array($handler->options['plugin_id'], $text_filter_plugin_ids)) { + $exposed_required_text_filters[$handler->options['expose']['identifier']] = $id; + } $exposed_filters[$handler->options['expose']['identifier']] = $id; } } + + // If any required exposed text filters loop through them to see if they + // have any input. If not don't auto process the form. + if (!empty($exposed_required_text_filters)) { + // Prevent form validation if exposed text filter is not set. + $exposed_input = $this->view->getExposedInput(); + foreach ($exposed_required_text_filters as $key => $required_filter) { + if (!$exposed_input || empty($exposed_input[$key])) { + $form_state->setAlwaysProcess(FALSE); + break; + } + } + } $all_exposed = array_merge($exposed_sorts, $exposed_filters); // Set the access to FALSE if there is no exposed input. -- GitLab From 9afd86876914a4a6ab932be41b574d77d3ec7f44 Mon Sep 17 00:00:00 2001 From: Nathan Rogers Date: Tue, 8 Aug 2023 00:08:10 -0600 Subject: [PATCH 2/4] Added test showing issue. --- ...test_exposed_form_required_text_filter.yml | 90 +++++++++++++++++++ .../src/Functional/Plugin/ExposedFormTest.php | 21 ++++- 2 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_required_text_filter.yml diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_required_text_filter.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_required_text_filter.yml new file mode 100644 index 000000000000..dc1cf42793fd --- /dev/null +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_required_text_filter.yml @@ -0,0 +1,90 @@ +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.teaser + - node.type.article + - node.type.page + module: + - node +id: test_exposed_form_required_text_filter +label: '' +module: views +description: '' +tag: '' +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + pager: + type: full + exposed_form: + type: input_required + options: + submit_button: Apply + reset_button: true + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + text_input_required: 'Select any filter and click on Apply to see results' + text_input_required_format: basic_html + access: + type: none + cache: + type: tag + filters: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: title + plugin_id: string + operator: '=' + value: '' + group: 1 + exposed: true + expose: + operator_id: title_op + label: Title + description: '' + use_operator: false + operator: title_op + operator_limit_selection: false + operator_list: { } + identifier: title + required: true + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + content_editor: '0' + administrator: '0' + placeholder: '' + is_grouped: false + style: + type: default + row: + type: 'entity:node' + query: + type: views_query + options: + query_comment: '' + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: 0 + display_options: + path: test_exposed_form_required_text_filter diff --git a/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php b/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php index b653c352795c..6a28d030995c 100644 --- a/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php +++ b/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php @@ -6,9 +6,9 @@ use Drupal\entity_test\Entity\EntityTest; use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait; use Drupal\Tests\views\Functional\ViewTestBase; +use Drupal\views\Entity\View; use Drupal\views\ViewExecutable; use Drupal\views\Views; -use Drupal\views\Entity\View; /** * Tests exposed forms functionality. @@ -24,7 +24,7 @@ class ExposedFormTest extends ViewTestBase { * * @var array */ - public static $testViews = ['test_exposed_form_buttons', 'test_exposed_block', 'test_exposed_form_sort_items_per_page', 'test_exposed_form_pager', 'test_remember_selected']; + public static $testViews = ['test_exposed_form_buttons', 'test_exposed_form_required_text_filter', 'test_exposed_block', 'test_exposed_form_sort_items_per_page', 'test_exposed_form_pager', 'test_remember_selected']; /** * Modules to enable. @@ -344,6 +344,23 @@ public function testTextInputRequired() { $this->assertSession()->pageTextNotContains($on_demand_text); } + /** + * Tests the input required exposed form type with a text type filter. + */ + public function testInputRequiredTextFilter() { + $this->drupalGet('test_exposed_form_required_text_filter'); + $this->assertSession()->statusCodeEquals(200); + $this->helperButtonHasLabel('edit-submit-test-exposed-form-required-text-filter', 'Apply'); + + // Ensure that no results are displayed by default when no input is + // provided. + $this->assertSession()->elementNotExists('xpath', "//div[contains(@class, 'views-row')]"); + + // Ensure that no error element is shown. + $this->assertSession()->elementNotExists('css', '.messages--error'); + $this->assertFalse($this->getSession()->getPage()->findField('title')->hasClass('error')); + } + /** * Tests exposed forms with exposed sort and items per page. */ -- GitLab From 874c9766ec79d269a2cc411a23bad2f0954c1929 Mon Sep 17 00:00:00 2001 From: Nathan Rogers Date: Tue, 8 Aug 2023 00:08:59 -0600 Subject: [PATCH 3/4] Empty exposed required filters error fix --- .../exposed_form/ExposedFormPluginBase.php | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php index 18308f57d227..157d2b81aebf 100644 --- a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php +++ b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php @@ -246,6 +246,31 @@ public function exposedFormAlter(&$form, FormStateInterface $form_state) { } } + // Check if there are exposed filters for this view + $exposed_filters = []; + $exposed_required_filters = []; + foreach ($this->view->filter as $id => $handler) { + if ($handler->canExpose() && $handler->isExposed() && !empty($handler->options['expose']['identifier'])) { + if ($handler->options['expose']['required']) { + $exposed_required_filters[$handler->options['expose']['identifier']] = $id; + } + $exposed_filters[$handler->options['expose']['identifier']] = $id; + } + } + + // If any required exposed filters loop through them to see if they + // have any input. If not don't auto process the form to prevent validation. + if (!empty($exposed_required_filters)) { + $form_values = $form_state->getUserInput(); + foreach ($exposed_required_filters as $key => $required_filter) { + if (!$form_values || empty($form_values[$key])) { + $form_state->setAlwaysProcess(FALSE); + break; + } + } + } + $all_exposed = array_merge($exposed_sorts, $exposed_filters); + if (!empty($this->options['reset_button'])) { $form['actions']['reset'] = [ '#value' => $this->options['reset_button_label'], @@ -253,34 +278,6 @@ public function exposedFormAlter(&$form, FormStateInterface $form_state) { '#weight' => 10, ]; - // Get an array of exposed filters and exposed required text filters, - // keyed by identifier option. - $exposed_filters = []; - $text_filter_plugin_ids = ['string', 'combine']; - $exposed_required_text_filters = []; - foreach ($this->view->filter as $id => $handler) { - if ($handler->canExpose() && $handler->isExposed() && !empty($handler->options['expose']['identifier'])) { - if ($handler->options['expose']['required'] && in_array($handler->options['plugin_id'], $text_filter_plugin_ids)) { - $exposed_required_text_filters[$handler->options['expose']['identifier']] = $id; - } - $exposed_filters[$handler->options['expose']['identifier']] = $id; - } - } - - // If any required exposed text filters loop through them to see if they - // have any input. If not don't auto process the form. - if (!empty($exposed_required_text_filters)) { - // Prevent form validation if exposed text filter is not set. - $exposed_input = $this->view->getExposedInput(); - foreach ($exposed_required_text_filters as $key => $required_filter) { - if (!$exposed_input || empty($exposed_input[$key])) { - $form_state->setAlwaysProcess(FALSE); - break; - } - } - } - $all_exposed = array_merge($exposed_sorts, $exposed_filters); - // Set the access to FALSE if there is no exposed input. if (!array_intersect_key($all_exposed, $this->view->getExposedInput())) { $form['actions']['reset']['#access'] = FALSE; -- GitLab From fa0ed382a2eb8300056adfbba554f16e3600ee8c Mon Sep 17 00:00:00 2001 From: Nathan Rogers Date: Tue, 8 Aug 2023 11:07:04 -0600 Subject: [PATCH 4/4] Fixed boolean plugin id failing --- .../src/Plugin/views/exposed_form/ExposedFormPluginBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php index 157d2b81aebf..8384c975895a 100644 --- a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php +++ b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php @@ -251,7 +251,7 @@ public function exposedFormAlter(&$form, FormStateInterface $form_state) { $exposed_required_filters = []; foreach ($this->view->filter as $id => $handler) { if ($handler->canExpose() && $handler->isExposed() && !empty($handler->options['expose']['identifier'])) { - if ($handler->options['expose']['required']) { + if ($handler->options['expose']['required'] && $handler->options['plugin_id'] !== 'boolean') { $exposed_required_filters[$handler->options['expose']['identifier']] = $id; } $exposed_filters[$handler->options['expose']['identifier']] = $id; -- GitLab