diff --git a/composer.json b/composer.json index aabb08e27f9d9f6b236d25ceee1f9c3347a59129..8a4067a9f33eb7f23ba76c834e2e9ed26b355906 100644 --- a/composer.json +++ b/composer.json @@ -176,7 +176,7 @@ "drupal/view_unpublished": "1.0-rc1", "drupal/views_accordion": "1.1", "drupal/views_ajax_history": "1.2", - "drupal/views_autocomplete_filters": "1.2", + "drupal/views_autocomplete_filters": "1.3", "drupal/views_bootstrap": "3.1", "drupal/views_bulk_operations": "3.4", "drupal/views_fieldsets": "3.3", diff --git a/composer.lock b/composer.lock index cd907bc32a4bb92cbebe1f47f5fedb9224a235a6..82b9c389d406332b2bc263a69b47bb848d4245f0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7d3787aaafd1ba3db8d0785fee363f8b", + "content-hash": "124cbcf646e3115ddc38b504be3ec372", "packages": [ { "name": "alchemy/zippy", @@ -8100,29 +8100,26 @@ }, { "name": "drupal/views_autocomplete_filters", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://git.drupalcode.org/project/views_autocomplete_filters.git", - "reference": "8.x-1.2" + "reference": "8.x-1.3" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/views_autocomplete_filters-8.x-1.2.zip", - "reference": "8.x-1.2", - "shasum": "bab27febe29aa69eff5c6b3ce3125e49685a6346" + "url": "https://ftp.drupal.org/files/projects/views_autocomplete_filters-8.x-1.3.zip", + "reference": "8.x-1.3", + "shasum": "55762182e55c70f117d5edb8692049e0881ec4ce" }, "require": { - "drupal/core": "*" + "drupal/core": "^8 || ^9" }, "type": "drupal-module", "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - }, "drupal": { - "version": "8.x-1.2", - "datestamp": "1500109743", + "version": "8.x-1.3", + "datestamp": "1587146330", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" @@ -8138,15 +8135,24 @@ "name": "RobLoach", "homepage": "https://www.drupal.org/user/61114" }, + { + "name": "colan", + "homepage": "https://www.drupal.org/user/58704" + }, { "name": "vasike", "homepage": "https://www.drupal.org/user/156237" } ], - "description": "Use Autocomplete for string field filters.", + "description": "Add autocomplete functionality to the views filter text fields.", "homepage": "https://www.drupal.org/project/views_autocomplete_filters", + "keywords": [ + "Drupal", + "views_autocomplete_filters" + ], "support": { - "source": "https://git.drupalcode.org/project/views_autocomplete_filters" + "source": "https://git.drupalcode.org/project/views_autocomplete_filters", + "issues": "https://www.drupal.org/project/issues/views_autocomplete_filters" } }, { diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 9d55d91d2dfba5113562356562d6dbe081fbdd73..ca52c19783e7d25eb9cd1d9be4f706b6c53650e3 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -8352,30 +8352,27 @@ }, { "name": "drupal/views_autocomplete_filters", - "version": "1.2.0", - "version_normalized": "1.2.0.0", + "version": "1.3.0", + "version_normalized": "1.3.0.0", "source": { "type": "git", "url": "https://git.drupalcode.org/project/views_autocomplete_filters.git", - "reference": "8.x-1.2" + "reference": "8.x-1.3" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/views_autocomplete_filters-8.x-1.2.zip", - "reference": "8.x-1.2", - "shasum": "bab27febe29aa69eff5c6b3ce3125e49685a6346" + "url": "https://ftp.drupal.org/files/projects/views_autocomplete_filters-8.x-1.3.zip", + "reference": "8.x-1.3", + "shasum": "55762182e55c70f117d5edb8692049e0881ec4ce" }, "require": { - "drupal/core": "*" + "drupal/core": "^8 || ^9" }, "type": "drupal-module", "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - }, "drupal": { - "version": "8.x-1.2", - "datestamp": "1500109743", + "version": "8.x-1.3", + "datestamp": "1587146330", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" @@ -8392,15 +8389,24 @@ "name": "RobLoach", "homepage": "https://www.drupal.org/user/61114" }, + { + "name": "colan", + "homepage": "https://www.drupal.org/user/58704" + }, { "name": "vasike", "homepage": "https://www.drupal.org/user/156237" } ], - "description": "Use Autocomplete for string field filters.", + "description": "Add autocomplete functionality to the views filter text fields.", "homepage": "https://www.drupal.org/project/views_autocomplete_filters", + "keywords": [ + "Drupal", + "views_autocomplete_filters" + ], "support": { - "source": "https://git.drupalcode.org/project/views_autocomplete_filters" + "source": "https://git.drupalcode.org/project/views_autocomplete_filters", + "issues": "https://www.drupal.org/project/issues/views_autocomplete_filters" } }, { diff --git a/web/modules/views_autocomplete_filters/composer.json b/web/modules/views_autocomplete_filters/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..381cb35c1fbfffbe65c099369af8b2becba25bbe --- /dev/null +++ b/web/modules/views_autocomplete_filters/composer.json @@ -0,0 +1,13 @@ +{ + "name": "drupal/views_autocomplete_filters", + "description": "Add autocomplete functionality to the views filter text fields.", + "type": "drupal-module", + "keywords": ["Drupal", "views_autocomplete_filters"], + "homepage": "https://www.drupal.org/project/views_autocomplete_filters", + "support": { + "issues": "https://www.drupal.org/project/issues/views_autocomplete_filters" + }, + "require": { + "drupal/core": "^8 || ^9" + } +} diff --git a/web/modules/views_autocomplete_filters/js/views-autocomplete-filters-dependent.js b/web/modules/views_autocomplete_filters/js/views-autocomplete-filters-dependent.js index 9e81b71b4cbc4d0c551a766ddc732b75f70c315c..79fb745b48850730f39b319a3201e4dfe3d128e1 100644 --- a/web/modules/views_autocomplete_filters/js/views-autocomplete-filters-dependent.js +++ b/web/modules/views_autocomplete_filters/js/views-autocomplete-filters-dependent.js @@ -149,7 +149,13 @@ if (Drupal.isDependent(this.element)) { var a = Drupal.serializeOuterForm(this.element); $.each(a, function (key, value) { - data_string.data[value['name']] = value['value']; + // We should have an array of values for element with multi values. + if (value['name'].indexOf('[]') > -1) { + if (!data_string.data[value['name']]) data_string.data[value['name']] = [] + data_string.data[value['name']].push(value['value']); + } else { + data_string.data[value['name']] = value['value']; + } }); } var options = $.extend(data_string, autocomplete.ajax); diff --git a/web/modules/views_autocomplete_filters/src/Controller/ViewsAutocompleteFiltersController.php b/web/modules/views_autocomplete_filters/src/Controller/ViewsAutocompleteFiltersController.php index 5d7fc54da2f7ee25d9e5c7de1a2bbbf710dddb13..5458b6c7cddb427e70652fefcc095aaeec9cc154 100644 --- a/web/modules/views_autocomplete_filters/src/Controller/ViewsAutocompleteFiltersController.php +++ b/web/modules/views_autocomplete_filters/src/Controller/ViewsAutocompleteFiltersController.php @@ -4,8 +4,11 @@ use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Unicode; +use Drupal\Component\Utility\Xss; use Drupal\Core\Access\AccessResult; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\views\Plugin\views\display\DisplayPluginBase; +use Drupal\views\ViewExecutable; use Drupal\views\Views; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -165,15 +168,9 @@ public function autocomplete(Request $request, $view_name, $view_display, $filte return new JsonResponse($matches); } } + // Collect exposed filter values and set them to the view. - if (!empty($expose_options['autocomplete_dependent'])) { - $exposed_input = $view->getExposedInput() ; - } - else { - $exposed_input = []; - } - $exposed_input[$expose_options['identifier']] = $string; - $view->setExposedInput($exposed_input); + $view->setExposedInput($this->getExposedInput($view, $request, $expose_options)); // Disable cache for view, because caching autocomplete is a waste of time and memory. $display_handler->setOption('cache', ['type' => 'none']); @@ -207,11 +204,11 @@ public function autocomplete(Request $request, $view_name, $view_display, $filte $view->row_index = 0; foreach ($view->result as $index => $row) { $view->row_index = $index; - $rendered_field = $raw_field = ''; /** @var \Drupal\views\Plugin\views\style\StylePluginBase $style_plugin */ $style_plugin = $display_handler->getPlugin('style'); foreach ($field_names as $field_name) { + $rendered_field = $raw_field = ''; // Render field only if suggestion or dropdown item not in RAW format. if (!$use_raw_suggestion || !$use_raw_dropdown) { $rendered_field = $style_plugin->getField($index, $field_name); @@ -231,21 +228,28 @@ public function autocomplete(Request $request, $view_name, $view_display, $filte } } - if (empty($raw_field)) { + if (empty($raw_field) && !empty($rendered_field)) { $raw_field = [['value' => $rendered_field]]; } - foreach ($raw_field as $delta => $item) { - if (isset($item['value']) && strstr(Unicode::strtolower($item['value']), Unicode::strtolower($string))) { - $dropdown = $use_raw_dropdown ? Html::escape($item['value']) : $rendered_field; - if ($dropdown != '') { - $suggestion = $use_raw_suggestion ? Html::escape($item['value']) : $rendered_field; - $suggestion = Html::decodeEntities($suggestion); + if (is_array($raw_field)) { + foreach ($raw_field as $delta => $item) { + if (isset($item['value']) && strstr(mb_strtolower($item['value']), mb_strtolower($string))) { + $dropdown = $use_raw_dropdown ? Html::escape($item['value']) : $rendered_field; + if ($dropdown != '') { + if ($use_raw_suggestion) { + $suggestion = Unicode::truncate(Html::escape($item['value']), 128); + } + else { + $suggestion = Unicode::truncate(Xss::filter($rendered_field, []), 128); + } + $suggestion = Html::decodeEntities($suggestion); - // Add a class wrapper for a few required CSS overrides. - $matches[] = [ - 'value' => $suggestion, - 'label' => $dropdown, - ]; + // Add a class wrapper for a few required CSS overrides. + $matches[] = [ + 'value' => $suggestion, + 'label' => $dropdown, + ]; + } } } } @@ -264,4 +268,41 @@ public function autocomplete(Request $request, $view_name, $view_display, $filte return new JsonResponse($matches); } + /** + * Collect exposed filter values for setting them to the view. + * + * @param \Drupal\views\ViewExecutable $view + * The view. + * @param \Symfony\Component\HttpFoundation\Request $request + * The request. + * @param array $expose_options + * The options for the exposed filter. + * + * @return array|string[] + * The exposed input. + */ + protected function getExposedInput(ViewExecutable $view, Request $request, array $expose_options) { + $display_handler = $view->display_handler; + $filters = $display_handler->getOption('filters'); + + if (!empty($expose_options['autocomplete_dependent'])) { + $exposed_input = $view->getExposedInput(); + } + else { + $exposed_input = []; + // Need to reset the default values for exposed filters. + foreach ($display_handler->getOption('filters') as $name => $filter) { + if (!empty($filters[$name]['exposed'])) { + if (!empty($filter['is_grouped'])) { + $filters[$name]['group_info']['default_group'] = 'All'; + } + $filters[$name]['value'] = []; + } + } + $display_handler->setOption('filters', $filters); + } + $exposed_input[$expose_options['identifier']] = $request->query->get('q'); + return $exposed_input; + } + } diff --git a/web/modules/views_autocomplete_filters/src/Plugin/views/filter/ViewsAutocompleteFiltersTrait.php b/web/modules/views_autocomplete_filters/src/Plugin/views/filter/ViewsAutocompleteFiltersTrait.php index f737d3e3e73825e878c9dd2c3f41f0bc5fea8d28..8888157c10ce52532cad02b08183b1944563e1c7 100644 --- a/web/modules/views_autocomplete_filters/src/Plugin/views/filter/ViewsAutocompleteFiltersTrait.php +++ b/web/modules/views_autocomplete_filters/src/Plugin/views/filter/ViewsAutocompleteFiltersTrait.php @@ -36,83 +36,105 @@ public function defineOptions() { public function buildOptionsForm(&$form, FormStateInterface $form_state) { parent::buildOptionsForm($form, $form_state); - if ($this->canExpose() && !empty($form['expose'])) { - $field_options_all = $this->view->display_handler->getFieldLabels(); - // Limit options to fields with the same name. - /** @var \Drupal\views\Plugin\views\field\FieldHandlerInterface $handler */ - foreach ($this->view->display_handler->getHandlers('field') as $id => $handler) { - if ($handler->field == $this->realField) { - $field_options[$id] = $field_options_all[$id]; - } - } - if (empty($field_options)) { - $field_options[''] = $this->t('Add some fields to view'); - } - elseif (empty($this->options['expose']['autocomplete_field']) && !empty($field_options[$this->options['id']])) { - $this->options['expose']['autocomplete_field'] = $this->options['id']; - } + if (!$this->canExpose() || empty($form['expose'])) { + return; + } + + $field_options = $this->getFieldOptions(); + if (empty($this->options['expose']['autocomplete_field']) && !empty($field_options[$this->options['id']])) { + $this->options['expose']['autocomplete_field'] = $this->options['id']; + } + + // Build form elements for the right side of the exposed filter form. + $states = [ + 'visible' => [' + :input[name="options[expose][autocomplete_filter]"]' => ['checked' => TRUE], + ], + ]; + $form['expose'] += [ + 'autocomplete_filter' => [ + '#type' => 'checkbox', + '#title' => $this->t('Use Autocomplete'), + '#default_value' => $this->options['expose']['autocomplete_filter'], + '#description' => $this->t('Use Autocomplete for this filter.'), + ], + 'autocomplete_items' => [ + '#type' => 'number', + '#title' => $this->t('Maximum number of items in Autocomplete'), + '#default_value' => $this->options['expose']['autocomplete_items'], + '#description' => $this->t('Enter 0 for no limit.'), + '#min' => 0, + '#states' => $states, + ], + 'autocomplete_min_chars' => [ + '#type' => 'number', + '#title' => $this->t('Minimum number of characters to start filter'), + '#default_value' => $this->options['expose']['autocomplete_min_chars'], + '#min' => 0, + '#states' => $states, + ], + 'autocomplete_dependent' => [ + '#type' => 'checkbox', + '#title' => $this->t('Suggestions depend on other filter fields'), + '#default_value' => $this->options['expose']['autocomplete_dependent'], + '#description' => $this->t('Autocomplete suggestions will be filtered by other filter fields'), + '#states' => $states, + ], + 'autocomplete_field' => [ + '#type' => 'select', + '#title' => $this->t('Field with autocomplete results'), + '#default_value' => $this->options['expose']['autocomplete_field'], + '#options' => $field_options, + '#description' => $this->t('Selected field will be used for dropdown results of autocomplete. In most cases it should be the same field you use for filter.'), + '#states' => $states, + ], + 'autocomplete_raw_dropdown' => [ + '#type' => 'checkbox', + '#title' => $this->t('Unformatted dropdown'), + '#default_value' => $this->options['expose']['autocomplete_raw_dropdown'], + '#description' => $this->t('Use unformatted data from database for dropdown list instead of field formatter result. Value will be printed as plain text.'), + '#states' => $states, + ], + 'autocomplete_raw_suggestion' => [ + '#type' => 'checkbox', + '#title' => $this->t('Unformatted suggestion'), + '#default_value' => $this->options['expose']['autocomplete_raw_suggestion'], + '#description' => $this->t('The same as above, but for suggestion (text appearing inside textfield when item is selected).'), + '#states' => $states, + ], + ]; + if (!$this->hasAutocompleteFieldSelector()) { + unset($form['expose']['autocomplete_field']); + } + } - // Build form elements for the right side of the exposed filter form. - $states = [ - 'visible' => [' - :input[name="options[expose][autocomplete_filter]"]' => ['checked' => TRUE], - ], - ]; - $form['expose'] += [ - 'autocomplete_filter' => [ - '#type' => 'checkbox', - '#title' => $this->t('Use Autocomplete'), - '#default_value' => $this->options['expose']['autocomplete_filter'], - '#description' => $this->t('Use Autocomplete for this filter.'), - ], - 'autocomplete_items' => [ - '#type' => 'textfield', - '#title' => $this->t('Maximum number of items in Autocomplete'), - '#default_value' => $this->options['expose']['autocomplete_items'], - '#description' => $this->t('Enter 0 for no limit.'), - '#states' => $states, - ], - 'autocomplete_min_chars' => [ - '#type' => 'textfield', - '#title' => t('Minimum number of characters to start filter'), - '#default_value' => $this->options['expose']['autocomplete_min_chars'], - '#element_validate' => ['element_validate_integer'], - '#states' => $states, - ], - 'autocomplete_dependent' => [ - '#type' => 'checkbox', - '#title' => $this->t('Suggestions depend on other filter fields'), - '#default_value' => $this->options['expose']['autocomplete_dependent'], - '#description' => $this->t('Autocomplete suggestions will be filtered by other filter fields'), - '#states' => $states, - ], - 'autocomplete_field' => [ - '#type' => 'select', - '#title' => $this->t('Field with autocomplete results'), - '#default_value' => $this->options['expose']['autocomplete_field'], - '#options' => $field_options, - '#description' => $this->t('Selected field will be used for dropdown results of autocomplete. In most cases it should be the same field you use for filter.'), - '#states' => $states, - ], - 'autocomplete_raw_dropdown' => [ - '#type' => 'checkbox', - '#title' => $this->t('Unformatted dropdown'), - '#default_value' => $this->options['expose']['autocomplete_raw_dropdown'], - '#description' => $this->t('Use unformatted data from database for dropdown list instead of field formatter result. Value will be printed as plain text.'), - '#states' => $states, - ], - 'autocomplete_raw_suggestion' => [ - '#type' => 'checkbox', - '#title' => $this->t('Unformatted suggestion'), - '#default_value' => $this->options['expose']['autocomplete_raw_suggestion'], - '#description' => $this->t('The same as above, but for suggestion (text appearing inside textfield when item is selected).'), - '#states' => $states, - ], - ]; - if (!$this->hasAutocompleteFieldSelector()) { - unset($form['expose']['autocomplete_field']); + /** + * Fetches the autocomplete field options. + * + * @return array + * The list of options. + */ + protected function getFieldOptions() { + $field_options = []; + + // Limit options to fields with the same name. + /** @var \Drupal\views\Plugin\views\field\FieldHandlerInterface $handler */ + foreach ($this->view->display_handler->getHandlers('field') as $id => $handler) { + if (in_array($this->realField, [ + $handler->field, + $handler->field . '_value', + $handler->realField . '_value', + ])) { + $field_options_all = $this->view->display_handler->getFieldLabels(); + $field_options[$id] = $field_options_all[$id]; } } + + if (empty($field_options)) { + $field_options[''] = $this->t('Add some fields to view'); + } + + return $field_options; } public function valueForm(&$form, FormStateInterface $form_state) { diff --git a/web/modules/views_autocomplete_filters/views_autocomplete_filters.info.yml b/web/modules/views_autocomplete_filters/views_autocomplete_filters.info.yml index 0b096a291a2fb35b2fee950853cdff7df8a7503b..1442c29c7847a00960e9965cb2f18aa25562bb49 100644 --- a/web/modules/views_autocomplete_filters/views_autocomplete_filters.info.yml +++ b/web/modules/views_autocomplete_filters/views_autocomplete_filters.info.yml @@ -2,15 +2,15 @@ name: Views Autocomplete Filters type: module description: 'Use Autocomplete for string field filters.' package: Views -# core: 8.x +core: 8.x +core_version_requirement: ^8 || ^9 dependencies: - - views + - drupal:views tags: - views - utility -# Information added by Drupal.org packaging script on 2017-07-15 -version: '8.x-1.2' -core: '8.x' +# Information added by Drupal.org packaging script on 2020-04-17 +version: '8.x-1.3' project: 'views_autocomplete_filters' -datestamp: 1500109745 +datestamp: 1587146332