Issue #1886018 by dagmar, Munavijayalakshmi, Lendude, Manuel Garcia, abi_cima, alexpott: Make it possible to configure exposed filter operators

merge-requests/1119/head
Lee Rowlands 2019-06-14 06:14:47 +10:00
parent 83fd8cc951
commit 09fdae118d
No known key found for this signature in database
GPG Key ID: 2B829A3DF9204DC4
31 changed files with 1016 additions and 2 deletions

View File

@ -375,6 +375,8 @@ display:
authenticated: authenticated
anonymous: '0'
administrator: '0'
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -416,6 +418,8 @@ display:
anonymous: '0'
administrator: '0'
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -0,0 +1,128 @@
langcode: und
status: true
dependencies:
module:
- datetime
- node
id: test_exposed_filter_datetime
label: test_exposed_filter_datetime
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: '8'
display:
default:
display_options:
access:
type: none
cache:
type: none
exposed_form:
type: basic
fields:
nid:
field: nid
id: nid
table: node_field_data
plugin_id: node
filters:
field_date_value:
id: field_date_value
table: node__field_date
field: field_date_value
relationship: none
group_type: group
admin_label: ''
operator: '='
value:
min: ''
max: ''
value: ''
type: date
group: 1
exposed: true
expose:
operator_id: field_date_value_op
label: 'field_date (field_date)'
description: ''
use_operator: true
operator: field_date_value_op
operator_limit_selection: true
operator_list:
'=': '='
'!=': '!='
identifier: field_date_value
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
anonymous: '0'
administrator: '0'
placeholder: ''
min_placeholder: ''
max_placeholder: ''
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: datetime
sorts:
id:
field: nid
id: nid
order: ASC
relationship: none
table: node_field_data
plugin_id: numeric
pager:
type: full
query:
options:
query_comment: ''
type: views_query
style:
type: default
row:
type: fields
display_extenders: { }
display_plugin: default
display_title: Master
id: default
position: 0
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
tags: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: test_exposed_filter_datetime
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
tags: { }

View File

@ -0,0 +1,120 @@
<?php
namespace Drupal\Tests\datetime\Functional;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
/**
* Test exposed datetime filters functionality.
*
* @group views
* @see \Drupal\datetime\Plugin\views\filter\Date
*/
class DateFilterTest extends ViewTestBase {
/**
* A user with permission to administer views.
*
* @var \Drupal\user\Entity\User
*/
protected $adminUser;
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_exposed_filter_datetime'];
/**
* {@inheritdoc}
*/
public static $modules = [
'datetime_test',
'node',
'datetime',
'field',
'views_ui',
];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['datetime_test']);
// Add a date field to page nodes.
$node_type = NodeType::create([
'type' => 'page',
'name' => 'page',
]);
$node_type->save();
$fieldStorage = FieldStorageConfig::create([
'field_name' => 'field_date',
'entity_type' => 'node',
'type' => 'datetime',
'settings' => ['datetime_type' => DateTimeItem::DATETIME_TYPE_DATETIME],
]);
$fieldStorage->save();
$field = FieldConfig::create([
'field_storage' => $fieldStorage,
'bundle' => 'page',
'required' => TRUE,
]);
$field->save();
$this->adminUser = $this->drupalCreateUser(['administer views']);
$this->drupalLogin($this->adminUser);
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
}
/**
* Tests the limit of the expose operator functionality.
*/
public function testLimitExposedOperators() {
$this->drupalGet('test_exposed_filter_datetime');
$this->assertResponse(200);
$this->assertOption('edit-field-date-value-op', '=');
$this->assertNoOption('edit-field-date-value-op', '>');
$this->assertNoOption('edit-field-date-value-op', '>=');
// Because there are not operators that use the min and max fields, those
// fields should not be in the exposed form.
$this->assertFieldById('edit-field-date-value-value');
$this->assertNoFieldById('edit-field-date-value-min');
$this->assertNoFieldById('edit-field-date-value-max');
$edit = [];
$edit['options[operator]'] = '>';
$edit['options[expose][operator_list][]'] = ['>', '>=', 'between'];
$this->drupalPostForm('admin/structure/views/nojs/handler/test_exposed_filter_datetime/default/filter/field_date_value', $edit, t('Apply'));
$this->drupalPostForm('admin/structure/views/view/test_exposed_filter_datetime/edit/default', [], t('Save'));
$this->drupalGet('test_exposed_filter_datetime');
$this->assertResponse(200);
$this->assertNoOption('edit-field-date-value-op', '<');
$this->assertNoOption('edit-field-date-value-op', '<=');
$this->assertNoOption('edit-field-date-value-op', '=');
$this->assertOption('edit-field-date-value-op', '>');
$this->assertOption('edit-field-date-value-op', '>=');
$this->assertFieldById('edit-field-date-value-value');
$this->assertFieldById('edit-field-date-value-min');
$this->assertFieldById('edit-field-date-value-max');
// Set the default to an excluded operator.
$edit = [];
$edit['options[operator]'] = '=';
$edit['options[expose][operator_list][]'] = ['<', '>'];
$this->drupalPostForm('admin/structure/views/nojs/handler/test_exposed_filter_datetime/default/filter/field_date_value', $edit, t('Apply'));
$this->assertText('You selected the "Is equal to" operator as the default value but is not included in the list of limited operators.');
}
}

View File

@ -582,6 +582,8 @@ display:
anonymous: '0'
administrator: '0'
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -621,6 +623,8 @@ display:
anonymous: '0'
administrator: '0'
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -595,6 +595,8 @@ display:
authenticated: authenticated
anonymous: '0'
administrator: '0'
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -635,6 +637,8 @@ display:
authenticated: authenticated
anonymous: '0'
administrator: '0'
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -676,6 +680,8 @@ display:
anonymous: '0'
administrator: '0'
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -639,6 +639,8 @@ display:
authenticated: authenticated
anonymous: '0'
administrator: '0'
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -680,6 +682,8 @@ display:
anonymous: '0'
administrator: '0'
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -718,6 +722,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: true
group_info:
label: 'Published status'
@ -767,6 +773,8 @@ display:
anonymous: '0'
administrator: '0'
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -212,6 +212,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: true
group_info:
label: Published
@ -260,6 +262,8 @@ display:
authenticated: authenticated
anonymous: '0'
administrator: '0'
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -301,6 +305,8 @@ display:
anonymous: '0'
administrator: '0'
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: 'Media type'
@ -565,6 +571,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -605,6 +613,8 @@ display:
authenticated: authenticated
anonymous: '0'
administrator: '0'
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -808,6 +818,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -848,6 +860,8 @@ display:
authenticated: authenticated
anonymous: '0'
administrator: '0'
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -110,6 +110,8 @@ display:
group: 0
expose:
operator: '0'
operator_limit_selection: false
operator_list: { }
plugin_id: boolean
entity_type: node
entity_field: status
@ -138,6 +140,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -378,6 +378,8 @@ display:
authenticated: authenticated
anonymous: '0'
administrator: '0'
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -419,6 +421,8 @@ display:
anonymous: '0'
administrator: '0'
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -457,6 +461,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: true
group_info:
label: 'Published status'
@ -506,6 +512,8 @@ display:
anonymous: '0'
administrator: '0'
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -530,6 +538,9 @@ display:
plugin_id: node_status
group: 1
entity_type: node
expose:
operator_limit_selection: false
operator_list: { }
sorts: { }
title: Content
empty:

View File

@ -185,6 +185,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -224,6 +226,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -333,6 +333,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -601,6 +601,8 @@ display:
authenticated: authenticated
anonymous: '0'
administrator: '0'
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -642,6 +644,8 @@ display:
authenticated: authenticated
anonymous: '0'
administrator: '0'
operator_limit_selection: false
operator_list: { }
is_grouped: true
group_info:
label: Status
@ -691,6 +695,8 @@ display:
anonymous: '0'
administrator: '0'
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -731,6 +737,8 @@ display:
anonymous: '0'
administrator: '0'
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -768,6 +776,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -809,6 +819,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -95,6 +95,8 @@ display:
id: status
expose:
operator: '0'
operator_limit_selection: false
operator_list: { }
group: 1
plugin_id: boolean
entity_type: user
@ -126,6 +128,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -102,6 +102,8 @@ display:
id: status
expose:
operator: '0'
operator_limit_selection: false
operator_list: { }
group: 1
plugin_id: boolean
entity_type: user
@ -135,6 +137,8 @@ display:
authenticated: authenticated
anonymous: '0'
administrator: '0'
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -701,6 +701,15 @@ views_filter:
operator:
type: string
label: 'Operator'
operator_limit_selection:
type: boolean
label: 'Limit the available operators'
operator_list:
type: sequence
label: 'List of available operators'
sequence:
type: string
label: 'Operator'
identifier:
type: string
label: 'Filter identifier'

View File

@ -128,6 +128,8 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
'description' => ['default' => ''],
'use_operator' => ['default' => FALSE],
'operator' => ['default' => ''],
'operator_limit_selection' => ['default' => FALSE],
'operator_list' => ['default' => []],
'identifier' => ['default' => ''],
'required' => ['default' => FALSE],
'remember' => ['default' => FALSE],
@ -559,6 +561,36 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
'#description' => $this->t('Allow the user to choose the operator.'),
'#default_value' => !empty($this->options['expose']['use_operator']),
];
$operators = $this->operatorOptions();
if (!empty($operators) && count($operators) > 1) {
$form['expose']['operator_limit_selection'] = [
'#type' => 'checkbox',
'#title' => $this->t('Limit the available operators'),
'#description' => $this->t('Limit the available operators to be shown on the exposed filter.'),
'#default_value' => !empty($this->options['expose']['operator_limit_selection']),
'#states' => [
'visible' => [
':input[name="options[expose][use_operator]"]' => ['checked' => TRUE],
],
],
];
$form['expose']['operator_list'] = [
'#type' => 'select',
'#title' => $this->t('Restrict operators to'),
'#default_value' => $this->options['expose']['operator_list'],
'#options' => $operators,
'#multiple' => TRUE,
'#description' => $this->t('Selecting none will make all of them available.'),
'#states' => [
'visible' => [
':input[name="options[expose][operator_limit_selection]"]' => ['checked' => TRUE],
':input[name="options[expose][use_operator]"]' => ['checked' => TRUE],
],
],
];
}
$form['expose']['operator_id'] = [
'#type' => 'textfield',
'#default_value' => $this->options['expose']['operator_id'],
@ -623,6 +655,15 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
public function validateExposeForm($form, FormStateInterface $form_state) {
$identifier = $form_state->getValue(['options', 'expose', 'identifier']);
$this->validateIdentifier($identifier, $form_state, $form['expose']['identifier']);
$limit_operators = $form_state->getValue(['options', 'expose', 'operator_limit_selection']);
$operators_selected = $form_state->getValue(['options', 'expose', 'operator_list']);
$selected_operator = $form_state->getValue(['options', 'operator']);
if ($limit_operators && !in_array($selected_operator, $operators_selected, TRUE)) {
$form_state->setError(
$form['expose']['operator_list'],
$this->t('You selected the "@operator" operator as the default value but is not included in the list of limited operators.', ['@operator' => $this->operatorOptions()[$selected_operator]]));
}
}
/**
@ -763,6 +804,8 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
$this->options['expose'] = [
'use_operator' => FALSE,
'operator' => $this->options['id'] . '_op',
'operator_limit_selection' => FALSE,
'operator_list' => [],
'identifier' => $this->options['id'],
'label' => $this->definition['title'],
'description' => NULL,
@ -848,6 +891,15 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id'])) {
$operator = $this->options['expose']['operator_id'];
$this->operatorForm($form, $form_state);
// Limit the exposed operators if needed.
if (!empty($this->options['expose']['operator_limit_selection']) &&
!empty($this->options['expose']['operator_list'])) {
$options = $this->operatorOptions();
$operator_list = $this->options['expose']['operator_list'];
$form['operator']['#options'] = array_intersect_key($options, $operator_list);
}
$form[$operator] = $form['operator'];
$this->exposedTranslate($form[$operator], 'operator');

View File

@ -258,7 +258,23 @@ class NumericFilter extends FilterPluginBase {
}
}
if ($which == 'all' || $which == 'minmax') {
// Minimum and maximum form fields are associated to some specific operators
// like 'between'. Ensure that min and max fields are only visible if
// the associated operator is not excluded from the operator list.
$two_value_operators_available = ($which == 'all' || $which == 'minmax');
if (!empty($this->options['expose']['operator_limit_selection']) &&
!empty($this->options['expose']['operator_list'])) {
$two_value_operators_available = FALSE;
foreach ($this->options['expose']['operator_list'] as $operator) {
if (in_array($operator, $this->operatorValues(2), TRUE)) {
$two_value_operators_available = TRUE;
break;
}
}
}
if ($two_value_operators_available) {
$form['value']['min'] = [
'#type' => 'textfield',
'#title' => !$exposed ? $this->t('Min') : $this->exposedInfo()['label'],

View File

@ -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([
'collection' => '',
'name' => 'views.view.test_exposed_filters',
'data' => serialize(Yaml::decode(file_get_contents('core/modules/views/tests/fixtures/update/views.view.test_exposed_filters.yml'))),
])
->execute();

View File

@ -0,0 +1,272 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_exposed_filters
label: 'Test Exposed filters'
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
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: null
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:
style:
type: default
row:
type: fields
options:
default_field_elements: true
inline: { }
separator: ''
hide_empty: false
fields:
title:
id: title
table: node_field_data
field: title
entity_type: node
entity_field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
settings:
link_to_entity: true
plugin_id: field
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
click_sort_column: value
type: string
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
filters:
status:
value: '1'
table: node_field_data
field: status
plugin_id: boolean
entity_type: node
entity_field: status
id: status
expose:
operator: ''
group: 1
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
operator: '='
value: ''
group: 1
exposed: true
expose:
operator_id: title_op
label: Title
description: ''
use_operator: true
operator: title_op
identifier: title
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
anonymous: '0'
administrator: '0'
placeholder: ''
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: node
entity_field: title
plugin_id: string
created:
id: created
table: node_field_data
field: created
relationship: none
group_type: group
admin_label: ''
operator: '='
value:
min: ''
max: ''
value: ''
type: date
group: 1
exposed: true
expose:
operator_id: created_op
label: 'Authored on'
description: ''
use_operator: true
operator: created_op
identifier: created
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
anonymous: '0'
administrator: '0'
placeholder: ''
min_placeholder: ''
max_placeholder: ''
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: node
entity_field: created
plugin_id: date
sorts:
created:
id: created
table: node_field_data
field: created
order: DESC
entity_type: node
entity_field: created
plugin_id: date
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
granularity: second
title: 'Test Exposed filters'
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
- user.permissions
tags: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: test-exposed-filters
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
- user.permissions
tags: { }

View File

@ -1,12 +1,15 @@
langcode: en
status: true
dependencies: { }
dependencies:
module:
- node
id: test_filter_in_operator_ui
label: ''
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: '8'
display:
default:
@ -17,6 +20,38 @@ display:
type: tag
exposed_form:
type: basic
fields:
nid:
id: nid
table: node_field_data
field: nid
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: number_integer
settings:
thousand_separator: ''
prefix_suffix: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: node
entity_field: nid
plugin_id: field
filters:
type:
expose:
@ -32,13 +67,89 @@ display:
plugin_id: in_operator
entity_type: node
entity_field: type
nid:
id: nid
table: node_field_data
field: nid
relationship: none
group_type: group
admin_label: ''
operator: '='
value:
min: ''
max: ''
value: ''
group: 1
exposed: true
expose:
operator_id: nid_op
label: ID
description: ''
use_operator: true
operator: nid_op
operator_limit_selection: true
operator_list:
'<': '<'
'<=': '<='
'=': '='
identifier: nid
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
anonymous: '0'
administrator: '0'
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: node
entity_field: nid
plugin_id: numeric
pager:
type: full
style:
type: default
row:
type: fields
display_extenders: { }
display_plugin: default
display_title: Master
id: default
position: 0
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
tags: { }
page_1:
display_options:
path: test_filter_in_operator_ui
display_extenders: { }
display_plugin: page
display_title: Page
id: page_1
position: 0
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
tags: { }

View File

@ -164,4 +164,49 @@ class FilterTest extends ViewTestBase {
$this->assertNoText('An illegal choice has been detected.');
}
/**
* Tests the limit of the expose operator functionality.
*/
public function testLimitExposedOperators() {
$this->drupalGet('test_filter_in_operator_ui');
$this->assertResponse(200);
$this->assertOption('edit-nid-op', '<');
$this->assertOption('edit-nid-op', '<=');
$this->assertOption('edit-nid-op', '=');
$this->assertNoOption('edit-nid-op', '>');
$this->assertNoOption('edit-nid-op', '>=');
// Because there are not operators that use the min and max fields, those
// fields should not be in the exposed form.
$this->assertFieldById('edit-nid-value');
$this->assertNoFieldById('edit-nid-min');
$this->assertNoFieldById('edit-nid-max');
$edit = [];
$edit['options[operator]'] = '>';
$edit['options[expose][operator_list][]'] = ['>', '>=', 'between'];
$this->drupalPostForm('admin/structure/views/nojs/handler/test_filter_in_operator_ui/default/filter/nid', $edit, t('Apply'));
$this->drupalPostForm('admin/structure/views/view/test_filter_in_operator_ui/edit/default', [], t('Save'));
$this->drupalGet('test_filter_in_operator_ui');
$this->assertResponse(200);
$this->assertNoOption('edit-nid-op', '<');
$this->assertNoOption('edit-nid-op', '<=');
$this->assertNoOption('edit-nid-op', '=');
$this->assertOption('edit-nid-op', '>');
$this->assertOption('edit-nid-op', '>=');
$this->assertFieldById('edit-nid-value');
$this->assertFieldById('edit-nid-min');
$this->assertFieldById('edit-nid-max');
// Set the default to an excluded operator.
$edit = [];
$edit['options[operator]'] = '=';
$edit['options[expose][operator_list][]'] = ['<', '>'];
$this->drupalPostForm('admin/structure/views/nojs/handler/test_filter_in_operator_ui/default/filter/nid', $edit, t('Apply'));
$this->assertText('You selected the "Is equal to" operator as the default value but is not included in the list of limited operators.');
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace Drupal\Tests\views\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\views\Entity\View;
/**
* Tests the upgrade path for limit operators feature.
*
* @see views_post_update_limit_operator_defaults()
*
* @group Update
* @group legacy
*/
class LimitOperatorsDefaultsTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../fixtures/update/limit-exposed-operators.php',
];
}
/**
* Tests that default settings for limit operators are present.
*/
public function testViewsPostUpdateLimitOperatorsDefaultValues() {
// Load and initialize our test view.
$view = View::load('test_exposed_filters');
$data = $view->toArray();
// Check that the filters have no defaults values to limit operators.
$title_filter = $data['display']['default']['display_options']['filters']['title']['expose'];
$this->assertArrayNotHasKey('operator_limit_selection', $title_filter);
$this->assertArrayNotHasKey('operator_list', $title_filter);
$created_filter = $data['display']['default']['display_options']['filters']['created']['expose'];
$this->assertArrayNotHasKey('operator_limit_selection', $created_filter);
$this->assertArrayNotHasKey('operator_list', $created_filter);
$this->runUpdates();
// Load and initialize our test view.
$view = View::load('test_exposed_filters');
$data = $view->toArray();
// Check that the filters have defaults values to limit operators.
$title_filter = $data['display']['default']['display_options']['filters']['title']['expose'];
$this->assertIdentical(FALSE, $title_filter['operator_limit_selection']);
$this->assertIdentical([], $title_filter['operator_list']);
$created_filter = $data['display']['default']['display_options']['filters']['created']['expose'];
$this->assertIdentical(FALSE, $created_filter['operator_limit_selection']);
$this->assertIdentical([], $created_filter['operator_list']);
}
}

View File

@ -894,6 +894,18 @@ function views_view_presave(ViewEntityInterface $view) {
}
}
}
if (isset($display['display_options']['filters'])) {
foreach ($display['display_options']['filters'] as $filter_name => &$filter) {
if (!isset($filter['expose']['operator_limit_selection'])) {
$filter['expose']['operator_limit_selection'] = FALSE;
$changed = TRUE;
}
if (!isset($filter['expose']['operator_list'])) {
$filter['expose']['operator_list'] = [];
$changed = TRUE;
}
}
}
}
if ($changed) {
$view->set('display', $displays);

View File

@ -389,3 +389,35 @@ function views_post_update_make_placeholders_translatable() {
// Empty update to cause a cache rebuild to allow placeholder texts to be
// translatable.
}
/**
* Define default values for limit operators settings in all filters.
*/
function views_post_update_limit_operator_defaults(&$sandbox = NULL) {
\Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view', function ($view) {
/** @var \Drupal\views\ViewEntityInterface $view */
$displays = $view->get('display');
$update = FALSE;
foreach ($displays as $display_name => &$display) {
if (!isset($display['display_options']['filters'])) {
continue;
}
foreach ($display['display_options']['filters'] as $filter_name => $filter) {
if (!isset($filter['expose']['operator_limit_selection'])) {
$filter['expose']['operator_limit_selection'] = FALSE;
$update = TRUE;
}
if (!isset($filter['expose']['operator_list'])) {
$filter['expose']['operator_list'] = [];
$update = TRUE;
}
if ($update) {
$view->set("display.$display_name.display_options.filters.$filter_name", $filter);
}
}
}
return $update;
});
}

View File

@ -118,6 +118,8 @@ display:
id: status
expose:
operator: ''
operator_limit_selection: false
operator_list: { }
group: 1
type:
id: type
@ -128,6 +130,9 @@ display:
entity_type: node
entity_field: type
plugin_id: bundle
expose:
operator_limit_selection: false
operator_list: { }
langcode:
id: langcode
table: node_field_data
@ -153,6 +158,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -132,6 +132,8 @@ display:
id: status
expose:
operator: ''
operator_limit_selection: false
operator_list: { }
group: 1
type:
id: type
@ -142,6 +144,9 @@ display:
entity_type: node
entity_field: type
plugin_id: bundle
expose:
operator_limit_selection: false
operator_list: { }
langcode:
id: langcode
table: node_field_data
@ -167,6 +172,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -86,6 +86,8 @@ display:
authenticated: authenticated
required: false
use_operator: false
operator_limit_selection: false
operator_list: { }
exposed: false
field: promote
group: 1
@ -113,6 +115,8 @@ display:
status:
expose:
operator: ''
operator_limit_selection: false
operator_list: { }
field: status
group: 1
id: status
@ -146,6 +150,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -186,6 +192,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -120,6 +120,8 @@ display:
id: status
expose:
operator: ''
operator_limit_selection: false
operator_list: { }
group: 1
promote:
id: promote
@ -144,6 +146,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -185,6 +189,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -273,6 +279,8 @@ display:
id: status
expose:
operator: ''
operator_limit_selection: false
operator_list: { }
group: 1
promote:
id: promote
@ -297,6 +305,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -337,6 +347,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -377,6 +389,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -428,6 +442,8 @@ display:
id: status
expose:
operator: ''
operator_limit_selection: false
operator_list: { }
group: 1
promote:
id: promote
@ -452,6 +468,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -492,6 +510,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -532,6 +552,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -123,6 +123,9 @@ display:
entity_type: taxonomy_term
entity_field: vid
plugin_id: bundle
expose:
operator_limit_selection: false
operator_list: { }
langcode:
id: langcode
table: taxonomy_term_field_data
@ -148,6 +151,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -132,6 +132,8 @@ display:
id: status
expose:
operator: ''
operator_limit_selection: false
operator_list: { }
group: 1
type:
id: type
@ -142,6 +144,9 @@ display:
entity_type: node
entity_field: type
plugin_id: bundle
expose:
operator_limit_selection: false
operator_list: { }
langcode:
id: langcode
table: node_field_data
@ -167,6 +172,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''

View File

@ -159,6 +159,8 @@ display:
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
@ -197,6 +199,8 @@ display:
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''