From 87f04ea95b176f49267aa8365e0b735d0a161574 Mon Sep 17 00:00:00 2001 From: Matt Carmichael Date: Mon, 6 Aug 2018 14:28:00 -0500 Subject: [PATCH 01/59] move has_site_name_property logic to form callback --- search_api_federated_solr.module | 35 +------------------ ...hApiFederatedSolrSearchAppSettingsForm.php | 26 +++++++++++++- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/search_api_federated_solr.module b/search_api_federated_solr.module index 229ff67c1..36c02ee75 100644 --- a/search_api_federated_solr.module +++ b/search_api_federated_solr.module @@ -58,7 +58,7 @@ function search_api_federated_solr_search_api_solr_field_mapping_alter(\Drupal\s if (array_key_exists($key,$singleFieldsMap)) { $fields[$key] = $singleFieldsMap[$key]; } - + // Map all "federated_field" property fields to their single value in solr. $field = $index->getField($key); if (method_exists($field,'getPropertyPath') && 'federated_field' == $field->getPropertyPath()) { @@ -82,36 +82,3 @@ function search_api_federated_solr_form_federated_search_page_block_form_alter(& $form['form_token']['#access'] = FALSE; $form['form_id']['#access'] = FALSE; } - -/** - * Implements hook_form_FORM_ID_alter() for the search_api_federated_solr_search_settings_form form. - * - * Validates whether or not the search app's chosen index has a site_name property - * and alters the search app settings form accordingly. - * - * @see \Drupal\search_api_federated_solr\Form\SearchApiFederatedSolrSearchAppSettingsForm - */ -function search_api_federated_solr_form_search_api_federated_solr_search_app_settings_alter(&$form, FormStateInterface $form_state) { - if ($search_index_id = $form['search_index']['#default_value']) { - $config = \Drupal::configFactory()->getEditable('search_api_federated_solr.search_app.settings'); - $index_config = \Drupal::config('search_api.index.' . $search_index_id); - // Determine if the index has a site name property, which could have been - // added / removed since last form load. - $site_name_property = $index_config->get('field_settings.site_name.configuration.site_name'); - $config->set('index.has_site_name_property', $site_name_property ? true : false); - - // If the index does not have a site name property, reset the search app config options. - if (!$site_name_property) { - $form['site_name_property']['#value'] = ''; - $form['set_search_site']['#default_value'] = 0; - $config->set('facet.site_name.set_default', 0); - } - else { - // If the index does have a site name property, ensure the hidden form field reflects that. - $form['site_name_property']['#value'] = 'true'; - } - - $config->save(); - } - -} diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index 107dd7bc3..714d6ef69 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -47,6 +47,29 @@ public function buildForm(array $form, FormStateInterface $form_state) { $index_options[$search_api_index->id()] = $search_api_index->label(); } + // Validates whether or not the search app's chosen index has a site_name property + // and alters the search app settings form accordingly. + if ($search_index_id = $form['search_index']['#default_value']) { + $index_config = \Drupal::config('search_api.index.' . $search_index_id); + // Determine if the index has a site name property, which could have been + // added / removed since last form load. + $site_name_property = $index_config->get('field_settings.site_name.configuration.site_name'); + $config->set('index.has_site_name_property', $site_name_property ? true : false); + + // If the index does not have a site name property, reset the search app config options. + if (!$site_name_property) { + $site_name_property_value = ''; + $site_name_property_default_value = 0; + $config->set('facet.site_name.set_default', 0); + } + else { + // If the index does have a site name property, ensure the hidden form field reflects that. + $site_name_property_value = 'true'; + } + + $config->save(); + } + $form['path'] = [ '#type' => 'textfield', '#title' => $this->t('Search app path'), @@ -74,7 +97,8 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#attributes' => [ 'id' => ['site-name-property'], ], - '#value' => $config->get('index.has_site_name_property') ? 'true' : '', + '#value' => $site_name_property_value, + '#default_value' => $site_name_property_default_value, ]; $form['search_index_basic_auth'] = [ From 0419a6d02fe504accbe23d3b821515010dbc8416 Mon Sep 17 00:00:00 2001 From: Daniel Montgomery Date: Fri, 4 Jan 2019 12:05:40 -0600 Subject: [PATCH 02/59] PL-31: Set the site name based on the configuration values. The form values had not been set yet, so I used the configuration values which is what the form is doing down the codebase. I also set defaults in case it isn't found. I'm not sure how to test this on the app yet and I reached out for more information. --- src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index f9dff0b01..c5a5ef48b 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -48,9 +48,12 @@ public function buildForm(array $form, FormStateInterface $form_state) { $index_options[$search_api_index->id()] = $search_api_index->label(); } + $site_name_property_value = ''; + $site_name_property_default_value = ''; + // Validates whether or not the search app's chosen index has a site_name property // and alters the search app settings form accordingly. - if ($search_index_id = $form['search_index']['#default_value']) { + if ($search_index_id = $config->get('index.id')) { $index_config = \Drupal::config('search_api.index.' . $search_index_id); // Determine if the index has a site name property, which could have been // added / removed since last form load. @@ -66,6 +69,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { else { // If the index does have a site name property, ensure the hidden form field reflects that. $site_name_property_value = 'true'; + $site_name_property_default_value = 'true'; } $config->save(); From f8609ffd0046af3c199632a4095a1dc53bcf71cb Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 5 Mar 2019 10:04:49 -0500 Subject: [PATCH 03/59] root-198: add autocomplete config fields to search app settings page --- ...hApiFederatedSolrSearchAppSettingsForm.php | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index e99a83fb1..f9362e3b4 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -159,6 +159,105 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The max number of numbered pagination buttons to show at a given time. (Default: 5)'), ]; + /* + * // OPTIONAL: defaults to false + * autocomplete.isEnabled + * // REQUIRED + * autocomplete.url + * // OPTIONAL: defaults to false, whether or not to append wildcard to query term + * autocomplete.appendWildcard + * // OPTIONAL: defaults to 5, max number of results which should be returned + * autocomplete.suggestionRows + * // OPTIONAL: defaults to 2, number of characters *after* which autocomplete results should appear + * autocomplete.numChars + * // REQUIRED: show search-as-you-type results ('result', default) or search term ('term') suggestions + * autocomplete.mode + * // OPTIONAL: default set + * autocomplete.[mode].titleText + * // OPTIONAL: defaults to false + * autocomplete.[mode].showDirectionsText + */ + + $form['autocomplete_is_enabled'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Enable autocomplete for the search results page search form'), + '#default_value' => $config->get('autocomplete.isEnabled'), + '#description' => $this + ->t('Checking this will expose more configuration options for autocomplete behavior for the search form on the Search Results page.'), + ]; + + $form['autocomplete'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Search results page > search form: autocompletion'), + '#description' => $this->t('These options apply to the autocomplete functionality on the search for which appears above the search results on the search results page. Configure your placed Federated Search Page Form block to add autocomplete to that form.') + ]; + + $form['autocomplete']['url'] = [ + '#type' => 'url', + '#title' => $this->t('Endpoint URL'), + '#default_value' => $config->get('autocomplete.url'), + '#maxlength' => 2048, + '#size' => 50, + '#description' => $this + ->t('The URL where requests for autocomplete queries should be made. Defaults to the url of the select Request Handler on the server of the selected Search API index.
Supports absolute url pattern to any endpoint which returns the expected autocomplete result structure.'), + ]; + + $form['autocomplete']['is_append_wildcard'] = [ + '#type' => 'checkbox', + '#title' => '' . $this->t('Append a wildcard \'*\' to support partial text search') . '', + '#default_value' => $config->get('autocomplete.isAppendWildcard'), + '#description' => $this + ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), + ]; + + $form['autocomplete']['suggestion_rows'] = [ + '#type' => 'number', + '#title' => $this->t('Number of results'), + '#default_value' => $config->get('autocomplete.suggestionRows'), + '#description' => $this + ->t('The max number of results to render in the autocomplete results dropdown. (Default: 5)'), + ]; + + $form['autocomplete']['num_chars'] = [ + '#type' => 'number', + '#title' => $this->t('Number of characters after which autocomplete query should execute'), + '#default_value' => $config->get('autocomplete.numChars'), + '#description' => $this + ->t('Autocomplete query will be executed after a user types this many characters in the search query field. (Default: 2)'), + ]; + + $autocomplete_mode = $config->get('autocomplete.mode') || false; + + $form['autocomplete']['mode'] = [ + '#type' => 'select', + '#title' => $this->t('Result mode'), + '#description' => $this->t('Type of results the autocomplete response returns: search results (default) or search terms.'), + '#options' => [ + 'result' => $this + ->t('Search results (i.e. search as you type functionality)'), + 'Search terms (coming soon)' => [], + ], + '#default_value' => $autocomplete_mode || 'result', + ]; + + $form['autocomplete']['mode_title_text'] = [ + '#type' => 'textfield', + '#title' => $this->t('Results title text'), + '#size' => 50, + '#default_value' => $autocomplete_mode ? $config->get('autocomplete.' . $autocomplete_mode . '.titleText') : '', + '#description' => $this + ->t('The title text is shown above the results in the autocomplete drop down. (Default: "What are you interested in?" for Search Results mode and "What would you like to search for?" for Search Term mode.)'), + ]; + + $form['autocomplete']['mode_show_directions'] = [ + '#type' => 'checkbox', + '#title' => '' . $this->t('Make keyboard directions visible') . '', + '#default_value' => $config->get('autocomplete.isAppendWildcard'), + '#description' => $this + ->t('Check this box to make the autocomplete keyboard usage directions visible in the results dropdown. This option is recommended for sites that want to maximize their accessibility UX for sighted keyboard users. (Default: false)'), + ]; + + $form['#cache'] = ['max-age' => 0]; return parent::buildForm($form, $form_state); From a8befe49578e0a67956c369a7032e9db373ef898 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 5 Mar 2019 10:33:47 -0500 Subject: [PATCH 04/59] root-198: organize search app settings form in field groups Note: this affects neither their form state value nor config structure --- ...hApiFederatedSolrSearchAppSettingsForm.php | 109 ++++++++++-------- 1 file changed, 64 insertions(+), 45 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index f9362e3b4..513f96648 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -48,15 +48,21 @@ public function buildForm(array $form, FormStateInterface $form_state) { $index_options[$search_api_index->id()] = $search_api_index->label(); } - $form['path'] = [ + $form['setup'] = [ + '#type' => 'details', + '#title' => 'Search Results Page > Set Up', + '#open' => TRUE, + ]; + + $form['setup']['path'] = [ '#type' => 'textfield', - '#title' => $this->t('Search app path'), + '#title' => $this->t('Search results page path'), '#default_value' => $config->get('path'), '#description' => $this ->t('The path for the search app (Default: "/search-app").'), ]; - $form['page_title'] = [ + $form['setup']['page_title'] = [ '#type' => 'textfield', '#title' => $this->t('Search results page title'), '#default_value' => $config->get('page_title'), @@ -64,7 +70,15 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The title that will live in the header tag of the search results page (leave empty to hide completely).'), ]; - $form['search_index'] = [ + $form['setup']['autocomplete_is_enabled'] = [ + '#type' => 'checkbox', + '#title' => '' . $this->t('Enable autocomplete for the search results page search form') . '', + '#default_value' => $config->get('autocomplete.isEnabled'), + '#description' => $this + ->t('Checking this will expose more configuration options for autocomplete behavior for the search form on the Search Results page.'), + ]; + + $form['setup']['search_index'] = [ '#type' => 'select', '#title' => $this->t('Search API index'), '#description' => $this->t('Defines which search_api index and server the search app should use.'), @@ -78,56 +92,39 @@ public function buildForm(array $form, FormStateInterface $form_state) { ], ]; - $form['site_name_property'] = [ - '#type' => 'hidden', - '#attributes' => [ - 'id' => ['site-name-property'], - ], - '#value' => $config->get('index.has_site_name_property') ? 'true' : '', - ]; - - $form['search_index_basic_auth'] = [ + $form['setup']['search_index_basic_auth'] = [ '#type' => 'fieldset', '#title' => $this->t('Search Index Basic Authentication'), '#description' => $this->t('If your Solr server is protected by basic HTTP authentication, enter the login data here. This will be accessible to the client in an obscured, but non-secure method. It should, therefore, only provide read access to the index AND be different from that provided when configuring the server in Search API. The Password field is intentionally not obscured to emphasize this distinction.'), ]; - $form['search_index_basic_auth']['username'] = [ + $form['setup']['search_index_basic_auth']['username'] = [ '#type' => 'textfield', '#title' => $this->t('Username'), '#default_value' => $config->get('index.username'), ]; - $form['search_index_basic_auth']['password'] = [ + $form['setup']['search_index_basic_auth']['password'] = [ '#type' => 'textfield', '#title' => $this->t('Password'), '#default_value' => $config->get('index.password'), ]; - $form['set_search_site'] = [ - '#type' => 'checkbox', - '#title' => $this->t('Set the "Site name" facet to this site'), - '#default_value' => $config->get('facet.site_name.set_default'), - '#description' => $this - ->t('When checked, only search results from this site will be shown, by default, until this site\'s checkbox is unchecked in the search app\'s "Site name" facet.'), - '#states' => [ - 'visible' => [ - ':input[name="site_name_property"]' => [ - 'value' => "true", - ], - ], - ], + $form['search_page_options'] = [ + '#type' => 'details', + '#title' => 'Search Results Page > Options', + '#open' => FALSE, ]; - $form['show_empty_search_results'] = [ + $form['search_page_options']['show_empty_search_results'] = [ '#type' => 'checkbox', - '#title' => $this->t('Show results for empty search'), + '#title' => '' . $this->t('Show results for empty search') . '', '#default_value' => $config->get('content.show_empty_search_results'), '#description' => $this ->t(' When checked, this option allows users to see all results when no search term is entered. By default, empty searches are disabled and yield no results.'), ]; - $form['no_results_text'] = [ + $form['search_page_options']['no_results_text'] = [ '#type' => 'textfield', '#title' => $this->t('No results text'), '#default_value' => $config->get('content.no_results'), @@ -135,7 +132,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('This text is shown when a query returns no results. (Default: "Your search yielded no results.")'), ]; - $form['search_prompt_text'] = [ + $form['search_page_options']['search_prompt_text'] = [ '#type' => 'textfield', '#title' => $this->t('Search prompt text'), '#default_value' => $config->get('content.search_prompt'), @@ -143,7 +140,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('This text is shown when no query term has been entered. (Default: "Please enter a search term.")'), ]; - $form['rows'] = [ + $form['search_page_options']['rows'] = [ '#type' => 'number', '#title' => $this->t('Number of search results per page'), '#default_value' => $config->get('results.rows'), @@ -151,7 +148,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The max number of results to render per search results page. (Default: 20)'), ]; - $form['page_buttons'] = [ + $form['search_page_options']['page_buttons'] = [ '#type' => 'number', '#title' => $this->t('Number of pagination buttons'), '#default_value' => $config->get('pagination.buttons'), @@ -159,6 +156,35 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The max number of numbered pagination buttons to show at a given time. (Default: 5)'), ]; + $form['search_form_values'] = [ + '#type' => 'details', + '#title' => 'Search Results Page > Facets & Filters', + '#open' => FALSE, + ]; + + $form['search_form_values']['site_name_property'] = [ + '#type' => 'hidden', + '#attributes' => [ + 'id' => ['site-name-property'], + ], + '#value' => $config->get('index.has_site_name_property') ? 'true' : '', + ]; + + $form['search_form_values']['set_search_site'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Set the "Site name" facet to this site'), + '#default_value' => $config->get('facet.site_name.set_default'), + '#description' => $this + ->t('When checked, only search results from this site will be shown, by default, until this site\'s checkbox is unchecked in the search app\'s "Site name" facet.'), + '#states' => [ + 'visible' => [ + ':input[name="site_name_property"]' => [ + 'value' => "true", + ], + ], + ], + ]; + /* * // OPTIONAL: defaults to false * autocomplete.isEnabled @@ -178,18 +204,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { * autocomplete.[mode].showDirectionsText */ - $form['autocomplete_is_enabled'] = [ - '#type' => 'checkbox', - '#title' => $this->t('Enable autocomplete for the search results page search form'), - '#default_value' => $config->get('autocomplete.isEnabled'), - '#description' => $this - ->t('Checking this will expose more configuration options for autocomplete behavior for the search form on the Search Results page.'), - ]; - $form['autocomplete'] = [ - '#type' => 'fieldset', - '#title' => $this->t('Search results page > search form: autocompletion'), - '#description' => $this->t('These options apply to the autocomplete functionality on the search for which appears above the search results on the search results page. Configure your placed Federated Search Page Form block to add autocomplete to that form.') + '#type' => 'details', + '#title' => $this->t('Search Results Page > Search Form > Autocomplete'), + '#description' => $this->t('These options apply to the autocomplete functionality on the search for which appears above the search results on the search results page. Configure your placed Federated Search Page Form block to add autocomplete to that form.'), + '#open' => FALSE, ]; $form['autocomplete']['url'] = [ From e154f664de8d658ea5dc0f1d97e16082166ce8a2 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 5 Mar 2019 10:41:30 -0500 Subject: [PATCH 05/59] root-198: expose autocomplete config fields on enable checkbox check --- .../SearchApiFederatedSolrSearchAppSettingsForm.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index 513f96648..ca5f86080 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -75,7 +75,10 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#title' => '' . $this->t('Enable autocomplete for the search results page search form') . '', '#default_value' => $config->get('autocomplete.isEnabled'), '#description' => $this - ->t('Checking this will expose more configuration options for autocomplete behavior for the search form on the Search Results page.'), + ->t('Checking this will expose more configuration options for autocomplete behavior for the search form on the Search Results page at the end of this form.'), + '#attributes' => [ + 'id' => ['autocomplete-is-enabled'], + ], ]; $form['setup']['search_index'] = [ @@ -209,6 +212,13 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#title' => $this->t('Search Results Page > Search Form > Autocomplete'), '#description' => $this->t('These options apply to the autocomplete functionality on the search for which appears above the search results on the search results page. Configure your placed Federated Search Page Form block to add autocomplete to that form.'), '#open' => FALSE, + '#states' => [ + 'visible' => [ + ':input[name="autocomplete_is_enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $form['autocomplete']['url'] = [ From 90ab0d8ff5e3dadc24dd8398b141c603600dbc63 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 5 Mar 2019 14:47:36 -0500 Subject: [PATCH 06/59] root-198: add docs to fieldgroups --- ...hApiFederatedSolrSearchAppSettingsForm.php | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index ca5f86080..700d8fcef 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -48,6 +48,15 @@ public function buildForm(array $form, FormStateInterface $form_state) { $index_options[$search_api_index->id()] = $search_api_index->label(); } + /** + * Basic set up: + * - search results page path + * - search results page title + * - autocomplete enable triggers display of autocopmlete config fieldset + * - serach index to use as datasource, + * - basic auth credentials for index + */ + $form['setup'] = [ '#type' => 'details', '#title' => 'Search Results Page > Set Up', @@ -84,7 +93,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { $form['setup']['search_index'] = [ '#type' => 'select', '#title' => $this->t('Search API index'), - '#description' => $this->t('Defines which search_api index and server the search app should use.'), + '#description' => $this->t('Defines which search_api index and server the search app should use as a datasource.'), '#options' => $index_options, '#default_value' => $config->get('index.id'), '#required' => TRUE, @@ -113,6 +122,16 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $config->get('index.password'), ]; + /** + * Search results page options: + * - show empty search results (i.e. filterable listing page), + * - customize "no results" text + * - custom search prompt + * - renders in result area when show empty results no enabled and no query value + * - max number of search results per page + * - max number of "numbered" pagination buttons to show + */ + $form['search_page_options'] = [ '#type' => 'details', '#title' => 'Search Results Page > Options', @@ -159,6 +178,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The max number of numbered pagination buttons to show at a given time. (Default: 5)'), ]; + /** + * Settings and values for search facets and filters: + * - set the site name facet to the current site name property + */ + $form['search_form_values'] = [ '#type' => 'details', '#title' => 'Search Results Page > Facets & Filters', @@ -188,23 +212,15 @@ public function buildForm(array $form, FormStateInterface $form_state) { ], ]; - /* - * // OPTIONAL: defaults to false - * autocomplete.isEnabled - * // REQUIRED - * autocomplete.url - * // OPTIONAL: defaults to false, whether or not to append wildcard to query term - * autocomplete.appendWildcard - * // OPTIONAL: defaults to 5, max number of results which should be returned - * autocomplete.suggestionRows - * // OPTIONAL: defaults to 2, number of characters *after* which autocomplete results should appear - * autocomplete.numChars - * // REQUIRED: show search-as-you-type results ('result', default) or search term ('term') suggestions - * autocomplete.mode - * // OPTIONAL: default set - * autocomplete.[mode].titleText - * // OPTIONAL: defaults to false - * autocomplete.[mode].showDirectionsText + /** + * Autocomplete settings: + * - endpoint URL + * - use wildcard to support partial terms + * - customize number of autocomplete results + * - number of characters after which autocomplete query should be executed + * - autocomplete results mode (search results, terms) + * - title for autocomplete results + * - show/hide autocomplete keyboard directions */ $form['autocomplete'] = [ From 9ec3130f5024deda8e2fc53d5ca02a7d625d4e39 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 5 Mar 2019 14:48:00 -0500 Subject: [PATCH 07/59] root-198: namespace field names --- .../SearchApiFederatedSolrSearchAppSettingsForm.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index 700d8fcef..202e65bb6 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -237,7 +237,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ], ]; - $form['autocomplete']['url'] = [ + $form['autocomplete']['autocomplete_url'] = [ '#type' => 'url', '#title' => $this->t('Endpoint URL'), '#default_value' => $config->get('autocomplete.url'), @@ -247,7 +247,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The URL where requests for autocomplete queries should be made. Defaults to the url of the select Request Handler on the server of the selected Search API index.
Supports absolute url pattern to any endpoint which returns the expected autocomplete result structure.'), ]; - $form['autocomplete']['is_append_wildcard'] = [ + $form['autocomplete']['autocomplete_is_append_wildcard'] = [ '#type' => 'checkbox', '#title' => '' . $this->t('Append a wildcard \'*\' to support partial text search') . '', '#default_value' => $config->get('autocomplete.isAppendWildcard'), @@ -255,7 +255,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), ]; - $form['autocomplete']['suggestion_rows'] = [ + $form['autocomplete']['autocomplete_suggestion_rows'] = [ '#type' => 'number', '#title' => $this->t('Number of results'), '#default_value' => $config->get('autocomplete.suggestionRows'), @@ -263,7 +263,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The max number of results to render in the autocomplete results dropdown. (Default: 5)'), ]; - $form['autocomplete']['num_chars'] = [ + $form['autocomplete']['autocomplete_num_chars'] = [ '#type' => 'number', '#title' => $this->t('Number of characters after which autocomplete query should execute'), '#default_value' => $config->get('autocomplete.numChars'), @@ -273,7 +273,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { $autocomplete_mode = $config->get('autocomplete.mode') || false; - $form['autocomplete']['mode'] = [ + $form['autocomplete']['autocomplete_mode'] = [ '#type' => 'select', '#title' => $this->t('Result mode'), '#description' => $this->t('Type of results the autocomplete response returns: search results (default) or search terms.'), @@ -285,7 +285,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $autocomplete_mode || 'result', ]; - $form['autocomplete']['mode_title_text'] = [ + $form['autocomplete']['autocomplete_mode_title_text'] = [ '#type' => 'textfield', '#title' => $this->t('Results title text'), '#size' => 50, From 9f6fd75757fef1b00a6c1e4c5b177f8a637856d4 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 5 Mar 2019 14:48:41 -0500 Subject: [PATCH 08/59] root-198: change keyboard directions input logic from show -> hide --- src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index 202e65bb6..51fed9eff 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -294,12 +294,12 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The title text is shown above the results in the autocomplete drop down. (Default: "What are you interested in?" for Search Results mode and "What would you like to search for?" for Search Term mode.)'), ]; - $form['autocomplete']['mode_show_directions'] = [ + $form['autocomplete']['autocomplete_mode_hide_directions'] = [ '#type' => 'checkbox', - '#title' => '' . $this->t('Make keyboard directions visible') . '', - '#default_value' => $config->get('autocomplete.isAppendWildcard'), + '#title' => '' . $this->t('Hide keyboard directions') . '', + '#default_value' => $config->get('autocomplete.hideDirectionsText'), '#description' => $this - ->t('Check this box to make the autocomplete keyboard usage directions visible in the results dropdown. This option is recommended for sites that want to maximize their accessibility UX for sighted keyboard users. (Default: false)'), + ->t('Check this box to make hide the autocomplete keyboard usage directions in the results dropdown. For sites that want to maximize their accessibility UX for sighted keyboard users, we recommend leaving this unchecked. (Default: directions are visible)'), ]; From d93aba89b951f57fe46ebec5bb0328b394e2ba24 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 5 Mar 2019 14:49:07 -0500 Subject: [PATCH 09/59] root-198: save autocomplete form options to config --- ...hApiFederatedSolrSearchAppSettingsForm.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index 51fed9eff..1f2913b5d 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -379,6 +379,30 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // Set the number of pagination buttons. $config->set('pagination.buttons', $form_state->getValue('page_buttons')); + // Set autocomplete options. + $autocomplete_is_enabled = $form_state->getValue('autocomplete_is_enabled'); + $config->set('autocomplete.isEnabled', $form_state->getValue('autocomplete_is_enabled')); + + // If enabled, set the autocomplete options. + if ($autocomplete_is_enabled) { + // Cache form values that we'll use more than once. + $autocomplete_url_value = $form_state->getValue('autocomplete_url'); + $autocomplete_mode = $form_state->getValue('autocomplete_mode'); + + // Set the default autocomplete endpoint url to the default search url if none was passed in. + $autocomplete_url = $autocomplete_url_value ? $autocomplete_url_value : $server_url; + + // Set the actual autocomplete config options. + $config->set('autocomplete.url', $autocomplete_url); + $config->set('autocomplete.appendWildcard', $form_state->getValue('autocomplete_append_wildcard')); + $config->set('autocomplete.suggestionRows', $form_state->getValue('autocomplete_suggestion_rows')); + $config->set('autocomplete.numChars', $form_state->getValue('autocomplete_num_chars')); + if ($autocomplete_mode) { + $config->set('autocomplete.mode', $autocomplete_mode); + $config->set('autocomplete.' . $autocomplete_mode . '.titleText', $form_state->getvalue('autocomplete_mode_title_text')); + $config->set('autocomplete.' . $autocomplete_mode . '.hideDirectionsText', $form_state->getValue('autocomplete_mode_hide_directions')); + } + } $config->save(); if ($rebuild_routes) { From 5a52924383792ff18c638767fcd870c10d4ecbdc Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 5 Mar 2019 14:49:34 -0500 Subject: [PATCH 10/59] root-198: avoid sending encoded empty username:pass --- src/Controller/SearchController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index ddced35a1..d4f7f875a 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -34,7 +34,9 @@ public function searchPage() { * The username and password will be * combined and base64 encoded as per the application. */ - $federated_search_app_config['userpass'] = base64_encode($config->get('index.username') . ':' . $config->get('index.password')); + $username = $config->get('index.username'); + $pass = $config->get('index.password'); + $federated_search_app_config['userpass'] = $username && $pass ? base64_encode($config->get('index.username') . ':' . $config->get('index.password')) : ''; // Validate that there is still a site name property set for this index. $site_name_property = $index_config->get('field_settings.site_name.configuration.site_name'); From 372876484e9a0f64583d2c5510856854cff3dac9 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 5 Mar 2019 14:49:59 -0500 Subject: [PATCH 11/59] root-198: load autocomplete config in search app --- src/Controller/SearchController.php | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index d4f7f875a..d9af3bd83 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -84,6 +84,38 @@ public function searchPage() { $federated_search_app_config['pageTitle'] = $page_title; } + $federated_search_app_config['autocomplete'] = FALSE; + if ($autocomplete_is_enabled = $config->get('autocomplete.isEnabled')) { + // REQUIRED: Autocomplete endpoint, defaults to main search url + if ($autocomplete_url = $config->get('autocomplete.url')) { + $federated_search_app_config['autocomplete']['url'] = $autocomplete_url; + } + // OPTIONAL: defaults to false, whether or not to append wildcard to query term + if ($autocomplete_append_wildcard = $config->get('autocomplete.appendWildcard')) { + $federated_search_app_config['autocomplete']['appendWildcard'] = $autocomplete_append_wildcard; + } + // OPTIONAL: defaults to 5, max number of autocomplete results to return + if ($autocomplete_suggestion_rows = $config->get('autocomplete.suggestionRows')) { + $federated_search_app_config['autocomplete']['suggestionRows'] = $autocomplete_suggestion_rows; + } + // OPTIONAL: defaults to 2, number of characters *after* which autocomplete results should appear + if ($autocomplete_num_chars = $config->get('autocomplete.numChars')) { + $federated_search_app_config['autocomplete']['numChars'] = $autocomplete_num_chars; + } + // REQUIRED: show search-as-you-type results ('result', default) or search term ('term') suggestions + if ($autocomplete_mode = $config->get('autocomplete.mode')) { + $federated_search_app_config['autocomplete']['mode'] = $autocomplete_mode; + // OPTIONAL: default set, title to render above autocomplete results + if ($autocomplete_mode_title_text = $config->get('autocomplete.' . $autocomplete_mode . '.titleText')) { + $federated_search_app_config['autocomplete'][$autocomplete_mode]['titleText'] = $autocomplete_mode_title_text; + } + // OPTIONAL: defaults to false, whether or not to hide the keyboard usage directions text + if ($autocomplete_mode_hide_directions = $config->get('autocomplete.' . $autocomplete_mode . '.hideDirectionsText')) { + $federated_search_app_config['autocomplete'][$autocomplete_mode]['showDirectionsText'] = FALSE; + } + } + } + $element = [ '#theme' => 'search_app', '#federated_search_app_config' => $federated_search_app_config, From e99c894e72087d611b13910887e232d43b1c0584 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Wed, 6 Mar 2019 08:44:45 -0500 Subject: [PATCH 12/59] root-198: add search app config options to hide available facet/filters --- src/Controller/SearchController.php | 30 +++ ...hApiFederatedSolrSearchAppSettingsForm.php | 176 +++++++++++++++++- 2 files changed, 199 insertions(+), 7 deletions(-) diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index d9af3bd83..aa960d85e 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -54,6 +54,36 @@ public function searchPage() { $config->set('facet.site_name.set_default', 0); } + // Create an index property field map array to determine which fields + // exist on the index and should be hidden in the app UI. + $search_fields = [ + "sm_site_name" => [ + "property" => $site_name_property, + "is_hidden" => $config->get('facet.site_name.is_hidden'), + ], + "ss_federated_type" => [ + "property" => $config->get('index.has_federated_type_property'), + "is_hidden" => $config->get('facet.federated_type.is_hidden'), + ], + "ds_federated_date" => [ + "property" => $config->get('index.has_federated_date_property'), + "is_hidden" => $config->get('filter.federated_date.is_hidden'), + ], + "sm_federated_terms" => [ + "property" => $config->get('index.has_federated_terms_property'), + "is_hidden" => $config->get('facet.federated_terms.is_hidden'), + ], + ]; + + // Set hiddenSearchFields to an array of keys of those $search_fields items + // which both exist as an index property and are set to be hidden. + + // OPTIONAL: Machine name of those search fields whose facets/filter and + // current values should be hidden in UI. + $federated_search_app_config['hiddenSearchFields'] = array_keys(array_filter($search_fields, function ($value) { + return $value['property'] && $value['is_hidden']; + })); + // OPTIONAL: The text to display when the app loads with no search term. if ($search_prompt = $config->get('content.search_prompt')) { $federated_search_app_config['searchPrompt'] = $search_prompt; diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index 1f2913b5d..16e957777 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -48,6 +48,47 @@ public function buildForm(array $form, FormStateInterface $form_state) { $index_options[$search_api_index->id()] = $search_api_index->label(); } + /** + * Set index config values to indicate which properties + */ + $site_name_property_value = ''; + $site_name_property_default_value = ''; + // Validates whether or not the search app's chosen index has a site_name, + // federated_date, federated_type, and federated_terms properties + // and alters the search app settings form accordingly. + if ($search_index_id = $config->get('index.id')) { + $index_config = \Drupal::config('search_api.index.' . $search_index_id); + // Determine if the index has a site name property, which could have been + // added / removed since last form load. + $site_name_property = $index_config->get('field_settings.site_name.configuration.site_name'); + $config->set('index.has_site_name_property', $site_name_property ? TRUE : FALSE); + + // If the index does have a site name property, ensure the hidden form field reflects that. + if ($site_name_property) { + $site_name_property_value = 'true'; + $site_name_property_default_value = 'true'; + } + else { + // Assume properties are not present, set defaults. + $site_name_property_value = ''; + $site_name_property_default_value = 0; + $config->set('facet.site_name.set_default', 0); + } + + // Save config indicating which index field properties that + // correspond to facets and filters are present on the index. + $type_property = $index_config->get('field_settings.federated_type'); + $config->set('index.has_federated_type_property', $type_property ? 1 : 0); + + $date_property = $index_config->get('field_settings.federated_date'); + $config->set('index.has_federated_date_property', $date_property ? 1 : 0); + + $terms_property = $index_config->get('field_settings.federated_terms'); + $config->set('index.has_federated_terms_property', $terms_property ? 1 : 0); + + $config->save(); + } + /** * Basic set up: * - search results page path @@ -189,15 +230,56 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#open' => FALSE, ]; + /** + * Set hidden form element value based on presence of field properties on + * the selected index. This value will determine which inputs are + * visible for setting default facet/filter values and hiding in the UI. + */ + $form['search_form_values']['site_name_property'] = [ '#type' => 'hidden', '#attributes' => [ 'id' => ['site-name-property'], ], - '#value' => $config->get('index.has_site_name_property') ? 'true' : '', + '#value' => $site_name_property_value, + '#default_value' => $site_name_property_default_value, + ]; + + $form['search_form_values']['date_property'] = [ + '#type' => 'hidden', + '#attributes' => [ + 'id' => ['date-property'], + ], + '#value' => $config->get('index.has_date_property') ? 'true' : '', + ]; + + $form['search_form_values']['type_property'] = [ + '#type' => 'hidden', + '#attributes' => [ + 'id' => ['type-property'], + ], + '#value' => $config->get('index.has_type_property') ? 'true' : '', + ]; + + $form['search_form_values']['terms_property'] = [ + '#type' => 'hidden', + '#attributes' => [ + 'id' => ['terms-property'], + ], + '#value' => $config->get('index.has_terms_property') ? 'true' : '', + ]; + + /** + * Enable setting of default values for available facets / filter. + * As of now, this includes Site Name only. + */ + + $form['search_form_values']['defaults'] = [ + '#type' => 'fieldset', + '#title' => 'Set facet / filter default values' ]; - $form['search_form_values']['set_search_site'] = [ + $form['search_form_values']['defaults']['set_search_site'] = [ '#type' => 'checkbox', '#title' => $this->t('Set the "Site name" facet to this site'), '#default_value' => $config->get('facet.site_name.set_default'), @@ -212,6 +294,77 @@ public function buildForm(array $form, FormStateInterface $form_state) { ], ]; + /** + * Enable hiding available facets / filters. + * These form elements will only be visible if their corresopnding + * property exists on the index. + */ + $form['search_form_values']['hidden'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Hide facets / filters from sidebar'), + '#description' => $this->t('The checked facets / filters will be hidden from the search app.'), + ]; + + $form['search_form_values']['hidden']['hide_site_name'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Site name facet'), + '#default_value' => $config->get('facet.site_name.is_hidden'), + '#description' => $this + ->t('When checked, the ability to which sites should be included in the results will be hidden.'), + '#states' => [ + 'visible' => [ + ':input[name="site_name_property"]' => [ + 'value' => "true", + ], + ], + ], + ]; + + $form['search_form_values']['hidden']['hide_type'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Type facet'), + '#default_value' => $config->get('facet.federated_type.is_hidden'), + '#description' => $this + ->t('When checked, the ability to select those types (i.e. bundles) which should have results returned will be hidden.'), + '#states' => [ + 'visible' => [ + ':input[name="type_property"]' => [ + 'value' => "true", + ], + ], + ], + ]; + + $form['search_form_values']['hidden']['hide_date'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Date filter'), + '#default_value' => $config->get('filter.federated_date.is_hidden'), + '#description' => $this + ->t('When checked, the ability to filter results by date will be hidden.'), + '#states' => [ + 'visible' => [ + ':input[name="date_property"]' => [ + 'value' => "true", + ], + ], + ], + ]; + + $form['search_form_values']['hidden']['hide_terms'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Terms facet'), + '#default_value' => $config->get('facet.federated_terms.is_hidden'), + '#description' => $this + ->t('When checked, the ability to select those terms which should have results returned will be hidden.'), + '#states' => [ + 'visible' => [ + ':input[name="terms_property"]' => [ + 'value' => "true", + ], + ], + ], + ]; + /** * Autocomplete settings: * - endpoint URL @@ -332,6 +485,19 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $set_search_site = $form_state->getValue('set_search_site'); $config->set('facet.site_name.set_default', $set_search_site); + // Set the search app config settings for hidden filter/facets. + $hide_search_site = $form_state->getValue('hide_site_name'); + $config->set('facet.site_name.is_hidden', $hide_search_site); + + $hide_type = $form_state->getValue('hide_type'); + $config->set('facet.federated_type.is_hidden', $hide_type); + + $hide_terms = $form_state->getValue('hide_terms'); + $config->set('facet.federated_terms.is_hidden', $hide_terms); + + $hide_date = $form_state->getValue('hide_date'); + $config->set('filter.federated_date.is_hidden', $hide_date); + // Set the search app configuration setting for the default search site flag. $show_empty_search_results = $form_state->getValue('show_empty_search_results'); $config->set('content.show_empty_search_results', $show_empty_search_results); @@ -341,12 +507,8 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // Save the selected index option in search app config (for form state). $config->set('index.id', $search_index); - // Get the index configuration object. - $index_config = \Drupal::config('search_api.index.' . $search_index); - $site_name_property = $index_config->get('field_settings.site_name.configuration.site_name'); - $config->set('index.has_site_name_property', $site_name_property ? TRUE : FALSE); - // Get the id of the chosen index's server. + $index_config = \Drupal::config('search_api.index.' . $search_index); $index_server = $index_config->get('server'); // Get the server url. From 19a6d6528fde1625e3405de67f63b780c1b089f1 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Wed, 6 Mar 2019 08:45:15 -0500 Subject: [PATCH 13/59] root-198: update default install config --- ...earch_api_federated_solr.search_app.settings.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/config/install/search_api_federated_solr.search_app.settings.yml b/config/install/search_api_federated_solr.search_app.settings.yml index 9c1c11c91..2a1759270 100644 --- a/config/install/search_api_federated_solr.search_app.settings.yml +++ b/config/install/search_api_federated_solr.search_app.settings.yml @@ -1,8 +1,21 @@ +autocomplete: + is_enabled: false facet: site_name: set_default: false + is_hidden: false + federated_type: + is_hidden: false + federated_terms: + is_hidden: false +filter: + federated_date: + is_hidden: false index: id: '' + has_federated_date_property: false + has_federated_terms_property: false + has_federated_type_property: false has_site_name_property: false server_url: '' username: '' From 6271105d6cf6bc89c26c21b598a66592f5942a3b Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Wed, 6 Mar 2019 09:24:58 -0500 Subject: [PATCH 14/59] root-198: ensure append wildcard config is saved correctly --- src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index 16e957777..62b6adaff 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -403,7 +403,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_is_append_wildcard'] = [ '#type' => 'checkbox', '#title' => '' . $this->t('Append a wildcard \'*\' to support partial text search') . '', - '#default_value' => $config->get('autocomplete.isAppendWildcard'), + '#default_value' => $config->get('autocomplete.appendWildcard'), '#description' => $this ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), ]; @@ -556,7 +556,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // Set the actual autocomplete config options. $config->set('autocomplete.url', $autocomplete_url); - $config->set('autocomplete.appendWildcard', $form_state->getValue('autocomplete_append_wildcard')); + $config->set('autocomplete.appendWildcard', $form_state->getValue('autocomplete_is_append_wildcard')); $config->set('autocomplete.suggestionRows', $form_state->getValue('autocomplete_suggestion_rows')); $config->set('autocomplete.numChars', $form_state->getValue('autocomplete_num_chars')); if ($autocomplete_mode) { From 242a5437a0f7e473e491ad9e0a7f28efe8173703 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Wed, 6 Mar 2019 09:25:27 -0500 Subject: [PATCH 15/59] root-198: ensure autocomplete[mode] config values saved correctly --- ...earchApiFederatedSolrSearchAppSettingsForm.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index 62b6adaff..78beecead 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -424,7 +424,9 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('Autocomplete query will be executed after a user types this many characters in the search query field. (Default: 2)'), ]; - $autocomplete_mode = $config->get('autocomplete.mode') || false; + $autocomplete_mode = $config->get('autocomplete.mode'); + $title_text_config_key = 'autocomplete.' . $autocomplete_mode . '.titleText'; + $hide_directions_text_config_key = 'autocomplete.' . $autocomplete_mode . '.hideDirectionsText'; $form['autocomplete']['autocomplete_mode'] = [ '#type' => 'select', @@ -442,7 +444,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#type' => 'textfield', '#title' => $this->t('Results title text'), '#size' => 50, - '#default_value' => $autocomplete_mode ? $config->get('autocomplete.' . $autocomplete_mode . '.titleText') : '', + '#default_value' => $autocomplete_mode ? $config->get($title_text_config_key) : '', '#description' => $this ->t('The title text is shown above the results in the autocomplete drop down. (Default: "What are you interested in?" for Search Results mode and "What would you like to search for?" for Search Term mode.)'), ]; @@ -450,12 +452,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_mode_hide_directions'] = [ '#type' => 'checkbox', '#title' => '' . $this->t('Hide keyboard directions') . '', - '#default_value' => $config->get('autocomplete.hideDirectionsText'), + '#default_value' => $autocomplete_mode ? $config->get($hide_directions_text_config_key) : 0, '#description' => $this ->t('Check this box to make hide the autocomplete keyboard usage directions in the results dropdown. For sites that want to maximize their accessibility UX for sighted keyboard users, we recommend leaving this unchecked. (Default: directions are visible)'), ]; - $form['#cache'] = ['max-age' => 0]; return parent::buildForm($form, $form_state); @@ -561,8 +562,10 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $config->set('autocomplete.numChars', $form_state->getValue('autocomplete_num_chars')); if ($autocomplete_mode) { $config->set('autocomplete.mode', $autocomplete_mode); - $config->set('autocomplete.' . $autocomplete_mode . '.titleText', $form_state->getvalue('autocomplete_mode_title_text')); - $config->set('autocomplete.' . $autocomplete_mode . '.hideDirectionsText', $form_state->getValue('autocomplete_mode_hide_directions')); + $title_text_config_key = 'autocomplete.' . $autocomplete_mode . '.titleText'; + $config->set($title_text_config_key, $form_state->getvalue('autocomplete_mode_title_text')); + $hide_directions_config_key = 'autocomplete.' . $autocomplete_mode . '.hideDirectionsText'; + $config->set($hide_directions_config_key, $form_state->getValue('autocomplete_mode_hide_directions')); } } $config->save(); From c153bb63e56108c1faee8169727f76a46136d91f Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Wed, 6 Mar 2019 09:25:53 -0500 Subject: [PATCH 16/59] root-198: use boolean TRUE/FALSE instead of 1/0 https://yaml.org/type/bool.html --- .../SearchApiFederatedSolrSearchAppSettingsForm.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index 78beecead..e1e7b3326 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -71,20 +71,20 @@ public function buildForm(array $form, FormStateInterface $form_state) { else { // Assume properties are not present, set defaults. $site_name_property_value = ''; - $site_name_property_default_value = 0; - $config->set('facet.site_name.set_default', 0); + $site_name_property_default_value = FALSE; + $config->set('facet.site_name.set_default', FALSE); } // Save config indicating which index field properties that // correspond to facets and filters are present on the index. $type_property = $index_config->get('field_settings.federated_type'); - $config->set('index.has_federated_type_property', $type_property ? 1 : 0); + $config->set('index.has_federated_type_property', $type_property ? TRUE : FALSE); $date_property = $index_config->get('field_settings.federated_date'); - $config->set('index.has_federated_date_property', $date_property ? 1 : 0); + $config->set('index.has_federated_date_property', $date_property ? TRUE : FALSE); $terms_property = $index_config->get('field_settings.federated_terms'); - $config->set('index.has_federated_terms_property', $terms_property ? 1 : 0); + $config->set('index.has_federated_terms_property', $terms_property ? TRUE : FALSE); $config->save(); } From 9e8f1ac883bfc011ace8825a87cbf4536e93af5f Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Wed, 6 Mar 2019 11:06:26 -0500 Subject: [PATCH 17/59] root-198: fix bug where facet/filter fields don't render when should on search app config --- src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index e1e7b3326..dbf18f039 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -250,7 +250,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#attributes' => [ 'id' => ['date-property'], ], - '#value' => $config->get('index.has_date_property') ? 'true' : '', + '#value' => $config->get('index.has_federated_date_property') ? 'true' : '', ]; $form['search_form_values']['type_property'] = [ @@ -258,7 +258,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#attributes' => [ 'id' => ['type-property'], ], - '#value' => $config->get('index.has_type_property') ? 'true' : '', + '#value' => $config->get('index.has_federated_type_property') ? 'true' : '', ]; $form['search_form_values']['terms_property'] = [ @@ -266,7 +266,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#attributes' => [ 'id' => ['terms-property'], ], - '#value' => $config->get('index.has_terms_property') ? 'true' : '', + '#value' => $config->get('index.has_federated_terms_property') ? 'true' : '', ]; /** From 03abcd8df300d09250e95ae6d5c98c08359f8e62 Mon Sep 17 00:00:00 2001 From: Ken Rickard Date: Wed, 6 Mar 2019 13:24:53 -0500 Subject: [PATCH 18/59] Move the autocomplete checkbox to a more visible location --- ...hApiFederatedSolrSearchAppSettingsForm.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index dbf18f039..b69e3d17d 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -120,17 +120,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The title that will live in the header tag of the search results page (leave empty to hide completely).'), ]; - $form['setup']['autocomplete_is_enabled'] = [ - '#type' => 'checkbox', - '#title' => '' . $this->t('Enable autocomplete for the search results page search form') . '', - '#default_value' => $config->get('autocomplete.isEnabled'), - '#description' => $this - ->t('Checking this will expose more configuration options for autocomplete behavior for the search form on the Search Results page at the end of this form.'), - '#attributes' => [ - 'id' => ['autocomplete-is-enabled'], - ], - ]; - $form['setup']['search_index'] = [ '#type' => 'select', '#title' => $this->t('Search API index'), @@ -163,6 +152,17 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $config->get('index.password'), ]; + $form['setup']['autocomplete_is_enabled'] = [ + '#type' => 'checkbox', + '#title' => '' . $this->t('Enable autocomplete for the search results page search form') . '', + '#default_value' => $config->get('autocomplete.isEnabled'), + '#description' => $this + ->t('Checking this will expose more configuration options for autocomplete behavior for the search form on the Search Results page at the end of this form.'), + '#attributes' => [ + 'id' => ['autocomplete-is-enabled'], + ], + ]; + /** * Search results page options: * - show empty search results (i.e. filterable listing page), From db1a03c6cc9597d447583907695f0899f9e91376 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Fri, 8 Mar 2019 08:30:24 -0500 Subject: [PATCH 19/59] root-198: stub out autocomplete functionality for search block --- ...search_api_federated_solr_autocomplete.css | 83 +++++++ js/search_api_federated_solr_autocomplete.js | 219 ++++++++++++++++++ search_api_federated_solr.libraries.yml | 13 ++ src/Form/FederatedSearchPageBlockForm.php | 3 + 4 files changed, 318 insertions(+) create mode 100644 css/search_api_federated_solr_autocomplete.css create mode 100644 js/search_api_federated_solr_autocomplete.js diff --git a/css/search_api_federated_solr_autocomplete.css b/css/search_api_federated_solr_autocomplete.css new file mode 100644 index 000000000..fffa8a2de --- /dev/null +++ b/css/search_api_federated_solr_autocomplete.css @@ -0,0 +1,83 @@ +#autocomplete-wrapper { + position: relative; +} + +#federated-search-page-block-form .search-autocomplete-container { + position: absolute; + border: 1px solid #999; + background: #fff; + z-index: 1000; + width: 100%; + display: block; +} + +#federated-search-page-block-form .search-autocomplete-container:not(.visually-hidden) .search-autocomplete-container__title { + font-size:.8em; + line-height:1.46667em; + position: relative; + font-weight: bold; + padding: 10px 20px; + border-bottom: 1px dashed #ccc; + display: block; +} +.search-autocomplete-container:not(.visually-hidden) .search-autocomplete-container__close-button { + font-size:.8em; + line-height:1.46667em; + font-style:normal; + padding:3px 7px; + border-color:#ccc; + color:#333; + cursor:pointer; + position:absolute; + right:5px +} + +.search-autocomplete-container:not(.visually-hidden) .search-autocomplete-container__close-button:hover { + border-color:#b5b5b5; + background-color:#f6f6f6 +} + +#federated-search-page-block-form .search-autocomplete-container:not(.visually-hidden) .search-autocomplete-container__directions { + display: block; + padding:10px 20px; +} + +#federated-search-page-block-form .search-autocomplete-container:not(.visually-hidden) .search-autocomplete-container__directions-item { + display: block; + font-size:.8em; + line-height:1.46667em; +} + +.search-autocomplete-container:not(.visually-hidden) .autocomplete-suggestion { + cursor:pointer; + background-color:#fff; + white-space: nowrap; +} + +.search-autocomplete-container:not(.visually-hidden) .autocomplete-suggestion__link { + display: block; + color:#737373; + text-decoration: none; + font-size:.8em; + line-height:1.46667em; + padding:15px 20px; + border:1px solid #fff; + border-bottom-color:#ccc; +} + +.search-autocomplete-container:not(.visually-hidden) .highlight .autocomplete-suggestion__link, +.search-autocomplete-container:not(.visually-hidden) .autocomplete-suggestion__link:hover { + text-decoration: underline; + background-color:#f6f6f6; + border:1px solid #f6f6f6; + border-bottom-color:#ccc; +} + +.autocomplete-selected { + background: #f0f0f0; +} + +.visually-hidden { + position: absolute !important; + clip: rect(1px 1px 1px 1px); +} diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js new file mode 100644 index 000000000..c8f0d74da --- /dev/null +++ b/js/search_api_federated_solr_autocomplete.js @@ -0,0 +1,219 @@ +/** + * @file + * Adds autocomplete functionality to search_api_solr_federated block form. + */ + +(function ($, Drupal, drupalSettings) { + + 'use strict'; + + var autocomplete = {}; + + /** + * Attaches our custom autocomplete settings to the search_api_federated_solr block search form field. + * + * @type {Drupal~behavior} + * + * @prop {Drupal~behaviorAttach} attach + * Attaches the autocomplete behaviors. + */ + Drupal.behaviors.searchApiFederatedSolrAutocomplete = { + attach: function (context, settings) { + // Find our fields with autocomplete settings + $(context) + .find('#edit-search[data-search-api-autocomplete-search]') + .once('search-api-federated-solr-autocomplete-search') + .each(function () { + var $input = $(this); + var $form = $('#federated-search-page-block-form'); + + $input.attr("role","combobox") + .attr("aria-owns","res") + .attr("aria-autocomplete","list") + .attr("aria-expanded","false"); + + $('
What are you looking for?
Press ENTER to search for your current term or ESC to close.Press ↑ and ↓ to highlight a suggestion then ENTER to be redirected to that suggestion.
').insertAfter($(this)); + + var $results = $('#res'); + var $autocompleteContainer = $('.search-autocomplete-container'); + var $closeButton = $('.search-autocomplete-container__close-button'); + + var resultsLimit = 5; + var current; + var counter = 1; + var keys = { + ESC: 27, + TAB: 9, + RETURN: 13, + UP: 38, + DOWN: 40 + }; + + $(this).on("input", function(event) { + doSearch(resultsLimit); + }); + + $(this).on("keydown", function(event) { + doKeypress(keys, event); + }); + + function doSearch(resultsLimit) { + $input.removeAttr("aria-activedescendant"); + var query = $input.val(); + if (query.length >= 2) { + + $.ajax({ + // @todo: get this from config + url: "/search_api_autocomplete/quick_search?display=page_1&filter=full_text_title&q=" + query + }) + .done(function( results ) { + if (results.length >= 1) { + // Remove all suggestions + $('.autocomplete-suggestion').remove(); + $autocompleteContainer.removeClass('visually-hidden'); + $("#search-autocomplete").append(''); + $input.attr("aria-expanded","true"); + counter = 1; + } + + // Bind click event for close button + $closeButton.on("click", function(event) { + event.preventDefault(); + event.stopPropagation(); + $input.removeAttr("aria-activedescendant"); + // Remove all suggestions + $('.autocomplete-suggestion').remove(); + $autocompleteContainer.addClass('visually-hidden'); + $input.attr("aria-expanded","false"); + $input.focus(); + }); + + //Add results to the list + for (var term in results) { + if (counter <= resultsLimit) { + $results.append(""); + counter = counter + 1; + } + } + var number = $results.children('[role="option"]').length; + if (number >= 1) { + Drupal.announce(Drupal.t(number + " suggestions displayed. To navigate use up and down arrow keys.")); + } + }); + } + else { + // Remove all suggestions + $('.autocomplete-suggestion').remove(); + $autocompleteContainer.addClass('visually-hidden'); + $input.attr("aria-expanded","false"); + } + } + + function doKeypress(keys, event) { + var $suggestions = $('.autocomplete-suggestion'); + var highlighted = false; + highlighted = $results.children('div').hasClass('highlight'); + + switch (event.which) { + case keys.ESC: + event.preventDefault(); + event.stopPropagation(); + $input.removeAttr("aria-activedescendant"); + $suggestions.remove(); + $autocompleteContainer.addClass('visually-hidden'); + $input.attr("aria-expanded","false"); + break; + + case keys.TAB: + $input.removeAttr("aria-activedescendant"); + $suggestions.remove(); + $autocompleteContainer.addClass('visually-hidden'); + $input.attr("aria-expanded","false"); + break; + + case keys.RETURN: + if (highlighted) { + event.preventDefault(); + event.stopPropagation(); + return selectOption(highlighted, $('.highlight').find('a').attr('href')); + } + else { + $form.submit(); + return false; + } + break; + + case keys.UP: + event.preventDefault(); + event.stopPropagation(); + return moveUp(highlighted); + break; + + case keys.DOWN: + event.preventDefault(); + event.stopPropagation(); + return moveDown(highlighted); + break; + + default: + return; + } + } + + function moveUp(highlighted) { + $input.removeAttr("aria-activedescendant"); + + // if highlighted exists and if the highlighted item is not the first option + if (highlighted && !$results.children().first('div').hasClass('highlight')) { + removeCurrent(); + current.prev('div').addClass('highlight').attr('aria-selected', true); + $input.attr("aria-activedescendant", current.prev('div').attr('id')); + } + else { + // Go to bottom of list + removeCurrent(); + current = $results.children().last('div'); + current.addClass('highlight').attr('aria-selected', true); + $input.attr("aria-activedescendant", current.attr('id')); + } + } + + function moveDown(highlighted) { + $input.removeAttr("aria-activedescendant"); + + // if highlighted exists and if the highlighted item is not the last option + if (highlighted && !$results.children().last('div').hasClass('highlight')) { + removeCurrent(); + current.next('div').addClass('highlight').attr('aria-selected', true); + $input.attr("aria-activedescendant", current.next('div').attr('id')); + } + else { + // Go to top of list + removeCurrent(); + current = $results.children().first('div'); + current.addClass('highlight').attr('aria-selected', true); + $input.attr("aria-activedescendant", current.attr('id')); + } + } + + function removeCurrent() { + current = $results.find('.highlight'); + current.attr('aria-selected', false); + current.removeClass('highlight'); + } + + function selectOption(highlighted, href) { + if (highlighted && href) { // @todo add logic for non-link suggestions + $(location).attr("href", href); + } + else { + return; + } + } + }); + } + }; + + Drupal.SearchApiFederatedSolrAutocomplete = autocomplete; + +})(jQuery, Drupal, drupalSettings); diff --git a/search_api_federated_solr.libraries.yml b/search_api_federated_solr.libraries.yml index 53545e8bd..2e718efc7 100644 --- a/search_api_federated_solr.libraries.yml +++ b/search_api_federated_solr.libraries.yml @@ -8,3 +8,16 @@ search: preprocess: false, minified: true, } + +search_form_autocomplete: + css: + theme: + css/search_api_federated_solr_autocomplete.css: {} + js: + js/search_api_federated_solr_autocomplete.js: {} + dependencies: + - core/jquery + - core/drupal + - core/drupalSettings + - core/drupal.ajax + - core/drupal.announce diff --git a/src/Form/FederatedSearchPageBlockForm.php b/src/Form/FederatedSearchPageBlockForm.php index e8e808367..9789e86b5 100644 --- a/src/Form/FederatedSearchPageBlockForm.php +++ b/src/Form/FederatedSearchPageBlockForm.php @@ -41,6 +41,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#attributes' => [ 'title' => $this->t('Enter the terms you wish to search for.'), 'placeholder' => 'Search', + 'autocomplete' => "off", ], '#prefix' => '
', ]; @@ -70,6 +71,8 @@ public function buildForm(array $form, FormStateInterface $form_state) { $renderer->addCacheableDependency($form, $index_config); } + $form['#attached']['library'][] = 'search_api_federated_solr/search_form_autocomplete'; + $form['#action'] = $this->getUrlGenerator()->generateFromRoute('search_api_federated_solr.search'); $form['#method'] = 'get'; From dd6ae6d124ee644753aa01df1798b0d0cf4da5fd Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Fri, 8 Mar 2019 11:34:12 -0500 Subject: [PATCH 20/59] root-198: use Drupal.announce, highlight results, use better logic --- js/search_api_federated_solr_autocomplete.js | 57 ++++++++++++-------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index c8f0d74da..cae7b0d88 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -72,37 +72,50 @@ $('.autocomplete-suggestion').remove(); $autocompleteContainer.removeClass('visually-hidden'); $("#search-autocomplete").append(''); - $input.attr("aria-expanded","true"); + $input.attr("aria-expanded", "true"); counter = 1; - } - // Bind click event for close button - $closeButton.on("click", function(event) { - event.preventDefault(); - event.stopPropagation(); - $input.removeAttr("aria-activedescendant"); - // Remove all suggestions + // Bind click event for close button + $closeButton.on("click", function (event) { + event.preventDefault(); + event.stopPropagation(); + $input.removeAttr("aria-activedescendant"); + // Remove all suggestions + $('.autocomplete-suggestion').remove(); + $autocompleteContainer.addClass('visually-hidden'); + $input.attr("aria-expanded", "false"); + $input.focus(); + }); + + // Get first [resultsLimit] results + var limitedResults = results.slice(0, resultsLimit); + limitedResults.forEach(function(item) { + // Highlight query chars in returned title + var pattern = new RegExp(query, "gi"); + var highlighted = item.title.replace(pattern, function(string) { + return "" + string + "" + }); + + //Add results to the list + $results.append("
" + highlighted + "(" + counter + " of " + limitedResults.length + ")
"); + counter = counter + 1; + }); + + // Announce the number of suggestions. + var number = $results.children('[role="option"]').length; + if (number >= 1) { + Drupal.announce(Drupal.t(number + " suggestions displayed. To navigate use up and down arrow keys.")); + } + } else { + // No results, remove suggestions and hide container $('.autocomplete-suggestion').remove(); $autocompleteContainer.addClass('visually-hidden'); $input.attr("aria-expanded","false"); - $input.focus(); - }); - - //Add results to the list - for (var term in results) { - if (counter <= resultsLimit) { - $results.append(""); - counter = counter + 1; - } - } - var number = $results.children('[role="option"]').length; - if (number >= 1) { - Drupal.announce(Drupal.t(number + " suggestions displayed. To navigate use up and down arrow keys.")); } }); } else { - // Remove all suggestions + // Remove suggestions and hide container $('.autocomplete-suggestion').remove(); $autocompleteContainer.addClass('visually-hidden'); $input.attr("aria-expanded","false"); From 83527aa874a0b3f70daa59faf0dea62b7d522ec8 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Fri, 8 Mar 2019 11:40:26 -0500 Subject: [PATCH 21/59] root-198: use view rest export --- js/search_api_federated_solr_autocomplete.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index cae7b0d88..7608da4ea 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -64,7 +64,9 @@ $.ajax({ // @todo: get this from config - url: "/search_api_autocomplete/quick_search?display=page_1&filter=full_text_title&q=" + query + // url: "/search_api_autocomplete/quick_search?display=page_1&filter=full_text_title&q=" + query + url: "/search-api-federated-solr-block-form-autocomplete-rest?_format=json&term=" + query, + dataType: 'json' }) .done(function( results ) { if (results.length >= 1) { From 249051cee4795255a1954301df1401e75e4b47a0 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Fri, 8 Mar 2019 11:46:36 -0500 Subject: [PATCH 22/59] root-198: adhere to solr schema for title/url --- js/search_api_federated_solr_autocomplete.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index 7608da4ea..0a1527f34 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -94,12 +94,12 @@ limitedResults.forEach(function(item) { // Highlight query chars in returned title var pattern = new RegExp(query, "gi"); - var highlighted = item.title.replace(pattern, function(string) { + var highlighted = item.ss_federated_title.replace(pattern, function(string) { return "" + string + "" }); //Add results to the list - $results.append("
" + highlighted + "(" + counter + " of " + limitedResults.length + ")
"); + $results.append("
" + highlighted + "(" + counter + " of " + limitedResults.length + ")
"); counter = counter + 1; }); From ad5f499821d9e58b0f9fa5ecfde4d7a2a6815759 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Fri, 8 Mar 2019 11:53:48 -0500 Subject: [PATCH 23/59] root-198: add stubbed out view rest export for autocomplete --- ...federated_solr_block_form_autocomplete.yml | 300 ++++++++++++++++++ search_api_federated_solr.info.yml | 1 + 2 files changed, 301 insertions(+) create mode 100644 config/install/views.view.search_api_federated_solr_block_form_autocomplete.yml diff --git a/config/install/views.view.search_api_federated_solr_block_form_autocomplete.yml b/config/install/views.view.search_api_federated_solr_block_form_autocomplete.yml new file mode 100644 index 000000000..62eec8eb0 --- /dev/null +++ b/config/install/views.view.search_api_federated_solr_block_form_autocomplete.yml @@ -0,0 +1,300 @@ +uuid: f17b010f-c4f0-497f-802e-047b08b4c004 +langcode: en +status: true +dependencies: + module: + - node + - rest + - serialization + - user +id: search_api_federated_solr_block_form_autocomplete +label: 'Search API Federated Solr Block Form Autocomplete' +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: Search + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: false + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: some + options: + items_per_page: 10 + offset: 0 + style: + type: default + row: + type: fields + fields: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: string + settings: + link_to_entity: false + 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 + plugin_id: field + view_node: + id: view_node + table: node + field: view_node + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + text: view + output_url_as_text: true + absolute: false + entity_type: node + plugin_id: entity_link + 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: word + value: '' + group: 1 + exposed: true + expose: + operator_id: title_op + label: Title + description: '' + use_operator: false + operator: title_op + identifier: term + required: true + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + author: '0' + editor: '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 + sorts: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + order: ASC + exposed: false + expose: + label: '' + entity_type: node + entity_field: title + plugin_id: standard + title: 'Search API Federated Solr Block Form Autocomplete' + header: { } + footer: { } + empty: { } + relationships: { } + arguments: { } + display_extenders: { } + use_ajax: true + filter_groups: + operator: AND + groups: + 1: AND + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - 'user.node_grants:view' + - user.permissions + tags: { } + rest_export_1: + display_plugin: rest_export + id: rest_export_1 + display_title: 'REST export' + position: 2 + display_options: + display_extenders: { } + path: search-api-federated-solr-block-form-autocomplete-rest + style: + type: serializer + options: + uses_fields: false + formats: + json: json + row: + type: data_field + options: + field_options: + title: + alias: ss_federated_title + raw_output: false + view_node: + alias: ss_url + raw_output: false + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - request_format + - url + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/search_api_federated_solr.info.yml b/search_api_federated_solr.info.yml index 0599f238f..663d79ed8 100644 --- a/search_api_federated_solr.info.yml +++ b/search_api_federated_solr.info.yml @@ -8,3 +8,4 @@ dependencies: - search_api_solr - token - field + - rest From 24f8aa4b21a795ed5565553cae2351075200ee63 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Fri, 8 Mar 2019 13:31:44 -0500 Subject: [PATCH 24/59] Root-198: add js hook to search form --- src/Form/FederatedSearchPageBlockForm.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Form/FederatedSearchPageBlockForm.php b/src/Form/FederatedSearchPageBlockForm.php index 9789e86b5..398eee96b 100644 --- a/src/Form/FederatedSearchPageBlockForm.php +++ b/src/Form/FederatedSearchPageBlockForm.php @@ -42,6 +42,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { 'title' => $this->t('Enter the terms you wish to search for.'), 'placeholder' => 'Search', 'autocomplete' => "off", + 'data-search-api-autocomplete-search' => "quick_search", ], '#prefix' => '
', ]; From 08d52dbd5ffadc40a0250dd9cc427ce16981222a Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Fri, 8 Mar 2019 15:20:20 -0500 Subject: [PATCH 25/59] root-198: scaffold out federated search page block config --- .../Block/FederatedSearchPageFormBlock.php | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index 2dd13591f..a6663898a 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -4,6 +4,7 @@ use Drupal\Core\Block\BlockBase; use Drupal\Core\Block\BlockPluginInterface; +use Drupal\Core\Form\FormStateInterface; /** * Provides a "Federated Search Page Form" block. @@ -27,4 +28,163 @@ public function build() { return $build; } + /** + * {@inheritdoc} + */ + public function blockForm($form, FormStateInterface $form_state) { + $form = parent::blockForm($form, $form_state); + + $config = $this->getConfiguration(); + + $index_options = []; + $search_api_indexes = \Drupal::entityTypeManager()->getStorage('search_api_index')->loadMultiple(); + /* @var $search_api_index \Drupal\search_api\IndexInterface */ + foreach ($search_api_indexes as $search_api_index) { + $index_options[$search_api_index->id()] = $search_api_index->label(); + } + + /** + * Autocomplete settings: + * - endpoint URL + * - use wildcard to support partial terms + * - customize number of autocomplete results + * - number of characters after which autocomplete query should be executed + * - autocomplete results mode (search results, terms) + * - title for autocomplete results + * - show/hide autocomplete keyboard directions + */ + + $form['autocomplete'] = [ + '#type' => 'details', + '#title' => $this->t('Federated Search Page Form Block > Search Form > Autocomplete'), + '#description' => $this->t('These options apply to the autocomplete functionality on the search form which appears on pages that render the Federated Search Page Form Block. Configure the search results page search form functionality on the Federated Search App settings page.'), + '#open' => TRUE, + ]; + + $form['autocomplete']['autocomplete_is_enabled'] = [ + '#type' => 'checkbox', + '#title' => '' . $this->t('Enable autocomplete for the search results page search form') . '', + '#default_value' => $config['autocomplete.isEnabled'], + '#description' => $this + ->t('Checking this will expose more configuration options for autocomplete behavior for the search form on the Search Results page at the end of this form.'), + ]; + + $form['autocomplete']['autocomplete_url'] = [ + '#type' => 'url', + '#title' => $this->t('Endpoint URL'), + '#default_value' => $config['autocomplete.url'], + '#maxlength' => 2048, + '#size' => 50, + '#description' => $this + ->t('The URL where requests for autocomplete queries should be made. Defaults to the url of the select Request Handler on the server of the selected Search API index.
Supports absolute url pattern to any endpoint which returns the expected autocomplete result structure.'), + ]; + + $form['autocomplete']['autocomplete_is_append_wildcard'] = [ + '#type' => 'checkbox', + '#title' => '' . $this->t('Append a wildcard \'*\' to support partial text search') . '', + '#default_value' => $config['autocomplete.appendWildcard'], + '#description' => $this + ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), + ]; + + $form['autocomplete']['autocomplete_suggestion_rows'] = [ + '#type' => 'number', + '#title' => $this->t('Number of results'), + '#default_value' => $config['autocomplete.suggestionRows'], + '#description' => $this + ->t('The max number of results to render in the autocomplete results dropdown. (Default: 5)'), + ]; + + $form['autocomplete']['autocomplete_num_chars'] = [ + '#type' => 'number', + '#title' => $this->t('Number of characters after which autocomplete query should execute'), + '#default_value' => $config['autocomplete.numChars'], + '#description' => $this + ->t('Autocomplete query will be executed after a user types this many characters in the search query field. (Default: 2)'), + ]; + + $autocomplete_mode = $config['autocomplete.mode']; + $title_text_config_key = 'autocomplete.' . $autocomplete_mode . '.titleText'; + $hide_directions_text_config_key = 'autocomplete.' . $autocomplete_mode . '.hideDirectionsText'; + + $form['autocomplete']['autocomplete_mode'] = [ + '#type' => 'select', + '#title' => $this->t('Result mode'), + '#description' => $this->t('Type of results the autocomplete response returns: search results (default) or search terms.'), + '#options' => [ + 'result' => $this + ->t('Search results (i.e. search as you type functionality)'), + 'Search terms (coming soon)' => [], + ], + '#default_value' => $autocomplete_mode || 'result', + ]; + + $form['autocomplete']['autocomplete_mode_title_text'] = [ + '#type' => 'textfield', + '#title' => $this->t('Results title text'), + '#size' => 50, + '#default_value' => $autocomplete_mode ? $config[$title_text_config_key] : '', + '#description' => $this + ->t('The title text is shown above the results in the autocomplete drop down. (Default: "What are you interested in?" for Search Results mode and "What would you like to search for?" for Search Term mode.)'), + ]; + + $form['autocomplete']['autocomplete_mode_hide_directions'] = [ + '#type' => 'checkbox', + '#title' => '' . $this->t('Hide keyboard directions') . '', + '#default_value' => $autocomplete_mode ? $config->get($hide_directions_text_config_key) : 0, + '#description' => $this + ->t('Check this box to make hide the autocomplete keyboard usage directions in the results dropdown. For sites that want to maximize their accessibility UX for sighted keyboard users, we recommend leaving this unchecked. (Default: directions are visible)'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function blockSubmit($form, FormStateInterface $form_state) { + parent::blockSubmit($form, $form_state); + $values = $form_state->getValues(); + // Set autocomplete options. + $autocomplete_is_enabled = $values['autocomplete']['autocomplete_is_enabled']; + $this->configuration['autocomplete']['isEnabled'] = $autocomplete_is_enabled; + + // If enabled, set the autocomplete options. + if ($autocomplete_is_enabled) { + // Cache form values that we'll use more than once. + $autocomplete_url_value = $values['autocomplete']['autocomplete_url']; + $autocomplete_mode = $values['autocomplete']['autocomplete_mode']; + + // Set the default autocomplete endpoint url to the default search url if none was passed in. + // Get the id of the chosen index's server. + $app_config = \Drupal::config('search_api_federated_solr.search_app.settings'); + $search_index = $app_config->get('index.id'); + $index_config = \Drupal::config('search_api.index.' . $search_index); + $index_server = $index_config->get('server'); + + // Get the server url. + $server_config = \Drupal::config('search_api.server.' . $index_server); + $server = $server_config->get('backend_config.connector_config'); + // Get the required server config field data. + $server_url = $server['scheme'] . '://' . $server['host'] . ':' . $server['port']; + // Check for the non-required server config field data before appending. + $server_url .= $server['path'] ?: ''; + $server_url .= $server['core'] ? '/' . $server['core'] : ''; + // Append the request handler. + $server_url .= '/select'; + $autocomplete_url = $autocomplete_url_value ? $autocomplete_url_value : $server_url; + + // Set the actual autocomplete config options. + $this->configuration['autocomplete']['url'] = $autocomplete_url; + $this->configuration['autocomplete']['appendWildcard'] = $values['autocomplete']['autocomplete_is_append_wildcard']; + $this->configuration['autocomplete']['suggestionRows'] = $values['autocomplete']['autocomplete_suggestion_rows']; + $this->configuration['autocomplete']['numChars'] = $values['autocomplete']['autocomplete_num_chars']; + if ($autocomplete_mode) { + $this->configuration['autocomplete']['mode'] = $autocomplete_mode; + $this->configuration['autocomplete'][$autocomplete_mode]['titleText'] = $values['autocomplete']['autocomplete_mode_title_text']; + $this->configuration['autocomplete'][$autocomplete_mode]['hideDirectionsText'] = $values['autocomplete']['autocomplete_mode_hide_directions']; + } + } + } + } From 8266d7e583cac96b53a6dd29b083d9dfdfdabc0d Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Fri, 8 Mar 2019 15:20:53 -0500 Subject: [PATCH 26/59] root-198: make config available in drupalSettings for block --- search_api_federated_solr.module | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/search_api_federated_solr.module b/search_api_federated_solr.module index de3bbe99d..1a510dfab 100644 --- a/search_api_federated_solr.module +++ b/search_api_federated_solr.module @@ -148,3 +148,12 @@ function search_api_federated_solr_form_search_api_federated_solr_search_app_set } } + +/** + * implements hook_preprocess_block_BASE_BLOCK_ID + */ +function search_api_federated_solr_preprocess_block__federated_search_page_form_block(&$variables) { + $variables['#attached']['drupalSettings']['federatedSearchPageFormBlock'] = [ + 'autocomplete' => $variables['elements']['#configuration']['autocomplete'], + ]; +} From 10c1e95b57546ab4d94b31a2ae5666f9402aadf7 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Fri, 8 Mar 2019 15:22:03 -0500 Subject: [PATCH 27/59] root-198: begin working with block config in js --- js/search_api_federated_solr_autocomplete.js | 57 ++++++++++++++++---- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index 0a1527f34..91068ec2e 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -24,21 +24,54 @@ .find('#edit-search[data-search-api-autocomplete-search]') .once('search-api-federated-solr-autocomplete-search') .each(function () { + // Halt execution if we don't have the required config. + if (!Object.hasOwnProperty.call(drupalSettings, 'federatedSearchPageFormBlock') + || !Object.hasOwnProperty.call(drupalSettings.federatedSearchPageFormBlock, 'autocomplete') + || !Object.hasOwnProperty.call(drupalSettings.federatedSearchPageFormBlock.autocomplete, 'url')) { + return; + } + + // Set default settings. + var defaultSettings = { + isEnabled: false, + appendWildcard: false, + numChars: 2, + suggestionRows: 5, + mode: 'result', + result: { + titleText: "What are you looking for?", + hideDirectionsText: 0 + } + }; + // Get passed in config from block config. + var config = drupalSettings.federatedSearchPageFormBlock.autocomplete; + // Merge defaults with passed in config. + var options = Object.assign({}, defaultSettings, config); + + // Set scaffolding markup for suggestions container + var suggestionsContainerScaffoldingMarkup = '
' + options[options.mode].titleText + '
'; + + if (!options[options.mode].hideDirectionsText) { + suggestionsContainerScaffoldingMarkup += '
Press ENTER to search for your current term or ESC to close.Press ↑ and ↓ to highlight a suggestion then ENTER to be redirected to that suggestion.
'; + } + + suggestionsContainerScaffoldingMarkup += '
'; + + // Cache selectors. var $input = $(this); var $form = $('#federated-search-page-block-form'); - + // Set up input with attributes, suggestions scaffolding. $input.attr("role","combobox") .attr("aria-owns","res") .attr("aria-autocomplete","list") .attr("aria-expanded","false"); - - $('
What are you looking for?
Press ENTER to search for your current term or ESC to close.Press ↑ and ↓ to highlight a suggestion then ENTER to be redirected to that suggestion.
').insertAfter($(this)); - + $(suggestionsContainerScaffoldingMarkup).insertAfter($input); + // Cache inserted selectors. var $results = $('#res'); var $autocompleteContainer = $('.search-autocomplete-container'); var $closeButton = $('.search-autocomplete-container__close-button'); - var resultsLimit = 5; + // Initiate helper vars. var current; var counter = 1; var keys = { @@ -49,15 +82,17 @@ DOWN: 40 }; - $(this).on("input", function(event) { - doSearch(resultsLimit); + // Bind events to input. + $input.on("input", function(event) { + doSearch(options.suggestionRows); }); - $(this).on("keydown", function(event) { + $input.on("keydown", function(event) { doKeypress(keys, event); }); - function doSearch(resultsLimit) { + // Define event handlers. + function doSearch(suggestionRows) { $input.removeAttr("aria-activedescendant"); var query = $input.val(); if (query.length >= 2) { @@ -89,8 +124,8 @@ $input.focus(); }); - // Get first [resultsLimit] results - var limitedResults = results.slice(0, resultsLimit); + // Get first [suggestionRows] results + var limitedResults = results.slice(0, suggestionRows); limitedResults.forEach(function(item) { // Highlight query chars in returned title var pattern = new RegExp(query, "gi"); From 7638115c2c241e0177bd69d4b5cbe1b1f806f36a Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 13:51:54 -0400 Subject: [PATCH 28/59] root-198: create view rest export serializer to structure solr response --- src/Plugin/views/style/SolrSerializer.php | 38 +++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/Plugin/views/style/SolrSerializer.php diff --git a/src/Plugin/views/style/SolrSerializer.php b/src/Plugin/views/style/SolrSerializer.php new file mode 100644 index 000000000..4f84979d5 --- /dev/null +++ b/src/Plugin/views/style/SolrSerializer.php @@ -0,0 +1,38 @@ + Date: Mon, 11 Mar 2019 13:56:49 -0400 Subject: [PATCH 29/59] root-198: move autocomplete view to optional, use new serializer --- ...federated_solr_block_form_autocomplete.yml | 222 ++++++++++++++++-- 1 file changed, 203 insertions(+), 19 deletions(-) rename config/{install => optional}/views.view.search_api_federated_solr_block_form_autocomplete.yml (59%) diff --git a/config/install/views.view.search_api_federated_solr_block_form_autocomplete.yml b/config/optional/views.view.search_api_federated_solr_block_form_autocomplete.yml similarity index 59% rename from config/install/views.view.search_api_federated_solr_block_form_autocomplete.yml rename to config/optional/views.view.search_api_federated_solr_block_form_autocomplete.yml index 62eec8eb0..f723cbc57 100644 --- a/config/install/views.view.search_api_federated_solr_block_form_autocomplete.yml +++ b/config/optional/views.view.search_api_federated_solr_block_form_autocomplete.yml @@ -5,6 +5,7 @@ dependencies: module: - node - rest + - search_api_federated_solr - serialization - user id: search_api_federated_solr_block_form_autocomplete @@ -228,21 +229,7 @@ display: entity_type: node entity_field: title plugin_id: string - sorts: - title: - id: title - table: node_field_data - field: title - relationship: none - group_type: group - admin_label: '' - order: ASC - exposed: false - expose: - label: '' - entity_type: node - entity_field: title - plugin_id: standard + sorts: { } title: 'Search API Federated Solr Block Form Autocomplete' header: { } footer: { } @@ -267,15 +254,14 @@ display: rest_export_1: display_plugin: rest_export id: rest_export_1 - display_title: 'REST export' + display_title: 'REST export - exposed' position: 2 display_options: display_extenders: { } - path: search-api-federated-solr-block-form-autocomplete-rest + path: search-api-federated-solr-block-form-autocomplete/exposed-filter style: - type: serializer + type: solr_serializer options: - uses_fields: false formats: json: json row: @@ -288,6 +274,204 @@ display: view_node: alias: ss_url raw_output: false + display_description: '' + 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: word + value: '' + group: 1 + exposed: true + expose: + operator_id: title_op + label: Title + description: '' + use_operator: false + operator: title_op + identifier: title + required: true + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + author: '0' + editor: '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 + defaults: + filters: false + filter_groups: false + filter_groups: + operator: AND + groups: + 1: AND + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - request_format + - url + - 'user.node_grants:view' + - user.permissions + tags: { } + rest_export_2: + display_plugin: rest_export + id: rest_export_2 + display_title: 'REST export - contextual' + position: 2 + display_options: + display_extenders: { } + display_description: '' + path: search-api-federated-solr-block-form-autocomplete/contextual-filter/% + 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: contains + value: '' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + 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 + defaults: + filters: false + filter_groups: false + arguments: false + filter_groups: + operator: AND + groups: + 1: AND + arguments: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + default_action: default + exception: + value: all + title_enable: false + title: All + title_enable: false + title: '' + default_argument_type: raw + default_argument_options: + index: 1 + use_alias: false + default_argument_skip_url: false + summary_options: + base_path: '' + count: true + items_per_page: 25 + override: false + summary: + sort_order: asc + number_of_records: 0 + format: default_summary + specify_validation: true + validate: + type: 'entity:field_config' + fail: ignore + validate_options: + operation: view + multiple: 0 + access: false + bundles: null + glossary: false + limit: 0 + case: none + path_case: lower + transform_dash: true + break_phrase: false + entity_type: node + entity_field: title + plugin_id: string + row: + type: data_field + options: + field_options: + title: + alias: ss_federated_title + raw_output: false + view_node: + alias: ss_url + raw_output: false cache_metadata: max-age: -1 contexts: From 653c62ca3ee0020415ed58d0c63b5ddb8f1ea8f4 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 13:57:46 -0400 Subject: [PATCH 30/59] root-198: fix config item paths in bock config form --- .../Block/FederatedSearchPageFormBlock.php | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index a6663898a..07584e12c 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -64,33 +64,43 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_is_enabled'] = [ '#type' => 'checkbox', '#title' => '' . $this->t('Enable autocomplete for the search results page search form') . '', - '#default_value' => $config['autocomplete.isEnabled'], + '#default_value' => $config['autocomplete']['isEnabled'], '#description' => $this - ->t('Checking this will expose more configuration options for autocomplete behavior for the search form on the Search Results page at the end of this form.'), + ->t('Check this box to enable autocomplete for the text input on the search form rendered in this block.'), + ]; + + $form['autocomplete']['autocomplete_is_append_wildcard'] = [ + '#type' => 'checkbox', + '#title' => '' . $this->t('Append a wildcard \'*\' to support partial text search') . '', + '#default_value' => $config['autocomplete']['appendWildcard'], + '#description' => $this + ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), ]; $form['autocomplete']['autocomplete_url'] = [ '#type' => 'url', '#title' => $this->t('Endpoint URL'), - '#default_value' => $config['autocomplete.url'], + '#default_value' => $config['autocomplete']['url'], '#maxlength' => 2048, '#size' => 50, '#description' => $this - ->t('The URL where requests for autocomplete queries should be made. Defaults to the url of the select Request Handler on the server of the selected Search API index.
Supports absolute url pattern to any endpoint which returns the expected autocomplete result structure.'), - ]; - - $form['autocomplete']['autocomplete_is_append_wildcard'] = [ - '#type' => 'checkbox', - '#title' => '' . $this->t('Append a wildcard \'*\' to support partial text search') . '', - '#default_value' => $config['autocomplete.appendWildcard'], - '#description' => $this - ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), + ->t('The URL where requests for autocomplete queries should be made. (Default: the url of the select Request Handler on the server of the selected Search API index.)
  • Supports absolute url pattern to any endpoint which returns the expected response structure: +
    {
    +  response: {
    +   docs: [
    +     {
    +       ss_federated_title: [result title to be used as link text],
    +       ss_url: [result url to be used as link href],
    +     }
    +   ]
    +  }
    +}
  • Include [val] in the URL to indicate where you would like the form value to be inserted: http://d8.fs-demo.local/search-api-federated-solr-block-form-autocomplete/search-view?title=[val]&_format=json
  • Any facet/filter default values set for the search app will automatically be appended (i.e. &sm_site_name=[value of the site name for the index])
  • Include any other necessary url params (like &_format=json if you are using a Views Rest Export or &wt=json if you are using a different Request Handler on your Solr index.
  • '), ]; $form['autocomplete']['autocomplete_suggestion_rows'] = [ '#type' => 'number', '#title' => $this->t('Number of results'), - '#default_value' => $config['autocomplete.suggestionRows'], + '#default_value' => $config['autocomplete']['suggestionRows'], '#description' => $this ->t('The max number of results to render in the autocomplete results dropdown. (Default: 5)'), ]; @@ -98,14 +108,12 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_num_chars'] = [ '#type' => 'number', '#title' => $this->t('Number of characters after which autocomplete query should execute'), - '#default_value' => $config['autocomplete.numChars'], + '#default_value' => $config['autocomplete']['numChars'], '#description' => $this ->t('Autocomplete query will be executed after a user types this many characters in the search query field. (Default: 2)'), ]; - $autocomplete_mode = $config['autocomplete.mode']; - $title_text_config_key = 'autocomplete.' . $autocomplete_mode . '.titleText'; - $hide_directions_text_config_key = 'autocomplete.' . $autocomplete_mode . '.hideDirectionsText'; + $autocomplete_mode = $config['autocomplete']['mode']; $form['autocomplete']['autocomplete_mode'] = [ '#type' => 'select', @@ -116,14 +124,14 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('Search results (i.e. search as you type functionality)'), 'Search terms (coming soon)' => [], ], - '#default_value' => $autocomplete_mode || 'result', + '#default_value' => $config['autocomplete']['mode'] || 'result', ]; $form['autocomplete']['autocomplete_mode_title_text'] = [ '#type' => 'textfield', '#title' => $this->t('Results title text'), '#size' => 50, - '#default_value' => $autocomplete_mode ? $config[$title_text_config_key] : '', + '#default_value' => $autocomplete_mode ? $config['autocomplete'][$autocomplete_mode]['titleText'] : '', '#description' => $this ->t('The title text is shown above the results in the autocomplete drop down. (Default: "What are you interested in?" for Search Results mode and "What would you like to search for?" for Search Term mode.)'), ]; @@ -131,7 +139,7 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_mode_hide_directions'] = [ '#type' => 'checkbox', '#title' => '' . $this->t('Hide keyboard directions') . '', - '#default_value' => $autocomplete_mode ? $config->get($hide_directions_text_config_key) : 0, + '#default_value' => $autocomplete_mode ? $config['autocomplete'][$autocomplete_mode]['hideDirectionsText'] : 0, '#description' => $this ->t('Check this box to make hide the autocomplete keyboard usage directions in the results dropdown. For sites that want to maximize their accessibility UX for sighted keyboard users, we recommend leaving this unchecked. (Default: directions are visible)'), ]; From ae15addec15b5876963849beb16c77ad9e0351c3 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 13:59:13 -0400 Subject: [PATCH 31/59] root-198: update js response handler to expect solr response structure --- js/search_api_federated_solr_autocomplete.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index 91068ec2e..f7c027985 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -103,8 +103,22 @@ url: "/search-api-federated-solr-block-form-autocomplete-rest?_format=json&term=" + query, dataType: 'json' }) + // Currently we only support the response structure from Solr: + // { + // response: { + // docs: [ + // { + // ss_federated_title: , + // ss_url: , + // } + // ] + // } + // } + + // @todo provide hook for transform function to be passed in + // via Drupal.settings then all it here. .done(function( results ) { - if (results.length >= 1) { + if (results.response.docs.length >= 1) { // Remove all suggestions $('.autocomplete-suggestion').remove(); $autocompleteContainer.removeClass('visually-hidden'); @@ -125,7 +139,7 @@ }); // Get first [suggestionRows] results - var limitedResults = results.slice(0, suggestionRows); + var limitedResults = results.response.docs.slice(0, suggestionRows); limitedResults.forEach(function(item) { // Highlight query chars in returned title var pattern = new RegExp(query, "gi"); From c67c0354ca378ac3571293d18a452748cc4ae57e Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 13:59:49 -0400 Subject: [PATCH 32/59] root-198: get url from config, append default value params, wildecard --- js/search_api_federated_solr_autocomplete.js | 44 +++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index f7c027985..a3f43e918 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -82,6 +82,14 @@ DOWN: 40 }; + // Determine param values for any set default filters/facets. + var defaultParams = ''; + $('input[type="hidden"]', $form).each(function(index, input) { + defaultParams += '&' + $(input).attr('name') + '=' + encodeURI($(input).val()); + }); + var urlWithDefaultParams = options.url + defaultParams; + + // Bind events to input. $input.on("input", function(event) { doSearch(options.suggestionRows); @@ -94,13 +102,37 @@ // Define event handlers. function doSearch(suggestionRows) { $input.removeAttr("aria-activedescendant"); - var query = $input.val(); - if (query.length >= 2) { + var value = $input.val(); + // Remove spaces on either end of the value. + var trimmed = value.trim(); + // Default to the trimmed value. + var query = trimmed; + // If the current value has more than the configured number of characters. + if (query.length > options.numChars) { + // Append wildcard to the query if configured to do so. + if (options.appendWildcard) { + // One method of supporting search-as-you-type is to append a wildcard '*' + // to match zero or more additional characters at the end of the users search term. + // @see: https://lucene.apache.org/solr/guide/6_6/the-standard-query-parser.html#TheStandardQueryParser-WildcardSearches + // @see: https://opensourceconnections.com/blog/2013/06/07/search-as-you-type-with-solr/ + // Split into word chunks. + const words = trimmed.split(" "); + // If there are multiple chunks, join them with "+", repeat the last word + append "*". + if (words.length > 1) { + query = words.join("+") + words.pop() + '*'; + } + else { + // If there is only 1 word, repeat it an append "*". + query = words + '+' + words + '*'; + } + } + + // Replace the placeholder with the query value. + var url = urlWithDefaultParams.replace('[val]', query); + // Make the ajax request $.ajax({ - // @todo: get this from config - // url: "/search_api_autocomplete/quick_search?display=page_1&filter=full_text_title&q=" + query - url: "/search-api-federated-solr-block-form-autocomplete-rest?_format=json&term=" + query, + url: url, dataType: 'json' }) // Currently we only support the response structure from Solr: @@ -142,7 +174,7 @@ var limitedResults = results.response.docs.slice(0, suggestionRows); limitedResults.forEach(function(item) { // Highlight query chars in returned title - var pattern = new RegExp(query, "gi"); + var pattern = new RegExp(trimmed, "gi"); var highlighted = item.ss_federated_title.replace(pattern, function(string) { return "" + string + "" }); From 56bb4d39746a27c99b91b67f45c5bb4ead33f8ca Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 14:00:26 -0400 Subject: [PATCH 33/59] root-198: append expected params to default url --- src/Plugin/Block/FederatedSearchPageFormBlock.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index 07584e12c..61ac71c71 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -178,8 +178,8 @@ public function blockSubmit($form, FormStateInterface $form_state) { // Check for the non-required server config field data before appending. $server_url .= $server['path'] ?: ''; $server_url .= $server['core'] ? '/' . $server['core'] : ''; - // Append the request handler. - $server_url .= '/select'; + // Append the request handler, main query and format params. + $server_url .= '/select?q=[val]&wt=json'; $autocomplete_url = $autocomplete_url_value ? $autocomplete_url_value : $server_url; // Set the actual autocomplete config options. From fc4c31540236893d15386627d75e5785e2388f12 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 14:02:00 -0400 Subject: [PATCH 34/59] root-198: don't make module depend on rest (since view is optional config) --- search_api_federated_solr.info.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/search_api_federated_solr.info.yml b/search_api_federated_solr.info.yml index 663d79ed8..0599f238f 100644 --- a/search_api_federated_solr.info.yml +++ b/search_api_federated_solr.info.yml @@ -8,4 +8,3 @@ dependencies: - search_api_solr - token - field - - rest From d2a981b6e581abe72590d485ed92ca947d94024e Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 14:40:51 -0400 Subject: [PATCH 35/59] root-198: only expose block form autocomplete config when enabled --- .../Block/FederatedSearchPageFormBlock.php | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index 61ac71c71..1318de265 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -67,6 +67,9 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $config['autocomplete']['isEnabled'], '#description' => $this ->t('Check this box to enable autocomplete for the text input on the search form rendered in this block.'), + '#attributes' => [ + 'id' => ['autocomplete-is-enabled'], + ], ]; $form['autocomplete']['autocomplete_is_append_wildcard'] = [ @@ -75,6 +78,13 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $config['autocomplete']['appendWildcard'], '#description' => $this ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), + '#states' => [ + 'visible' => [ + ':input[id="autocomplete-is-enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $form['autocomplete']['autocomplete_url'] = [ @@ -95,6 +105,13 @@ public function blockForm($form, FormStateInterface $form_state) { ] } }
  • Include [val] in the URL to indicate where you would like the form value to be inserted: http://d8.fs-demo.local/search-api-federated-solr-block-form-autocomplete/search-view?title=[val]&_format=json
  • Any facet/filter default values set for the search app will automatically be appended (i.e. &sm_site_name=[value of the site name for the index])
  • Include any other necessary url params (like &_format=json if you are using a Views Rest Export or &wt=json if you are using a different Request Handler on your Solr index.
  • '), + '#states' => [ + 'visible' => [ + ':input[id="autocomplete-is-enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $form['autocomplete']['autocomplete_suggestion_rows'] = [ @@ -103,6 +120,13 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $config['autocomplete']['suggestionRows'], '#description' => $this ->t('The max number of results to render in the autocomplete results dropdown. (Default: 5)'), + '#states' => [ + 'visible' => [ + ':input[id="autocomplete-is-enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $form['autocomplete']['autocomplete_num_chars'] = [ @@ -111,6 +135,13 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $config['autocomplete']['numChars'], '#description' => $this ->t('Autocomplete query will be executed after a user types this many characters in the search query field. (Default: 2)'), + '#states' => [ + 'visible' => [ + ':input[id="autocomplete-is-enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $autocomplete_mode = $config['autocomplete']['mode']; @@ -125,6 +156,13 @@ public function blockForm($form, FormStateInterface $form_state) { 'Search terms (coming soon)' => [], ], '#default_value' => $config['autocomplete']['mode'] || 'result', + '#states' => [ + 'visible' => [ + ':input[id="autocomplete-is-enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $form['autocomplete']['autocomplete_mode_title_text'] = [ @@ -134,6 +172,13 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $autocomplete_mode ? $config['autocomplete'][$autocomplete_mode]['titleText'] : '', '#description' => $this ->t('The title text is shown above the results in the autocomplete drop down. (Default: "What are you interested in?" for Search Results mode and "What would you like to search for?" for Search Term mode.)'), + '#states' => [ + 'visible' => [ + ':input[id="autocomplete-is-enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $form['autocomplete']['autocomplete_mode_hide_directions'] = [ @@ -142,6 +187,13 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $autocomplete_mode ? $config['autocomplete'][$autocomplete_mode]['hideDirectionsText'] : 0, '#description' => $this ->t('Check this box to make hide the autocomplete keyboard usage directions in the results dropdown. For sites that want to maximize their accessibility UX for sighted keyboard users, we recommend leaving this unchecked. (Default: directions are visible)'), + '#states' => [ + 'visible' => [ + ':input[id="autocomplete-is-enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; return $form; From 0397e41f03817680397eb3348062d0156c5d1d12 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 14:41:53 -0400 Subject: [PATCH 36/59] root-198: support basic auth in ajax request --- js/search_api_federated_solr_autocomplete.js | 17 ++++- .../Block/FederatedSearchPageFormBlock.php | 62 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index a3f43e918..707233985 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -35,6 +35,7 @@ var defaultSettings = { isEnabled: false, appendWildcard: false, + userpass: '', numChars: 2, suggestionRows: 5, mode: 'result', @@ -130,10 +131,24 @@ // Replace the placeholder with the query value. var url = urlWithDefaultParams.replace('[val]', query); + // Set up basic auth if we need it. + var xhrFields = {}; + var headers = {}; + if (options.userpass) { + xhrFields = { + withCredentials: true + }; + headers = { + 'Authorization': 'Basic ' + options.userpass + }; + } + // Make the ajax request $.ajax({ + xhrFields: xhrFields, + headers: headers, url: url, - dataType: 'json' + dataType: 'json', }) // Currently we only support the response structure from Solr: // { diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index 1318de265..4e429c31b 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -114,6 +114,54 @@ public function blockForm($form, FormStateInterface $form_state) { ], ]; + $form['autocomplete']['basic_auth'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Basic Authentication'), + '#description' => $this->t('If your endpoint is protected by basic HTTP authentication, enter the login data here. This will be accessible to the client in an obscured, but non-secure method. It should, therefore, only provide read access to the index AND be different from that provided when configuring the server in Search API. The Password field is intentionally not obscured to emphasize this distinction.'), + '#states' => [ + 'visible' => [ + ':input[id="autocomplete-is-enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], + ]; + + $form['autocomplete']['basic_auth']['use_search_app_creds'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Use credentials provided to search app'), + '#default_value' => $config['autocomplete']['use_search_app_creds'], + '#attributes' => [ + 'id' => ['autocomplete-use-search-app-creds'], + ], + ]; + + $form['autocomplete']['basic_auth']['username'] = [ + '#type' => 'textfield', + '#title' => $this->t('Username'), + '#default_value' => $config['autocomplete']['username'], + '#states' => [ + 'visible' => [ + ':input[id="autocomplete-use-search-app-creds"]' => [ + 'checked' => FALSE, + ], + ], + ], + ]; + + $form['autocomplete']['basic_auth']['password'] = [ + '#type' => 'textfield', + '#title' => $this->t('Password'), + '#default_value' => $config['autocomplete']['password'], + '#states' => [ + 'visible' => [ + ':input[id="autocomplete-use-search-app-creds"]' => [ + 'checked' => FALSE, + ], + ], + ], + ]; + $form['autocomplete']['autocomplete_suggestion_rows'] = [ '#type' => 'number', '#title' => $this->t('Number of results'), @@ -234,8 +282,22 @@ public function blockSubmit($form, FormStateInterface $form_state) { $server_url .= '/select?q=[val]&wt=json'; $autocomplete_url = $autocomplete_url_value ? $autocomplete_url_value : $server_url; + // Default to the form values + $username = $values['autocomplete']['basic_auth']['username']; + $password = $values['autocomplete']['basic_auth']['password']; + $use_search_app_creds = $values['autocomplete']['basic_auth']['use_search_app_creds']; + // Add basic auth credentials + if ($use_search_app_creds) { + $username = $app_config->get('index.username'); + $password = $app_config->get('index.password'); + } + // Set the actual autocomplete config options. $this->configuration['autocomplete']['url'] = $autocomplete_url; + $this->configuration['autocomplete']['use_search_app_creds'] = $use_search_app_creds; + $this->configuration['autocomplete']['username'] = $username; + $this->configuration['autocomplete']['password'] = $password; + $this->configuration['autocomplete']['userpass'] = $username && $password ? base64_encode($username . ':' . $password) : ''; $this->configuration['autocomplete']['appendWildcard'] = $values['autocomplete']['autocomplete_is_append_wildcard']; $this->configuration['autocomplete']['suggestionRows'] = $values['autocomplete']['autocomplete_suggestion_rows']; $this->configuration['autocomplete']['numChars'] = $values['autocomplete']['autocomplete_num_chars']; From 5b9fe14b827c43f064958b4f62360d36436ed028 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 14:59:14 -0400 Subject: [PATCH 37/59] root-198: honor the default values set in JS --- .../Block/FederatedSearchPageFormBlock.php | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index 4e429c31b..3ceb80dc8 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -293,20 +293,37 @@ public function blockSubmit($form, FormStateInterface $form_state) { } // Set the actual autocomplete config options. - $this->configuration['autocomplete']['url'] = $autocomplete_url; - $this->configuration['autocomplete']['use_search_app_creds'] = $use_search_app_creds; - $this->configuration['autocomplete']['username'] = $username; - $this->configuration['autocomplete']['password'] = $password; - $this->configuration['autocomplete']['userpass'] = $username && $password ? base64_encode($username . ':' . $password) : ''; - $this->configuration['autocomplete']['appendWildcard'] = $values['autocomplete']['autocomplete_is_append_wildcard']; - $this->configuration['autocomplete']['suggestionRows'] = $values['autocomplete']['autocomplete_suggestion_rows']; - $this->configuration['autocomplete']['numChars'] = $values['autocomplete']['autocomplete_num_chars']; + $autocomplete['url'] = $autocomplete_url; + $autocomplete['use_search_app_creds'] = $use_search_app_creds; + if ($username) { + $autocomplete['username'] = $username; + } + if ($password) { + $autocomplete['password'] = $password; + } + if ($username && $password) { + $autocomplete['userpass'] = base64_encode($username . ':' . $password); + } + if ($values['autocomplete']['autocomplete_is_append_wildcard']) { + $autocomplete['appendWildcard'] = $values['autocomplete']['autocomplete_is_append_wildcard']; + } + if ($values['autocomplete']['autocomplete_suggestion_rows']) { + $autocomplete['suggestionRows'] = $values['autocomplete']['autocomplete_suggestion_rows']; + } + if ($values['autocomplete']['autocomplete_num_chars']) { + $autocomplete['numChars'] = $values['autocomplete']['autocomplete_num_chars']; + } if ($autocomplete_mode) { - $this->configuration['autocomplete']['mode'] = $autocomplete_mode; - $this->configuration['autocomplete'][$autocomplete_mode]['titleText'] = $values['autocomplete']['autocomplete_mode_title_text']; - $this->configuration['autocomplete'][$autocomplete_mode]['hideDirectionsText'] = $values['autocomplete']['autocomplete_mode_hide_directions']; + $autocomplete['mode'] = $autocomplete_mode; + if ($values['autocomplete']['autocomplete_mode_title_text']) { + $autocomplete[$autocomplete_mode]['titleText'] = $values['autocomplete']['autocomplete_mode_title_text']; + } + if ($values['autocomplete']['autocomplete_mode_hide_directions']) { + $autocomplete[$autocomplete_mode]['hideDirectionsText'] = $values['autocomplete']['autocomplete_mode_hide_directions']; + } } + + $this->configuration['autocomplete'] = $autocomplete; } } - } From 09b197b4031b562b80d21479cc88a6792d481952 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 15:03:35 -0400 Subject: [PATCH 38/59] root-198: Fix bug where autocomplete is enabled was not being written to config --- src/Plugin/Block/FederatedSearchPageFormBlock.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index 3ceb80dc8..7839d0561 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -255,7 +255,7 @@ public function blockSubmit($form, FormStateInterface $form_state) { $values = $form_state->getValues(); // Set autocomplete options. $autocomplete_is_enabled = $values['autocomplete']['autocomplete_is_enabled']; - $this->configuration['autocomplete']['isEnabled'] = $autocomplete_is_enabled; + $autocomplete['isEnabled'] = $autocomplete_is_enabled; // If enabled, set the autocomplete options. if ($autocomplete_is_enabled) { From 3077d38444b06a90e3aaab808015f81dbb54b6ab Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 15:59:32 -0400 Subject: [PATCH 39/59] root-198: replace multiple instances of [val] in autocomplete URL --- js/search_api_federated_solr_autocomplete.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index 707233985..d30a45ef3 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -129,7 +129,8 @@ } // Replace the placeholder with the query value. - var url = urlWithDefaultParams.replace('[val]', query); + var pattern = new RegExp(/(\[val\])/, "gi"); + var url = urlWithDefaultParams.replace(pattern, query); // Set up basic auth if we need it. var xhrFields = {}; From 6b193d0619076a6a0d865d53ae43539f0fdc3a75 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 16:25:14 -0400 Subject: [PATCH 40/59] root-198: allow autocomplete to be disabled --- src/Plugin/Block/FederatedSearchPageFormBlock.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index 7839d0561..a91757ffd 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -322,8 +322,8 @@ public function blockSubmit($form, FormStateInterface $form_state) { $autocomplete[$autocomplete_mode]['hideDirectionsText'] = $values['autocomplete']['autocomplete_mode_hide_directions']; } } - - $this->configuration['autocomplete'] = $autocomplete; } + + $this->configuration['autocomplete'] = $autocomplete; } } From 4646c4c15378dee50bedfaf39da549f30922e993 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 16:26:01 -0400 Subject: [PATCH 41/59] root-198: attach library + drupal settings to block if autocomplete enabled --- search_api_federated_solr.module | 9 --------- src/Form/FederatedSearchPageBlockForm.php | 2 -- src/Plugin/Block/FederatedSearchPageFormBlock.php | 12 ++++++++++++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/search_api_federated_solr.module b/search_api_federated_solr.module index 1a510dfab..de3bbe99d 100644 --- a/search_api_federated_solr.module +++ b/search_api_federated_solr.module @@ -148,12 +148,3 @@ function search_api_federated_solr_form_search_api_federated_solr_search_app_set } } - -/** - * implements hook_preprocess_block_BASE_BLOCK_ID - */ -function search_api_federated_solr_preprocess_block__federated_search_page_form_block(&$variables) { - $variables['#attached']['drupalSettings']['federatedSearchPageFormBlock'] = [ - 'autocomplete' => $variables['elements']['#configuration']['autocomplete'], - ]; -} diff --git a/src/Form/FederatedSearchPageBlockForm.php b/src/Form/FederatedSearchPageBlockForm.php index 398eee96b..3fc5abe65 100644 --- a/src/Form/FederatedSearchPageBlockForm.php +++ b/src/Form/FederatedSearchPageBlockForm.php @@ -72,8 +72,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { $renderer->addCacheableDependency($form, $index_config); } - $form['#attached']['library'][] = 'search_api_federated_solr/search_form_autocomplete'; - $form['#action'] = $this->getUrlGenerator()->generateFromRoute('search_api_federated_solr.search'); $form['#method'] = 'get'; diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index a91757ffd..20fef14ea 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -21,10 +21,22 @@ class FederatedSearchPageFormBlock extends BlockBase implements BlockPluginInter * {@inheritdoc} */ public function build() { + $config = $this->getConfiguration(); + $build = []; $build['container']['form'] = \Drupal::formBuilder()->getForm('Drupal\search_api_federated_solr\Form\FederatedSearchPageBlockForm'); + // If autocomplete is enabled for this block, attach the js library. + if (array_key_exists('autocomplete',$config) + && array_key_exists('isEnabled', $config['autocomplete']) + && $config['autocomplete']['isEnabled'] === 1) { + $build['#attached']['library'][] = 'search_api_federated_solr/search_form_autocomplete'; + $build['#attached']['drupalSettings']['federatedSearchPageFormBlock'] = [ + 'autocomplete' => $config['autocomplete'], + ]; + } + return $build; } From b5590c2731bb92b3444d27f298c9b760b044272d Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 16:30:11 -0400 Subject: [PATCH 42/59] root-198: provide better namespace for autocomplete in drupalSettings --- js/search_api_federated_solr_autocomplete.js | 9 +++++---- src/Plugin/Block/FederatedSearchPageFormBlock.php | 6 ++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index d30a45ef3..b03f4b0b8 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -25,9 +25,10 @@ .once('search-api-federated-solr-autocomplete-search') .each(function () { // Halt execution if we don't have the required config. - if (!Object.hasOwnProperty.call(drupalSettings, 'federatedSearchPageFormBlock') - || !Object.hasOwnProperty.call(drupalSettings.federatedSearchPageFormBlock, 'autocomplete') - || !Object.hasOwnProperty.call(drupalSettings.federatedSearchPageFormBlock.autocomplete, 'url')) { + if (!Object.hasOwnProperty.call(drupalSettings, 'searchApiFederatedSolr') + || !Object.hasOwnProperty.call(drupalSettings.searchApiFederatedSolr, 'block') + || !Object.hasOwnProperty.call(drupalSettings.searchApiFederatedSolr.block, 'autocomplete') + || !Object.hasOwnProperty.call(drupalSettings.searchApiFederatedSolr.block.autocomplete, 'url')) { return; } @@ -45,7 +46,7 @@ } }; // Get passed in config from block config. - var config = drupalSettings.federatedSearchPageFormBlock.autocomplete; + var config = drupalSettings.searchApiFederatedSolr.block.autocomplete; // Merge defaults with passed in config. var options = Object.assign({}, defaultSettings, config); diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index 20fef14ea..20a1c5702 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -32,8 +32,10 @@ public function build() { && array_key_exists('isEnabled', $config['autocomplete']) && $config['autocomplete']['isEnabled'] === 1) { $build['#attached']['library'][] = 'search_api_federated_solr/search_form_autocomplete'; - $build['#attached']['drupalSettings']['federatedSearchPageFormBlock'] = [ - 'autocomplete' => $config['autocomplete'], + $build['#attached']['drupalSettings']['searchApiFederatedSolr'] = [ + 'block' => [ + 'autocomplete' => $config['autocomplete'], + ], ]; } From ea1d37fdc93354257278c1ef2b62ff7e7c7ac1e9 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Mon, 11 Mar 2019 16:32:06 -0400 Subject: [PATCH 43/59] root-198: remove contextual filter display from example view --- ...federated_solr_block_form_autocomplete.yml | 135 ------------------ 1 file changed, 135 deletions(-) diff --git a/config/optional/views.view.search_api_federated_solr_block_form_autocomplete.yml b/config/optional/views.view.search_api_federated_solr_block_form_autocomplete.yml index f723cbc57..22f6a4a87 100644 --- a/config/optional/views.view.search_api_federated_solr_block_form_autocomplete.yml +++ b/config/optional/views.view.search_api_federated_solr_block_form_autocomplete.yml @@ -347,138 +347,3 @@ display: - 'user.node_grants:view' - user.permissions tags: { } - rest_export_2: - display_plugin: rest_export - id: rest_export_2 - display_title: 'REST export - contextual' - position: 2 - display_options: - display_extenders: { } - display_description: '' - path: search-api-federated-solr-block-form-autocomplete/contextual-filter/% - 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: contains - value: '' - group: 1 - exposed: false - expose: - operator_id: '' - label: '' - description: '' - use_operator: false - operator: '' - identifier: '' - required: false - remember: false - multiple: false - remember_roles: - authenticated: authenticated - 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 - defaults: - filters: false - filter_groups: false - arguments: false - filter_groups: - operator: AND - groups: - 1: AND - arguments: - title: - id: title - table: node_field_data - field: title - relationship: none - group_type: group - admin_label: '' - default_action: default - exception: - value: all - title_enable: false - title: All - title_enable: false - title: '' - default_argument_type: raw - default_argument_options: - index: 1 - use_alias: false - default_argument_skip_url: false - summary_options: - base_path: '' - count: true - items_per_page: 25 - override: false - summary: - sort_order: asc - number_of_records: 0 - format: default_summary - specify_validation: true - validate: - type: 'entity:field_config' - fail: ignore - validate_options: - operation: view - multiple: 0 - access: false - bundles: null - glossary: false - limit: 0 - case: none - path_case: lower - transform_dash: true - break_phrase: false - entity_type: node - entity_field: title - plugin_id: string - row: - type: data_field - options: - field_options: - title: - alias: ss_federated_title - raw_output: false - view_node: - alias: ss_url - raw_output: false - cache_metadata: - max-age: -1 - contexts: - - 'languages:language_content' - - 'languages:language_interface' - - request_format - - url - - 'user.node_grants:view' - - user.permissions - tags: { } From 5269923de949babbb23d26e2f78c2d0718265352 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 07:05:48 -0400 Subject: [PATCH 44/59] root-198: add the js trigger class to the block based on config --- js/search_api_federated_solr_autocomplete.js | 2 +- src/Form/FederatedSearchPageBlockForm.php | 1 - src/Plugin/Block/FederatedSearchPageFormBlock.php | 4 ++++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index b03f4b0b8..de261edcb 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -21,7 +21,7 @@ attach: function (context, settings) { // Find our fields with autocomplete settings $(context) - .find('#edit-search[data-search-api-autocomplete-search]') + .find('.js-search-api-federated-solr-block-form-autocomplete #edit-search') .once('search-api-federated-solr-autocomplete-search') .each(function () { // Halt execution if we don't have the required config. diff --git a/src/Form/FederatedSearchPageBlockForm.php b/src/Form/FederatedSearchPageBlockForm.php index 3fc5abe65..e0d1c3bef 100644 --- a/src/Form/FederatedSearchPageBlockForm.php +++ b/src/Form/FederatedSearchPageBlockForm.php @@ -42,7 +42,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { 'title' => $this->t('Enter the terms you wish to search for.'), 'placeholder' => 'Search', 'autocomplete' => "off", - 'data-search-api-autocomplete-search' => "quick_search", ], '#prefix' => '
    ', ]; diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index 20a1c5702..e9eeafc33 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -31,12 +31,16 @@ public function build() { if (array_key_exists('autocomplete',$config) && array_key_exists('isEnabled', $config['autocomplete']) && $config['autocomplete']['isEnabled'] === 1) { + // Attach autocomplete JS library. $build['#attached']['library'][] = 'search_api_federated_solr/search_form_autocomplete'; + // Write the block config to Drupal settings. $build['#attached']['drupalSettings']['searchApiFederatedSolr'] = [ 'block' => [ 'autocomplete' => $config['autocomplete'], ], ]; + // Add the js trigger class to the block. + $build['#attributes']['class'][] = 'js-search-api-federated-solr-block-form-autocomplete'; } return $build; From b25d2c738eadc7c4539583e5e28bcfd8bf2c3c2b Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 07:10:54 -0400 Subject: [PATCH 45/59] root-198: add comment to clarify autocomplete html attribute --- src/Form/FederatedSearchPageBlockForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Form/FederatedSearchPageBlockForm.php b/src/Form/FederatedSearchPageBlockForm.php index e0d1c3bef..87327e7c4 100644 --- a/src/Form/FederatedSearchPageBlockForm.php +++ b/src/Form/FederatedSearchPageBlockForm.php @@ -41,7 +41,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#attributes' => [ 'title' => $this->t('Enter the terms you wish to search for.'), 'placeholder' => 'Search', - 'autocomplete' => "off", + 'autocomplete' => "off", // refers to html attribute, not our custom autocomplete. ], '#prefix' => '
    ', ]; From 6009a046fd56f714f921ca0381aabca3e1e389b3 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 07:17:34 -0400 Subject: [PATCH 46/59] root-198: remove uuid from autocomplete view --- ...ws.view.search_api_federated_solr_block_form_autocomplete.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/config/optional/views.view.search_api_federated_solr_block_form_autocomplete.yml b/config/optional/views.view.search_api_federated_solr_block_form_autocomplete.yml index 22f6a4a87..bd9bd5654 100644 --- a/config/optional/views.view.search_api_federated_solr_block_form_autocomplete.yml +++ b/config/optional/views.view.search_api_federated_solr_block_form_autocomplete.yml @@ -1,4 +1,3 @@ -uuid: f17b010f-c4f0-497f-802e-047b08b4c004 langcode: en status: true dependencies: From 4c2f61c05c4d8511a37c0dc68b739bc91925bcd6 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 08:06:28 -0400 Subject: [PATCH 47/59] root-198: update search app autocomplete url docs + other misc fixes - make autocomplete enable checkbox label clickable - open/close autocomplete details based on dis/enabled - make enable checkbox trigger for other form values, not whole details --- ...hApiFederatedSolrSearchAppSettingsForm.php | 88 ++++++++++++++----- .../Block/FederatedSearchPageFormBlock.php | 30 +++---- 2 files changed, 80 insertions(+), 38 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index dbf18f039..7320db21b 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -120,17 +120,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The title that will live in the header tag of the search results page (leave empty to hide completely).'), ]; - $form['setup']['autocomplete_is_enabled'] = [ - '#type' => 'checkbox', - '#title' => '' . $this->t('Enable autocomplete for the search results page search form') . '', - '#default_value' => $config->get('autocomplete.isEnabled'), - '#description' => $this - ->t('Checking this will expose more configuration options for autocomplete behavior for the search form on the Search Results page at the end of this form.'), - '#attributes' => [ - 'id' => ['autocomplete-is-enabled'], - ], - ]; - $form['setup']['search_index'] = [ '#type' => 'select', '#title' => $this->t('Search API index'), @@ -380,7 +369,26 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#type' => 'details', '#title' => $this->t('Search Results Page > Search Form > Autocomplete'), '#description' => $this->t('These options apply to the autocomplete functionality on the search for which appears above the search results on the search results page. Configure your placed Federated Search Page Form block to add autocomplete to that form.'), - '#open' => FALSE, + '#open' => $config->get('autocomplete.isEnabled'), + ]; + + $form['autocomplete']['autocomplete_is_enabled'] = [ + '#type' => 'checkbox', + '#title' => '' . $this->t('Enable autocomplete for the search results page search form') . '', + '#default_value' => $config->get('autocomplete.isEnabled'), + '#description' => $this + ->t('Check this box to enable autocomplete on the search results page search form and to expose more configuration options below.'), + '#attributes' => [ + 'name' => ['autocomplete_is_enabled'], + ], + ]; + + $form['autocomplete']['autocomplete_is_append_wildcard'] = [ + '#type' => 'checkbox', + '#title' => '' . $this->t('Append a wildcard \'*\' to support partial text search') . '', + '#default_value' => $config->get('autocomplete.appendWildcard'), + '#description' => $this + ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), '#states' => [ 'visible' => [ ':input[name="autocomplete_is_enabled"]' => [ @@ -392,20 +400,19 @@ public function buildForm(array $form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_url'] = [ '#type' => 'url', - '#title' => $this->t('Endpoint URL'), + '#title' => $this->t('Solr Endpoint URL'), '#default_value' => $config->get('autocomplete.url'), '#maxlength' => 2048, '#size' => 50, '#description' => $this - ->t('The URL where requests for autocomplete queries should be made. Defaults to the url of the select Request Handler on the server of the selected Search API index.
    Supports absolute url pattern to any endpoint which returns the expected autocomplete result structure.'), - ]; - - $form['autocomplete']['autocomplete_is_append_wildcard'] = [ - '#type' => 'checkbox', - '#title' => '' . $this->t('Append a wildcard \'*\' to support partial text search') . '', - '#default_value' => $config->get('autocomplete.appendWildcard'), - '#description' => $this - ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), + ->t('The URL where requests for autocomplete queries should be made. (Default: the url of the select Request Handler on the server of the selected Search API index.)
    • Supports an absolute url pattern to any other Request Handler for an index on your solr server
    • The value of the main search field will be appended to the url as the main query param (i.e. ?q=[value of the search field, wildcard appended if enabled])
    • Any facet/filter default values set for the search app will automatically be appended (i.e. &sm_site_name=[value of the site name for the index])
    • The format param &wt=json will automatically be appended
    • Include any other necessary url params corresponding to query parameters.
    • '), + '#states' => [ + 'visible' => [ + ':input[name="autocomplete_is_enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $form['autocomplete']['autocomplete_suggestion_rows'] = [ @@ -414,6 +421,13 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $config->get('autocomplete.suggestionRows'), '#description' => $this ->t('The max number of results to render in the autocomplete results dropdown. (Default: 5)'), + '#states' => [ + 'visible' => [ + ':input[name="autocomplete_is_enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $form['autocomplete']['autocomplete_num_chars'] = [ @@ -422,6 +436,13 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $config->get('autocomplete.numChars'), '#description' => $this ->t('Autocomplete query will be executed after a user types this many characters in the search query field. (Default: 2)'), + '#states' => [ + 'visible' => [ + ':input[name="autocomplete_is_enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $autocomplete_mode = $config->get('autocomplete.mode'); @@ -430,7 +451,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_mode'] = [ '#type' => 'select', - '#title' => $this->t('Result mode'), + '#title' => $this->t('Autocomplete mode'), '#description' => $this->t('Type of results the autocomplete response returns: search results (default) or search terms.'), '#options' => [ 'result' => $this @@ -438,6 +459,13 @@ public function buildForm(array $form, FormStateInterface $form_state) { 'Search terms (coming soon)' => [], ], '#default_value' => $autocomplete_mode || 'result', + '#states' => [ + 'visible' => [ + ':input[name="autocomplete_is_enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $form['autocomplete']['autocomplete_mode_title_text'] = [ @@ -447,6 +475,13 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $autocomplete_mode ? $config->get($title_text_config_key) : '', '#description' => $this ->t('The title text is shown above the results in the autocomplete drop down. (Default: "What are you interested in?" for Search Results mode and "What would you like to search for?" for Search Term mode.)'), + '#states' => [ + 'visible' => [ + ':input[name="autocomplete_is_enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $form['autocomplete']['autocomplete_mode_hide_directions'] = [ @@ -455,6 +490,13 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $autocomplete_mode ? $config->get($hide_directions_text_config_key) : 0, '#description' => $this ->t('Check this box to make hide the autocomplete keyboard usage directions in the results dropdown. For sites that want to maximize their accessibility UX for sighted keyboard users, we recommend leaving this unchecked. (Default: directions are visible)'), + '#states' => [ + 'visible' => [ + ':input[name="autocomplete_is_enabled"]' => [ + 'checked' => TRUE, + ], + ], + ], ]; $form['#cache'] = ['max-age' => 0]; diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index e9eeafc33..f44dbe086 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -84,9 +84,9 @@ public function blockForm($form, FormStateInterface $form_state) { '#title' => '' . $this->t('Enable autocomplete for the search results page search form') . '', '#default_value' => $config['autocomplete']['isEnabled'], '#description' => $this - ->t('Check this box to enable autocomplete for the text input on the search form rendered in this block.'), + ->t('Check this box to enable autocomplete on the federated search block search form and to expose more configuration options below.'), '#attributes' => [ - 'id' => ['autocomplete-is-enabled'], + 'name' => ['autocomplete_is_enabled'], ], ]; @@ -98,7 +98,7 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), '#states' => [ 'visible' => [ - ':input[id="autocomplete-is-enabled"]' => [ + ':input[name="autocomplete_is_enabled"]' => [ 'checked' => TRUE, ], ], @@ -112,7 +112,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#maxlength' => 2048, '#size' => 50, '#description' => $this - ->t('The URL where requests for autocomplete queries should be made. (Default: the url of the select Request Handler on the server of the selected Search API index.)
      • Supports absolute url pattern to any endpoint which returns the expected response structure: + ->t('The URL where requests for autocomplete queries should be made. (Default: the url of the select Request Handler on the server of the selected Search API index.)
        • Supports absolute url pattern to any endpoint which returns the expected response structure:
          {
             response: {
              docs: [
          @@ -125,7 +125,7 @@ public function blockForm($form, FormStateInterface $form_state) {
           }
        • Include [val] in the URL to indicate where you would like the form value to be inserted: http://d8.fs-demo.local/search-api-federated-solr-block-form-autocomplete/search-view?title=[val]&_format=json
        • Any facet/filter default values set for the search app will automatically be appended (i.e. &sm_site_name=[value of the site name for the index])
        • Include any other necessary url params (like &_format=json if you are using a Views Rest Export or &wt=json if you are using a different Request Handler on your Solr index.
        • '), '#states' => [ 'visible' => [ - ':input[id="autocomplete-is-enabled"]' => [ + ':input[name="autocomplete_is_enabled"]' => [ 'checked' => TRUE, ], ], @@ -138,7 +138,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#description' => $this->t('If your endpoint is protected by basic HTTP authentication, enter the login data here. This will be accessible to the client in an obscured, but non-secure method. It should, therefore, only provide read access to the index AND be different from that provided when configuring the server in Search API. The Password field is intentionally not obscured to emphasize this distinction.'), '#states' => [ 'visible' => [ - ':input[id="autocomplete-is-enabled"]' => [ + ':input[name="autocomplete_is_enabled"]' => [ 'checked' => TRUE, ], ], @@ -150,7 +150,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#title' => $this->t('Use credentials provided to search app'), '#default_value' => $config['autocomplete']['use_search_app_creds'], '#attributes' => [ - 'id' => ['autocomplete-use-search-app-creds'], + 'name' => ['autocomplete_use_search_app_creds'], ], ]; @@ -160,7 +160,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $config['autocomplete']['username'], '#states' => [ 'visible' => [ - ':input[id="autocomplete-use-search-app-creds"]' => [ + ':input[name="autocomplete_use_search_app_creds"]' => [ 'checked' => FALSE, ], ], @@ -173,7 +173,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $config['autocomplete']['password'], '#states' => [ 'visible' => [ - ':input[id="autocomplete-use-search-app-creds"]' => [ + ':input[name="autocomplete_use_search_app_creds"]' => [ 'checked' => FALSE, ], ], @@ -188,7 +188,7 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('The max number of results to render in the autocomplete results dropdown. (Default: 5)'), '#states' => [ 'visible' => [ - ':input[id="autocomplete-is-enabled"]' => [ + ':input[name="autocomplete_is_enabled"]' => [ 'checked' => TRUE, ], ], @@ -203,7 +203,7 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('Autocomplete query will be executed after a user types this many characters in the search query field. (Default: 2)'), '#states' => [ 'visible' => [ - ':input[id="autocomplete-is-enabled"]' => [ + ':input[name="autocomplete_is_enabled"]' => [ 'checked' => TRUE, ], ], @@ -214,7 +214,7 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_mode'] = [ '#type' => 'select', - '#title' => $this->t('Result mode'), + '#title' => $this->t('Autocomplete mode'), '#description' => $this->t('Type of results the autocomplete response returns: search results (default) or search terms.'), '#options' => [ 'result' => $this @@ -224,7 +224,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $config['autocomplete']['mode'] || 'result', '#states' => [ 'visible' => [ - ':input[id="autocomplete-is-enabled"]' => [ + ':input[name="autocomplete_is_enabled"]' => [ 'checked' => TRUE, ], ], @@ -240,7 +240,7 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('The title text is shown above the results in the autocomplete drop down. (Default: "What are you interested in?" for Search Results mode and "What would you like to search for?" for Search Term mode.)'), '#states' => [ 'visible' => [ - ':input[id="autocomplete-is-enabled"]' => [ + ':input[name="autocomplete_is_enabled"]' => [ 'checked' => TRUE, ], ], @@ -255,7 +255,7 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('Check this box to make hide the autocomplete keyboard usage directions in the results dropdown. For sites that want to maximize their accessibility UX for sighted keyboard users, we recommend leaving this unchecked. (Default: directions are visible)'), '#states' => [ 'visible' => [ - ':input[id="autocomplete-is-enabled"]' => [ + ':input[name="autocomplete_is_enabled"]' => [ 'checked' => TRUE, ], ], From cd01037b6a4881a0ac7ac9ceea55d2325e4b14f8 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 08:57:29 -0400 Subject: [PATCH 48/59] root-198: emit event when a search block autocomplete option is selected --- js/search_api_federated_solr_autocomplete.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index de261edcb..b3d277fd1 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -317,6 +317,13 @@ function selectOption(highlighted, href) { if (highlighted && href) { // @todo add logic for non-link suggestions + // Emit an event whose data can be used to write to analytics, etc. + $(document).trigger("SearchApiFederatedSolr::block::autocomplete::selection", [{ + referrer: $(location).attr('href'), + target: href, + term: $input.val() + }]); + // Redirect to the selected link. $(location).attr("href", href); } else { From 02b5816061ac7b500d950f5a95c15eb8bcad7630 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 09:16:48 -0400 Subject: [PATCH 49/59] root-198: add custom event emission to autocomplete link click --- js/search_api_federated_solr_autocomplete.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/js/search_api_federated_solr_autocomplete.js b/js/search_api_federated_solr_autocomplete.js index b3d277fd1..ea29b3a1e 100644 --- a/js/search_api_federated_solr_autocomplete.js +++ b/js/search_api_federated_solr_autocomplete.js @@ -201,6 +201,15 @@ counter = counter + 1; }); + // On link click, emit an event whose data can be used to write to analytics, etc. + $('.autocomplete-suggestion__link').on('click', function (e) { + $(document).trigger("SearchApiFederatedSolr::block::autocomplete::selection", [{ + referrer: $(location).attr('href'), + target: $(this).attr('href'), + term: $input.val() + }]); + }); + // Announce the number of suggestions. var number = $results.children('[role="option"]').length; if (number >= 1) { From af1be88874d044e910d282819d90de0a0b5c8ad7 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 13:14:11 -0400 Subject: [PATCH 50/59] root-198: use custom data attr (vs name) as hook to show/hide form elements Fixes bug where all of the sudden the block config enable autocomplete value was not being honored --- ...hApiFederatedSolrSearchAppSettingsForm.php | 16 ++++++------- .../Block/FederatedSearchPageFormBlock.php | 24 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index 7320db21b..e531804cf 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -379,7 +379,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#description' => $this ->t('Check this box to enable autocomplete on the search results page search form and to expose more configuration options below.'), '#attributes' => [ - 'name' => ['autocomplete_is_enabled'], + 'data-autocomplete-enabler' => TRUE, ], ]; @@ -391,7 +391,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enabler]' => [ 'checked' => TRUE, ], ], @@ -408,7 +408,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The URL where requests for autocomplete queries should be made. (Default: the url of the select Request Handler on the server of the selected Search API index.)
          • Supports an absolute url pattern to any other Request Handler for an index on your solr server
          • The value of the main search field will be appended to the url as the main query param (i.e. ?q=[value of the search field, wildcard appended if enabled])
          • Any facet/filter default values set for the search app will automatically be appended (i.e. &sm_site_name=[value of the site name for the index])
          • The format param &wt=json will automatically be appended
          • Include any other necessary url params corresponding to query parameters.
          • '), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enabler]' => [ 'checked' => TRUE, ], ], @@ -423,7 +423,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The max number of results to render in the autocomplete results dropdown. (Default: 5)'), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enabler]' => [ 'checked' => TRUE, ], ], @@ -438,7 +438,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('Autocomplete query will be executed after a user types this many characters in the search query field. (Default: 2)'), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enabler]' => [ 'checked' => TRUE, ], ], @@ -461,7 +461,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $autocomplete_mode || 'result', '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enabler]' => [ 'checked' => TRUE, ], ], @@ -477,7 +477,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('The title text is shown above the results in the autocomplete drop down. (Default: "What are you interested in?" for Search Results mode and "What would you like to search for?" for Search Term mode.)'), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enabler]' => [ 'checked' => TRUE, ], ], @@ -492,7 +492,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ->t('Check this box to make hide the autocomplete keyboard usage directions in the results dropdown. For sites that want to maximize their accessibility UX for sighted keyboard users, we recommend leaving this unchecked. (Default: directions are visible)'), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enabler]' => [ 'checked' => TRUE, ], ], diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index f44dbe086..de94dcbd2 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -86,7 +86,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#description' => $this ->t('Check this box to enable autocomplete on the federated search block search form and to expose more configuration options below.'), '#attributes' => [ - 'name' => ['autocomplete_is_enabled'], + 'data-autocomplete-enable' => TRUE, ], ]; @@ -98,7 +98,7 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enable]' => [ 'checked' => TRUE, ], ], @@ -125,7 +125,7 @@ public function blockForm($form, FormStateInterface $form_state) { }
          • Include [val] in the URL to indicate where you would like the form value to be inserted: http://d8.fs-demo.local/search-api-federated-solr-block-form-autocomplete/search-view?title=[val]&_format=json
          • Any facet/filter default values set for the search app will automatically be appended (i.e. &sm_site_name=[value of the site name for the index])
          • Include any other necessary url params (like &_format=json if you are using a Views Rest Export or &wt=json if you are using a different Request Handler on your Solr index.
          • '), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enable]' => [ 'checked' => TRUE, ], ], @@ -138,7 +138,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#description' => $this->t('If your endpoint is protected by basic HTTP authentication, enter the login data here. This will be accessible to the client in an obscured, but non-secure method. It should, therefore, only provide read access to the index AND be different from that provided when configuring the server in Search API. The Password field is intentionally not obscured to emphasize this distinction.'), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enable]' => [ 'checked' => TRUE, ], ], @@ -150,7 +150,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#title' => $this->t('Use credentials provided to search app'), '#default_value' => $config['autocomplete']['use_search_app_creds'], '#attributes' => [ - 'name' => ['autocomplete_use_search_app_creds'], + 'data-autocomplete-use-search-app-creds' => TRUE, ], ]; @@ -160,7 +160,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $config['autocomplete']['username'], '#states' => [ 'visible' => [ - ':input[name="autocomplete_use_search_app_creds"]' => [ + ':input[data-autocomplete-use-search-app-creds]' => [ 'checked' => FALSE, ], ], @@ -173,7 +173,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $config['autocomplete']['password'], '#states' => [ 'visible' => [ - ':input[name="autocomplete_use_search_app_creds"]' => [ + ':input[data-autocomplete-use-search-app-creds]' => [ 'checked' => FALSE, ], ], @@ -188,7 +188,7 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('The max number of results to render in the autocomplete results dropdown. (Default: 5)'), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enable]' => [ 'checked' => TRUE, ], ], @@ -203,7 +203,7 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('Autocomplete query will be executed after a user types this many characters in the search query field. (Default: 2)'), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enable]' => [ 'checked' => TRUE, ], ], @@ -224,7 +224,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#default_value' => $config['autocomplete']['mode'] || 'result', '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enable]' => [ 'checked' => TRUE, ], ], @@ -240,7 +240,7 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('The title text is shown above the results in the autocomplete drop down. (Default: "What are you interested in?" for Search Results mode and "What would you like to search for?" for Search Term mode.)'), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enable]' => [ 'checked' => TRUE, ], ], @@ -255,7 +255,7 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('Check this box to make hide the autocomplete keyboard usage directions in the results dropdown. For sites that want to maximize their accessibility UX for sighted keyboard users, we recommend leaving this unchecked. (Default: directions are visible)'), '#states' => [ 'visible' => [ - ':input[name="autocomplete_is_enabled"]' => [ + ':input[data-autocomplete-enable]' => [ 'checked' => TRUE, ], ], From c129ae9d9e4cda032fdc93cf1c7e69bf59887fcc Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 13:29:15 -0400 Subject: [PATCH 51/59] root-198: set install, schema, + default values for autocomplete config --- ...api_federated_solr.search_app.settings.yml | 10 ++- .../search_api_federated_solr.schema.yml | 63 +++++++++++++++++++ search_api_federated_solr.install | 34 ++++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 search_api_federated_solr.install diff --git a/config/install/search_api_federated_solr.search_app.settings.yml b/config/install/search_api_federated_solr.search_app.settings.yml index 2a1759270..908e1e76e 100644 --- a/config/install/search_api_federated_solr.search_app.settings.yml +++ b/config/install/search_api_federated_solr.search_app.settings.yml @@ -1,5 +1,13 @@ autocomplete: - is_enabled: false + isEnabled: 0 + url: '' + appendWildcard: 0 + suggestionRows: '' + numChars: '' + mode: result + result: + titleText: '' + hideDirectionsText: 0 facet: site_name: set_default: false diff --git a/config/schema/search_api_federated_solr.schema.yml b/config/schema/search_api_federated_solr.schema.yml index 905e7ca55..244a9c3ea 100644 --- a/config/schema/search_api_federated_solr.schema.yml +++ b/config/schema/search_api_federated_solr.schema.yml @@ -16,12 +16,45 @@ search_api_federated_solr.search_app.settings: set_default: type: boolean label: 'When true, only search results from this site will be shown.' + is_hidden: + type: boolean + label: 'When true, this facet option is hidden in the UI.' + federated_type: + type: mapping + mapping: + is_hidden: + type: boolean + label: 'When true, this facet option is hidden in the UI.' + federated_terms: + type: mapping + mapping: + is_hidden: + type: boolean + label: 'When true, this facet option is hidden in the UI.' + filter: + type: mapping + mapping: + federated_date: + type: mapping + mapping: + is_hidden: + type: boolean + label: 'When true, this filter option is hidden in the UI.' index: type: mapping mapping: id: type: string label: 'Defines which search_api index and server the search app should use.' + has_federated_date_property: + type: boolean + label: 'Has federated date flag.' + has_federated_terms_property: + type: boolean + label: 'Has federated terms flag.' + has_federated_type_property: + type: boolean + label: 'Has federated type flag.' has_site_name_property: type: boolean label: 'Has site name flag.' @@ -55,3 +88,33 @@ search_api_federated_solr.search_app.settings: buttons: type: integer label: 'The max number of numbered pagination buttons to show at a given time.' + autocomplete: + type: mapping + mapping: + isEnabled: + type: integer + label: 'When true, autocomplete is enabled for the search app.' + url: + type: string + label: 'The autocomplete endpoint url to be used.' + appendWildcard: + type: integer + label: 'When checked, the query will have a wildcard appended.' + suggestionRows: + type: string + label: 'The number of results to return from the autocomplete request.' + numChars: + type: string + label: 'The number of characters, after which the autocomplete request should be made.' + mode: + type: string + label: 'The type of results returned by the autocomplete request: results or terms.' + result: + type: mapping + mapping: + titleText: + type: string + label: 'The title text for the autocomplete results.' + hideDirectionsText: + type: integer + label: 'When true, the keyboard directions text will be hidden from autocomplete dropdown.' diff --git a/search_api_federated_solr.install b/search_api_federated_solr.install new file mode 100644 index 000000000..46d991ac0 --- /dev/null +++ b/search_api_federated_solr.install @@ -0,0 +1,34 @@ +getEditable('search_api_federated_solr.search_app.settings'); + + // Set the autocomplete config defaults. + $config->set('autocomplete.isEnabled', 0); + $config->set('autocomplete.url', ''); + $config->set('autocomplete.appendWildcard', 0); + $config->set('autocomplete.suggestionRows', ''); + $config->set('autocomplete.mode', 'result'); + $config->set('autocomplete.result.titleText', ''); + $config->set('autocomplete.result.hideDirectionsText', 0); + + // Set the hidden facet/filter defaults. + $config->set('facet.site_name.is_hidden', false); + $config->set('facet.federated_terms.is_hidden', false); + $config->set('facet.federated_type.is_hidden', false); + $config->set('filter.federated_date.is_hidden', false); + + // Set the index defaults for property flags. + $config->set('index.has_federated_date_property', false); + $config->set('index.has_federated_term_property', false); + $config->set('index.has_federated_type_property', false); + + $config->save(TRUE); +} From 7060038d643239b3161fc67a414778619df3d17d Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 14:51:25 -0400 Subject: [PATCH 52/59] root-198: fix readme, make numbered list item links render as links --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7a69b406f..d681e6991 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Primary features of the module include: * Indexing of multiple, independent Drupal sites into a single index * Optional filtering of search results by site * Standard presentation of search results on all sites - * A standard search block for use on all sites + * A standard search block for use on all sites with optional configurable type-ahead search results * Customizable presentation using a single CSS file ### How does it work? @@ -60,27 +60,27 @@ CONFIGURATION On each site included in the federated search, you will need to: - 1. Install this module and its dependencies - 2. Configure a Search API server to connect to the shared Solr index - 3. Configure a Search API index according to the [required schema documentation](docs/federated_schema.md) - 4. Index the content for the site using Search API +1. Install this module and its dependencies +1. Configure a Search API server to connect to the shared Solr index +1. Configure a Search API index according to the [required schema documentation](docs/federated_schema.md) +1. Index the content for the site using Search API Once each site is configured, you may begin to index content. In order to display results from the Solr index: - 1. Configure the application route and settings at `/admin/config/search-api-federated-solr/search-app/settings` - 2. Set permissions for `Use Federated Search` and `Administer Federated Search` for the proper roles. - 3. Optional: [Theme the ReactJS search app](docs/theme.md) - 4. Optional: Add the federated search page form block to your site theme +1. Configure the application route and settings at `/admin/config/search-api-federated-solr/search-app/settings` +1. Set permissions for `Use Federated Search` and `Administer Federated Search` for the proper roles. +1. Optional: [Theme the ReactJS search app](docs/theme.md) +1. Optional: Add the federated search page form block to your site theme ### Updating the bundled React application When changes to [federated-search-react](https://github.com/palantirnet/federated-search-react/) are made they'll need to be pulled into this module. To do so: - 1. [Publish a release](https://github.com/palantirnet/federated-search-react#publishing-releases) of Federated Search React. - 2. Update `search_api_federated_solr.libraries.yml` to reference the new release. Note: You'll need to edit the version number and the hash of both the CSS and JS files. +1. [Publish a release](https://github.com/palantirnet/federated-search-react#publishing-releases) of Federated Search React. +1. Update `search_api_federated_solr.libraries.yml` to reference the new release. Note: You'll need to edit the version number and the hash of both the CSS and JS files. ### More information From 99ce7456a19ba1a6f200407b03b62a28dd9cda68 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 14:52:05 -0400 Subject: [PATCH 53/59] root-198: fix readme: point required schema docs to d.o guide --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d681e6991..6a436e6dd 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ On each site included in the federated search, you will need to: 1. Install this module and its dependencies 1. Configure a Search API server to connect to the shared Solr index -1. Configure a Search API index according to the [required schema documentation](docs/federated_schema.md) +1. Configure a Search API index according to the [required schema documentation](https://www.drupal.org/docs/8/modules/search-api-federated-solr/federated-search-schema) 1. Index the content for the site using Search API Once each site is configured, you may begin to index content. From 8787fc8dc1c2a0e0f4e2afb16c17a6b6cfd0e057 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 15:04:53 -0400 Subject: [PATCH 54/59] root-198: update readme: include steps to set default query fields, block config --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a436e6dd..8d8c55dbf 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ On each site included in the federated search, you will need to: 1. Install this module and its dependencies 1. Configure a Search API server to connect to the shared Solr index +1. Configure that Search API server to set default query fields for your default [Request Handler](https://lucene.apache.org/solr/guide/6_6/requesthandlers-and-searchcomponents-in-solrconfig.html#RequestHandlersandSearchComponentsinSolrConfig-SearchHandlers). (See [example](https://github.com/palantirnet/federated-search-demo/blob/root-198-search-terms-field/conf/solr/drupal8/custom/solr-conf/4.x/solrconfig.xml#L868) in federated-search-demo site Solr config) 1. Configure a Search API index according to the [required schema documentation](https://www.drupal.org/docs/8/modules/search-api-federated-solr/federated-search-schema) 1. Index the content for the site using Search API @@ -72,7 +73,7 @@ In order to display results from the Solr index: 1. Configure the application route and settings at `/admin/config/search-api-federated-solr/search-app/settings` 1. Set permissions for `Use Federated Search` and `Administer Federated Search` for the proper roles. 1. Optional: [Theme the ReactJS search app](docs/theme.md) -1. Optional: Add the federated search page form block to your site theme +1. Optional: Add the federated search page form block to your site theme + configure the block settings ### Updating the bundled React application From 07e756e9402817c4bcff7f930cc92c74460d4482 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Tue, 12 Mar 2019 15:36:52 -0400 Subject: [PATCH 55/59] root-198: update readme with autocomplete config, fields, views docs --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8d8c55dbf..ddcc281a4 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,10 @@ On each site included in the federated search, you will need to: 1. Install this module and its dependencies 1. Configure a Search API server to connect to the shared Solr index -1. Configure that Search API server to set default query fields for your default [Request Handler](https://lucene.apache.org/solr/guide/6_6/requesthandlers-and-searchcomponents-in-solrconfig.html#RequestHandlersandSearchComponentsinSolrConfig-SearchHandlers). (See [example](https://github.com/palantirnet/federated-search-demo/blob/root-198-search-terms-field/conf/solr/drupal8/custom/solr-conf/4.x/solrconfig.xml#L868) in federated-search-demo site Solr config) +1. Configure that Search API server to set default query fields for your default [Request Handler](https://lucene.apache.org/solr/guide/6_6/requesthandlers-and-searchcomponents-in-solrconfig.html#RequestHandlersandSearchComponentsinSolrConfig-SearchHandlers). (See [example](https://github.com/palantirnet/federated-search-demo/blob/root-198-search-terms-field/conf/solr/drupal8/custom/solr-conf/4.x/solrconfig.xml#L868) in Federated Search Demo site Solr server config) 1. Configure a Search API index according to the [required schema documentation](https://www.drupal.org/docs/8/modules/search-api-federated-solr/federated-search-schema) + 1. Optional: To help facilitate autocomplete term partial queries, consider adding a Fulltext [Ngram](https://lucene.apache.org/solr/guide/6_6/tokenizers.html) version of your title field to the index (See [example]() in the Federated Search Demo site Solr index config). Also consider adding that field as a default query field for your Solr server's default Request Handler. + 1. Optional: If your site uses a "search terms" or similar field to trigger a boost for items based on given search terms, consider adding a Fulltext [Ngram](https://lucene.apache.org/solr/guide/6_6/tokenizers.html) version of that field to the index. Also consider adding that field as a default query field for your Solr server's default Request Handler. 1. Index the content for the site using Search API Once each site is configured, you may begin to index content. @@ -74,7 +76,11 @@ In order to display results from the Solr index: 1. Set permissions for `Use Federated Search` and `Administer Federated Search` for the proper roles. 1. Optional: [Theme the ReactJS search app](docs/theme.md) 1. Optional: Add the federated search page form block to your site theme + configure the block settings - +1. Optional: If you want autocomplete functionality and would prefer that results come from a view, [create a Search API search view with a rest export](https://www.drupal.org/docs/8/modules/search-api/getting-started/search-forms-and-results-pages/searching-with-views-0) or create a content view with a rest export (see view added as optional config for this module in `config/optional`) and use that url as your autocomplete endpoint. + 1. Under format, choose Solr Serializer as the format (this wraps the view results with the same response object as Solr so they can be rendered) + 1. Under format, choose fields. Add the title (for Search views, we recommend adding a full text version of your title to the index and adding that instead) and link to content (for Search views, url) fields. + 1. Under format, configure settings for the fields. Use the alias `ss_federated_title` for your title field and `ss_url` for your url field. + 1. Under Filter Criteria, add those fields you would like to query for the search term as an exposed filter with the "contains any word" operator (for Search views use full text field searches). For each filter, assign a filter identifier. These will be used in your autocomplete url as querystring params: `&filter1_identifier_value=[val]&filter2_identifier_value=[val]`. ### Updating the bundled React application From cc76a5643267dfb627b775ac217ab61b96c3987b Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Wed, 13 Mar 2019 09:50:19 -0400 Subject: [PATCH 56/59] root-198: document autocomplete view name in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ddcc281a4..a97969ff7 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ In order to display results from the Solr index: 1. Set permissions for `Use Federated Search` and `Administer Federated Search` for the proper roles. 1. Optional: [Theme the ReactJS search app](docs/theme.md) 1. Optional: Add the federated search page form block to your site theme + configure the block settings -1. Optional: If you want autocomplete functionality and would prefer that results come from a view, [create a Search API search view with a rest export](https://www.drupal.org/docs/8/modules/search-api/getting-started/search-forms-and-results-pages/searching-with-views-0) or create a content view with a rest export (see view added as optional config for this module in `config/optional`) and use that url as your autocomplete endpoint. +1. Optional: If you want autocomplete functionality and would prefer that results come from a view, [create a Search API search view with a rest export](https://www.drupal.org/docs/8/modules/search-api/getting-started/search-forms-and-results-pages/searching-with-views-0) or create a content view with a rest export (see the "Search API Federated Solr Block Form Autocomplete" view added as optional config for this module in `config/optional`) and use that url as your autocomplete endpoint. 1. Under format, choose Solr Serializer as the format (this wraps the view results with the same response object as Solr so they can be rendered) 1. Under format, choose fields. Add the title (for Search views, we recommend adding a full text version of your title to the index and adding that instead) and link to content (for Search views, url) fields. 1. Under format, configure settings for the fields. Use the alias `ss_federated_title` for your title field and `ss_url` for your url field. From da717a2f09a44a0e41216abc27235febf69cd087 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Thu, 14 Mar 2019 11:48:08 -0400 Subject: [PATCH 57/59] root-198: bump version of react app dependency --- search_api_federated_solr.libraries.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/search_api_federated_solr.libraries.yml b/search_api_federated_solr.libraries.yml index afdfc16b1..a3d5ec0a8 100644 --- a/search_api_federated_solr.libraries.yml +++ b/search_api_federated_solr.libraries.yml @@ -2,11 +2,11 @@ search: version: 1.x css: theme: - https://cdn.jsdelivr.net/gh/palantirnet/federated-search-react@v1.0.10/css/main.cf6a58ce.css: + https://cdn.jsdelivr.net/gh/palantirnet/federated-search-react@v2.0/css/main.ec684809.css: type: external minified: true js: - https://cdn.jsdelivr.net/gh/palantirnet/federated-search-react@v1.0.10/js/main.d41fc3fe.js: + https://cdn.jsdelivr.net/gh/palantirnet/federated-search-react@v2.0/js/main.6a547bbe.js: preprocess: false minified: true From 81e3ee8f2ffd050113840b7d572d5b7650d041ef Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Thu, 14 Mar 2019 14:50:15 -0400 Subject: [PATCH 58/59] root-198: remove legacy checkbox to enable autocomplete --- .../SearchApiFederatedSolrSearchAppSettingsForm.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php index 3ecdb490f..e531804cf 100644 --- a/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php +++ b/src/Form/SearchApiFederatedSolrSearchAppSettingsForm.php @@ -152,17 +152,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $config->get('index.password'), ]; - $form['setup']['autocomplete_is_enabled'] = [ - '#type' => 'checkbox', - '#title' => '' . $this->t('Enable autocomplete for the search results page search form') . '', - '#default_value' => $config->get('autocomplete.isEnabled'), - '#description' => $this - ->t('Checking this will expose more configuration options for autocomplete behavior for the search form on the Search Results page at the end of this form.'), - '#attributes' => [ - 'id' => ['autocomplete-is-enabled'], - ], - ]; - /** * Search results page options: * - show empty search results (i.e. filterable listing page), From 27c9edf7c060f78533106150f9dfc2b5e3aaf541 Mon Sep 17 00:00:00 2001 From: Jes Constantine Date: Thu, 14 Mar 2019 16:55:46 -0400 Subject: [PATCH 59/59] root-198: address php notices by checking for config values --- .../Block/FederatedSearchPageFormBlock.php | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Plugin/Block/FederatedSearchPageFormBlock.php b/src/Plugin/Block/FederatedSearchPageFormBlock.php index d5c315870..67cc81aac 100644 --- a/src/Plugin/Block/FederatedSearchPageFormBlock.php +++ b/src/Plugin/Block/FederatedSearchPageFormBlock.php @@ -84,7 +84,7 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_is_enabled'] = [ '#type' => 'checkbox', '#title' => '' . $this->t('Enable autocomplete for the search results page search form') . '', - '#default_value' => $config['autocomplete']['isEnabled'], + '#default_value' => isset($config['autocomplete']['isEnabled']) ? $config['autocomplete']['isEnabled'] : 0, '#description' => $this ->t('Check this box to enable autocomplete on the federated search block search form and to expose more configuration options below.'), '#attributes' => [ @@ -95,7 +95,7 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_is_append_wildcard'] = [ '#type' => 'checkbox', '#title' => '' . $this->t('Append a wildcard \'*\' to support partial text search') . '', - '#default_value' => $config['autocomplete']['appendWildcard'], + '#default_value' => isset($config['autocomplete']['appendWildcard']) ? $config['autocomplete']['appendWildcard'] : 0, '#description' => $this ->t('Check this box to append a wildcard * to the end of the autocomplete query term (i.e. "car" becomes "car+car*"). This option is recommended if your solr config does not add a field(s) with NGram Tokenizers to your index or if your autocomplete Request Handler is not configured to search those fields.'), '#states' => [ @@ -110,7 +110,7 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_url'] = [ '#type' => 'url', '#title' => $this->t('Endpoint URL'), - '#default_value' => $config['autocomplete']['url'], + '#default_value' => isset($config['autocomplete']['url']) ? $config['autocomplete']['url'] : '', '#maxlength' => 2048, '#size' => 50, '#description' => $this @@ -150,7 +150,7 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['basic_auth']['use_search_app_creds'] = [ '#type' => 'checkbox', '#title' => $this->t('Use credentials provided to search app'), - '#default_value' => $config['autocomplete']['use_search_app_creds'], + '#default_value' => isset($config['autocomplete']['use_search_app_creds']) ? $config['autocomplete']['use_search_app_creds'] : 0, '#attributes' => [ 'data-autocomplete-use-search-app-creds' => TRUE, ], @@ -159,7 +159,7 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['basic_auth']['username'] = [ '#type' => 'textfield', '#title' => $this->t('Username'), - '#default_value' => $config['autocomplete']['username'], + '#default_value' => isset($config['autocomplete']['username']) ? $config['autocomplete']['username'] : '', '#states' => [ 'visible' => [ ':input[data-autocomplete-use-search-app-creds]' => [ @@ -172,7 +172,7 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['basic_auth']['password'] = [ '#type' => 'textfield', '#title' => $this->t('Password'), - '#default_value' => $config['autocomplete']['password'], + '#default_value' => isset($config['autocomplete']['password']) ? $config['autocomplete']['password'] : '', '#states' => [ 'visible' => [ ':input[data-autocomplete-use-search-app-creds]' => [ @@ -185,7 +185,7 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_suggestion_rows'] = [ '#type' => 'number', '#title' => $this->t('Number of results'), - '#default_value' => $config['autocomplete']['suggestionRows'], + '#default_value' => isset($config['autocomplete']['suggestionRows']) ? $config['autocomplete']['suggestionRows'] : '', '#description' => $this ->t('The max number of results to render in the autocomplete results dropdown. (Default: 5)'), '#states' => [ @@ -200,7 +200,7 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_num_chars'] = [ '#type' => 'number', '#title' => $this->t('Number of characters after which autocomplete query should execute'), - '#default_value' => $config['autocomplete']['numChars'], + '#default_value' => isset($config['autocomplete']['numChars']) ? $config['autocomplete']['numChars'] : '', '#description' => $this ->t('Autocomplete query will be executed after a user types this many characters in the search query field. (Default: 2)'), '#states' => [ @@ -212,7 +212,7 @@ public function blockForm($form, FormStateInterface $form_state) { ], ]; - $autocomplete_mode = $config['autocomplete']['mode']; + $autocomplete_mode = isset($config['autocomplete']['mode']) ? $config['autocomplete']['mode'] : ''; $form['autocomplete']['autocomplete_mode'] = [ '#type' => 'select', @@ -223,7 +223,7 @@ public function blockForm($form, FormStateInterface $form_state) { ->t('Search results (i.e. search as you type functionality)'), 'Search terms (coming soon)' => [], ], - '#default_value' => $config['autocomplete']['mode'] || 'result', + '#default_value' => isset($config['autocomplete']['mode']) ? $config['autocomplete']['mode'] : 'result', '#states' => [ 'visible' => [ ':input[data-autocomplete-enable]' => [ @@ -237,7 +237,7 @@ public function blockForm($form, FormStateInterface $form_state) { '#type' => 'textfield', '#title' => $this->t('Results title text'), '#size' => 50, - '#default_value' => $autocomplete_mode ? $config['autocomplete'][$autocomplete_mode]['titleText'] : '', + '#default_value' => $autocomplete_mode && isset($config['autocomplete'][$autocomplete_mode]['titleText']) ? $config['autocomplete'][$autocomplete_mode]['titleText'] : '', '#description' => $this ->t('The title text is shown above the results in the autocomplete drop down. (Default: "What are you interested in?" for Search Results mode and "What would you like to search for?" for Search Term mode.)'), '#states' => [ @@ -252,7 +252,7 @@ public function blockForm($form, FormStateInterface $form_state) { $form['autocomplete']['autocomplete_mode_hide_directions'] = [ '#type' => 'checkbox', '#title' => '' . $this->t('Hide keyboard directions') . '', - '#default_value' => $autocomplete_mode ? $config['autocomplete'][$autocomplete_mode]['hideDirectionsText'] : 0, + '#default_value' => $autocomplete_mode && isset($config['autocomplete'][$autocomplete_mode]['hideDirectionsText']) ? $config['autocomplete'][$autocomplete_mode]['hideDirectionsText'] : 0, '#description' => $this ->t('Check this box to make hide the autocomplete keyboard usage directions in the results dropdown. For sites that want to maximize their accessibility UX for sighted keyboard users, we recommend leaving this unchecked. (Default: directions are visible)'), '#states' => [