Loading CHANGELOG.txt +2 −0 Original line number Diff line number Diff line Search API Autocomplete 1.x, dev (xxxx-xx-xx): ---------------------------------------------- - #2947273 by drunken monkey: Fixed keywords preprocessing for “Live results” suggester. Search API Autocomplete 1.2 (2019-03-11): ----------------------------------------- Loading src/Controller/AutocompleteController.php +0 −7 Original line number Diff line number Diff line Loading @@ -88,12 +88,6 @@ class AutocompleteController extends ControllerBase implements ContainerInjectio try { $keys = $request->query->get('q'); // If the "Transliteration" processor is enabled for the search index, we // also need to transliterate the user input for autocompletion. if ($search->getIndex()->isValidProcessor('transliteration')) { $langcode = $this->languageManager()->getCurrentLanguage()->getId(); $keys = $this->transliterator->transliterate($keys, $langcode); } $split_keys = $this->autocompleteHelper->splitKeys($keys); list($complete, $incomplete) = $split_keys; $data = $request->query->all(); Loading @@ -106,7 +100,6 @@ class AutocompleteController extends ControllerBase implements ContainerInjectio // Prepare the query. $query->setSearchId('search_api_autocomplete:' . $search->id()); $query->addTag('search_api_autocomplete'); $query->preExecute(); // Get total limit and per-suggester limits. $limit = $search->getOption('limit'); Loading src/Plugin/search_api_autocomplete/suggester/Server.php +100 −3 Original line number Diff line number Diff line Loading @@ -2,14 +2,18 @@ namespace Drupal\search_api_autocomplete\Plugin\search_api_autocomplete\suggester; use Drupal\Component\Transliteration\TransliterationInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\PluginFormInterface; use Drupal\search_api\IndexInterface; use Drupal\search_api\Plugin\PluginFormTrait; use Drupal\search_api\Query\QueryInterface; use Drupal\search_api\SearchApiException; use Drupal\search_api_autocomplete\AutocompleteBackendInterface; use Drupal\search_api_autocomplete\SearchInterface; use Drupal\search_api_autocomplete\Suggester\SuggesterPluginBase; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a suggester plugin that retrieves suggestions from the server. Loading @@ -27,6 +31,20 @@ class Server extends SuggesterPluginBase implements PluginFormInterface { use PluginFormTrait; /** * The language manager. * * @var \Drupal\Core\Language\LanguageManagerInterface|null */ protected $languageManager; /** * The transliteration. * * @var \Drupal\Component\Transliteration\TransliterationInterface|null */ protected $transliterator; /** * {@inheritdoc} */ Loading @@ -34,6 +52,65 @@ class Server extends SuggesterPluginBase implements PluginFormInterface { return (bool) static::getBackend($search->getIndex()); } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { /** @var static $plugin */ $plugin = parent::create($container, $configuration, $plugin_id, $plugin_definition); $plugin->setLanguageManager($container->get('language_manager')); $plugin->setTransliterator($container->get('transliteration')); return $plugin; } /** * Retrieves the language manager. * * @return \Drupal\Core\Language\LanguageManagerInterface * The language manager. */ public function getLanguageManager() { return $this->languageManager ?: \Drupal::service('language_manager'); } /** * Sets the language manager. * * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The new language manager. * * @return $this */ public function setLanguageManager(LanguageManagerInterface $language_manager) { $this->languageManager = $language_manager; return $this; } /** * Retrieves the transliteration. * * @return \Drupal\Component\Transliteration\TransliterationInterface * The transliteration. */ public function getTransliterator() { return $this->transliterator ?: \Drupal::service('transliteration'); } /** * Sets the transliteration. * * @param \Drupal\Component\Transliteration\TransliterationInterface $transliterator * The new transliteration. * * @return $this */ public function setTransliterator(TransliterationInterface $transliterator) { $this->transliterator = $transliterator; return $this; } /** * {@inheritdoc} */ Loading Loading @@ -81,13 +158,28 @@ class Server extends SuggesterPluginBase implements PluginFormInterface { * {@inheritdoc} */ public function getAutocompleteSuggestions(QueryInterface $query, $incomplete_key, $user_input) { if (!($backend = static::getBackend($this->getSearch()->getIndex()))) { $index = $query->getIndex(); if (!($backend = static::getBackend($index))) { return []; } // If the "Transliteration" processor is enabled for the search index, we // also need to transliterate the user input for autocompletion. if ($index->isValidProcessor('transliteration')) { $langcode = $this->getLanguageManager()->getCurrentLanguage()->getId(); $incomplete_key = $this->getTransliterator()->transliterate($incomplete_key, $langcode); $user_input = $this->getTransliterator()->transliterate($user_input, $langcode); } if ($this->configuration['fields']) { $query->setFulltextFields($this->configuration['fields']); } try { $query->preExecute(); } catch (SearchApiException $e) { return []; } return $backend->getAutocompleteSuggestions($query, $this->getSearch(), $incomplete_key, $user_input); } Loading @@ -105,8 +197,13 @@ class Server extends SuggesterPluginBase implements PluginFormInterface { if (!$index->hasValidServer()) { return NULL; } try { $server = $index->getServerInstance(); $backend = $server->getBackend(); } catch (SearchApiException $e) { return NULL; } if ($server->supportsFeature('search_api_autocomplete') || $backend instanceof AutocompleteBackendInterface) { return $backend; } Loading tests/src/FunctionalJavascript/IntegrationTest.php +12 −37 Original line number Diff line number Diff line Loading @@ -232,23 +232,23 @@ class IntegrationTest extends IntegrationTestBase { } $expected = [ [ 'keys' => 'test-suggester-1', 'keys' => 'Tést-suggester-1', 'count' => 1, ], [ 'keys' => 'test-suggester-2', 'keys' => 'Tést-suggester-2', 'count' => 2, ], [ 'keys' => 'test-suggester-url', 'keys' => 'Tést-suggester-url', 'count' => NULL, ], [ 'keys' => 'test-backend-1', 'keys' => 'Tést-backend-1', 'count' => 1, ], [ 'keys' => 'test-backend-2', 'keys' => 'Tést-backend-2', 'count' => 2, ], ]; Loading @@ -264,17 +264,19 @@ class IntegrationTest extends IntegrationTestBase { if ($click_url_suggestion) { // Click the URL suggestion and verify it correctly redirects the browser // to that URL. $suggestion_elements['test-suggester-url']->click(); $suggestion_elements['Tést-suggester-url']->click(); $this->logPageChange(); $assert_session->addressEquals("/user/{$this->adminUser->id()}"); return; } // Click one of the search key suggestions. The form should now auto-submit. $suggestion_elements['test-suggester-1']->click(); $keys = 'Tést-suggester-1'; $suggestion_elements[$keys]->click(); $this->logPageChange(); $assert_session->addressEquals('/search-api-autocomplete-test'); $this->assertRegExp('#[?&]keys=test-suggester-1#', $this->getUrl()); $keys = urlencode($keys); $this->assertRegExp("#[?&]keys=$keys#", $this->getUrl()); // Check that autocomplete in the "Name" filter works, too, and that it sets // the correct fields on the query. Loading Loading @@ -328,7 +330,7 @@ class IntegrationTest extends IntegrationTestBase { $this->assertEquals(0, $query->getOption('offset')); $this->assertEquals(5, $query->getOption('limit')); $this->assertEquals(['body'], $query->getFulltextFields()); $this->assertEquals('test', $query->getOriginalKeys()); $this->assertEquals('Tést', $query->getOriginalKeys()); // Click on one of the suggestions and verify it takes us to the expected // page. Loading @@ -338,33 +340,6 @@ class IntegrationTest extends IntegrationTestBase { $assert_session->addressEquals('/' . $path); } /** * Retrieves autocomplete suggestions from a field on the current page. * * @param string $field_html_id * (optional) The HTML ID of the field. * @param string $input * (optional) The input to write into the field. * * @return \Behat\Mink\Element\NodeElement[] * The suggestion elements from the page. */ protected function getAutocompleteSuggestions($field_html_id = 'edit-keys', $input = 'test') { $assert_session = $this->assertSession(); $field = $assert_session->elementExists('css', "input[data-drupal-selector=\"$field_html_id\"]"); $field->setValue(substr($input, 0, -1)); $this->getSession()->getDriver()->keyDown($field->getXpath(), substr($input, -1)); $element = $assert_session->waitOnAutocomplete(); $this->assertTrue($element && $element->isVisible()); $this->logPageChange(); // Contrary to documentation, this can also return NULL. Therefore, we need // to make sure to return an array even in this case. $page = $this->getSession()->getPage(); return $page->findAll('css', '.ui-autocomplete .ui-menu-item') ?: []; } /** * Tests whether using a custom autocomplete script is properly supported. * Loading Loading @@ -404,7 +379,7 @@ class IntegrationTest extends IntegrationTestBase { $expected = [ 'display: page', 'filter: keys', 'q: test', 'q: Tést', "search_api_autocomplete_search: {$this->searchId}", ]; $this->assertEquals($expected, $suggestions, 'Unexpected suggestions returned by custom script.'); Loading tests/src/FunctionalJavascript/IntegrationTestBase.php +4 −2 Original line number Diff line number Diff line Loading @@ -27,12 +27,14 @@ abstract class IntegrationTestBase extends JavascriptTestBase { * @param string $field_html_id * (optional) The HTML ID of the field. * @param string $input * (optional) The input to write into the field. * (optional) The input to write into the field. The default contains * uppercase characters and accents to verify input is properly * preprocessed. * * @return \Behat\Mink\Element\NodeElement[] * The suggestion elements from the page. */ protected function getAutocompleteSuggestions($field_html_id = 'edit-keys', $input = 'test') { protected function getAutocompleteSuggestions($field_html_id = 'edit-keys', $input = 'Tést') { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); $field = $assert_session->elementExists('css', "input[data-drupal-selector=\"$field_html_id\"]"); Loading Loading
CHANGELOG.txt +2 −0 Original line number Diff line number Diff line Search API Autocomplete 1.x, dev (xxxx-xx-xx): ---------------------------------------------- - #2947273 by drunken monkey: Fixed keywords preprocessing for “Live results” suggester. Search API Autocomplete 1.2 (2019-03-11): ----------------------------------------- Loading
src/Controller/AutocompleteController.php +0 −7 Original line number Diff line number Diff line Loading @@ -88,12 +88,6 @@ class AutocompleteController extends ControllerBase implements ContainerInjectio try { $keys = $request->query->get('q'); // If the "Transliteration" processor is enabled for the search index, we // also need to transliterate the user input for autocompletion. if ($search->getIndex()->isValidProcessor('transliteration')) { $langcode = $this->languageManager()->getCurrentLanguage()->getId(); $keys = $this->transliterator->transliterate($keys, $langcode); } $split_keys = $this->autocompleteHelper->splitKeys($keys); list($complete, $incomplete) = $split_keys; $data = $request->query->all(); Loading @@ -106,7 +100,6 @@ class AutocompleteController extends ControllerBase implements ContainerInjectio // Prepare the query. $query->setSearchId('search_api_autocomplete:' . $search->id()); $query->addTag('search_api_autocomplete'); $query->preExecute(); // Get total limit and per-suggester limits. $limit = $search->getOption('limit'); Loading
src/Plugin/search_api_autocomplete/suggester/Server.php +100 −3 Original line number Diff line number Diff line Loading @@ -2,14 +2,18 @@ namespace Drupal\search_api_autocomplete\Plugin\search_api_autocomplete\suggester; use Drupal\Component\Transliteration\TransliterationInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\PluginFormInterface; use Drupal\search_api\IndexInterface; use Drupal\search_api\Plugin\PluginFormTrait; use Drupal\search_api\Query\QueryInterface; use Drupal\search_api\SearchApiException; use Drupal\search_api_autocomplete\AutocompleteBackendInterface; use Drupal\search_api_autocomplete\SearchInterface; use Drupal\search_api_autocomplete\Suggester\SuggesterPluginBase; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a suggester plugin that retrieves suggestions from the server. Loading @@ -27,6 +31,20 @@ class Server extends SuggesterPluginBase implements PluginFormInterface { use PluginFormTrait; /** * The language manager. * * @var \Drupal\Core\Language\LanguageManagerInterface|null */ protected $languageManager; /** * The transliteration. * * @var \Drupal\Component\Transliteration\TransliterationInterface|null */ protected $transliterator; /** * {@inheritdoc} */ Loading @@ -34,6 +52,65 @@ class Server extends SuggesterPluginBase implements PluginFormInterface { return (bool) static::getBackend($search->getIndex()); } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { /** @var static $plugin */ $plugin = parent::create($container, $configuration, $plugin_id, $plugin_definition); $plugin->setLanguageManager($container->get('language_manager')); $plugin->setTransliterator($container->get('transliteration')); return $plugin; } /** * Retrieves the language manager. * * @return \Drupal\Core\Language\LanguageManagerInterface * The language manager. */ public function getLanguageManager() { return $this->languageManager ?: \Drupal::service('language_manager'); } /** * Sets the language manager. * * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The new language manager. * * @return $this */ public function setLanguageManager(LanguageManagerInterface $language_manager) { $this->languageManager = $language_manager; return $this; } /** * Retrieves the transliteration. * * @return \Drupal\Component\Transliteration\TransliterationInterface * The transliteration. */ public function getTransliterator() { return $this->transliterator ?: \Drupal::service('transliteration'); } /** * Sets the transliteration. * * @param \Drupal\Component\Transliteration\TransliterationInterface $transliterator * The new transliteration. * * @return $this */ public function setTransliterator(TransliterationInterface $transliterator) { $this->transliterator = $transliterator; return $this; } /** * {@inheritdoc} */ Loading Loading @@ -81,13 +158,28 @@ class Server extends SuggesterPluginBase implements PluginFormInterface { * {@inheritdoc} */ public function getAutocompleteSuggestions(QueryInterface $query, $incomplete_key, $user_input) { if (!($backend = static::getBackend($this->getSearch()->getIndex()))) { $index = $query->getIndex(); if (!($backend = static::getBackend($index))) { return []; } // If the "Transliteration" processor is enabled for the search index, we // also need to transliterate the user input for autocompletion. if ($index->isValidProcessor('transliteration')) { $langcode = $this->getLanguageManager()->getCurrentLanguage()->getId(); $incomplete_key = $this->getTransliterator()->transliterate($incomplete_key, $langcode); $user_input = $this->getTransliterator()->transliterate($user_input, $langcode); } if ($this->configuration['fields']) { $query->setFulltextFields($this->configuration['fields']); } try { $query->preExecute(); } catch (SearchApiException $e) { return []; } return $backend->getAutocompleteSuggestions($query, $this->getSearch(), $incomplete_key, $user_input); } Loading @@ -105,8 +197,13 @@ class Server extends SuggesterPluginBase implements PluginFormInterface { if (!$index->hasValidServer()) { return NULL; } try { $server = $index->getServerInstance(); $backend = $server->getBackend(); } catch (SearchApiException $e) { return NULL; } if ($server->supportsFeature('search_api_autocomplete') || $backend instanceof AutocompleteBackendInterface) { return $backend; } Loading
tests/src/FunctionalJavascript/IntegrationTest.php +12 −37 Original line number Diff line number Diff line Loading @@ -232,23 +232,23 @@ class IntegrationTest extends IntegrationTestBase { } $expected = [ [ 'keys' => 'test-suggester-1', 'keys' => 'Tést-suggester-1', 'count' => 1, ], [ 'keys' => 'test-suggester-2', 'keys' => 'Tést-suggester-2', 'count' => 2, ], [ 'keys' => 'test-suggester-url', 'keys' => 'Tést-suggester-url', 'count' => NULL, ], [ 'keys' => 'test-backend-1', 'keys' => 'Tést-backend-1', 'count' => 1, ], [ 'keys' => 'test-backend-2', 'keys' => 'Tést-backend-2', 'count' => 2, ], ]; Loading @@ -264,17 +264,19 @@ class IntegrationTest extends IntegrationTestBase { if ($click_url_suggestion) { // Click the URL suggestion and verify it correctly redirects the browser // to that URL. $suggestion_elements['test-suggester-url']->click(); $suggestion_elements['Tést-suggester-url']->click(); $this->logPageChange(); $assert_session->addressEquals("/user/{$this->adminUser->id()}"); return; } // Click one of the search key suggestions. The form should now auto-submit. $suggestion_elements['test-suggester-1']->click(); $keys = 'Tést-suggester-1'; $suggestion_elements[$keys]->click(); $this->logPageChange(); $assert_session->addressEquals('/search-api-autocomplete-test'); $this->assertRegExp('#[?&]keys=test-suggester-1#', $this->getUrl()); $keys = urlencode($keys); $this->assertRegExp("#[?&]keys=$keys#", $this->getUrl()); // Check that autocomplete in the "Name" filter works, too, and that it sets // the correct fields on the query. Loading Loading @@ -328,7 +330,7 @@ class IntegrationTest extends IntegrationTestBase { $this->assertEquals(0, $query->getOption('offset')); $this->assertEquals(5, $query->getOption('limit')); $this->assertEquals(['body'], $query->getFulltextFields()); $this->assertEquals('test', $query->getOriginalKeys()); $this->assertEquals('Tést', $query->getOriginalKeys()); // Click on one of the suggestions and verify it takes us to the expected // page. Loading @@ -338,33 +340,6 @@ class IntegrationTest extends IntegrationTestBase { $assert_session->addressEquals('/' . $path); } /** * Retrieves autocomplete suggestions from a field on the current page. * * @param string $field_html_id * (optional) The HTML ID of the field. * @param string $input * (optional) The input to write into the field. * * @return \Behat\Mink\Element\NodeElement[] * The suggestion elements from the page. */ protected function getAutocompleteSuggestions($field_html_id = 'edit-keys', $input = 'test') { $assert_session = $this->assertSession(); $field = $assert_session->elementExists('css', "input[data-drupal-selector=\"$field_html_id\"]"); $field->setValue(substr($input, 0, -1)); $this->getSession()->getDriver()->keyDown($field->getXpath(), substr($input, -1)); $element = $assert_session->waitOnAutocomplete(); $this->assertTrue($element && $element->isVisible()); $this->logPageChange(); // Contrary to documentation, this can also return NULL. Therefore, we need // to make sure to return an array even in this case. $page = $this->getSession()->getPage(); return $page->findAll('css', '.ui-autocomplete .ui-menu-item') ?: []; } /** * Tests whether using a custom autocomplete script is properly supported. * Loading Loading @@ -404,7 +379,7 @@ class IntegrationTest extends IntegrationTestBase { $expected = [ 'display: page', 'filter: keys', 'q: test', 'q: Tést', "search_api_autocomplete_search: {$this->searchId}", ]; $this->assertEquals($expected, $suggestions, 'Unexpected suggestions returned by custom script.'); Loading
tests/src/FunctionalJavascript/IntegrationTestBase.php +4 −2 Original line number Diff line number Diff line Loading @@ -27,12 +27,14 @@ abstract class IntegrationTestBase extends JavascriptTestBase { * @param string $field_html_id * (optional) The HTML ID of the field. * @param string $input * (optional) The input to write into the field. * (optional) The input to write into the field. The default contains * uppercase characters and accents to verify input is properly * preprocessed. * * @return \Behat\Mink\Element\NodeElement[] * The suggestion elements from the page. */ protected function getAutocompleteSuggestions($field_html_id = 'edit-keys', $input = 'test') { protected function getAutocompleteSuggestions($field_html_id = 'edit-keys', $input = 'Tést') { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); $field = $assert_session->elementExists('css', "input[data-drupal-selector=\"$field_html_id\"]"); Loading