diff --git a/application/modules/opac/controllers/RechercheController.php b/application/modules/opac/controllers/RechercheController.php index 7fa1024c3bfaf0998bb4788540cc0ac404585b3f..3726aede6842868a091e7742cae69f1702061985 100644 --- a/application/modules/opac/controllers/RechercheController.php +++ b/application/modules/opac/controllers/RechercheController.php @@ -484,8 +484,15 @@ class RechercheController extends ZendAfi_Controller_Action { $this->_helper->json([]); return; } - - $this->_helper->json(Class_Autocomplete_Index::searchAll($startsWith)); + $results = Class_Autocomplete_Index::searchAll($startsWith); + $terms = array_map(function($term) {return '/('.$term.')/iu';}, + Class_Autocomplete_IndexSearcher::extractTerms($startsWith)); + $json = []; + foreach($results as $index => $result) { + $json[] = ['label' => preg_replace($terms, '<span>$1</span>', $result), + 'value' => $result]; + } + $this->_helper->json($json); } diff --git a/library/Class/Autocomplete/Index.php b/library/Class/Autocomplete/Index.php index 83e19d4467fc6f453541855994e42eee40fb393a..aad915e680ccbd3baba090c563b8cd4dea681025 100644 --- a/library/Class/Autocomplete/Index.php +++ b/library/Class/Autocomplete/Index.php @@ -120,7 +120,8 @@ class Class_Autocomplete_Index { public function search($terms) { - return $this->getSearcher()->search($this->getFilePath(), $terms); + return $this->getSearcher() + ->search($terms); } @@ -138,7 +139,8 @@ class Class_Autocomplete_Index { protected static function getSearcher() { if (null !== static::$_searcher) return static::$_searcher; - return new Class_Autocomplete_IndexSearcher(); + return (new Class_Autocomplete_IndexSearcher()) + ->setBasePath(static::getBasePath()); } } ?> \ No newline at end of file diff --git a/library/Class/Autocomplete/IndexSearcher.php b/library/Class/Autocomplete/IndexSearcher.php index b9d3c3fab027f1511680033320a21c0d5e989b19..a43075f2959f466ba2e8ebddec93f77771406bb8 100644 --- a/library/Class/Autocomplete/IndexSearcher.php +++ b/library/Class/Autocomplete/IndexSearcher.php @@ -21,22 +21,38 @@ class Class_Autocomplete_IndexSearcher { - public function search($file_path, $terms) { - $terms = str_replace("'", "'\''", $terms); - $terms = str_replace('e', '[éeèêë]', $terms); + protected $base_path; + + public static function extractTerms($search_expression) { + $terms = str_replace(['e', 'i', 'o', 'u'], + ['[éeèêë]', '[iïî]', '[oôö]', '[uùûü]'], $search_expression); $terms = explode(' ', $terms); $terms = array_filter($terms); - $grep = "grep -ri -E -m10 --no-filename '(^|\s)" . $terms[0] . "' " . $file_path . " | sort --unique"; + usort($terms, + function($a, $b) { return strlen($a)<strlen($b);}); + return $terms; + } + + + public function setBasePath($path) { + $this->base_path = $path; + return $this; + } + + public function search($file_path, $search_expression) { + $search_expression = str_replace("'", "'\''", $search_expression); + $terms = static::extractTerms($search_expression); + $grep = "grep -ri -E -m10 --no-filename '(^|\s)" . $terms[0] . "' " . $this->base_path . " | sort --unique"; $out=[]; - if(count($terms) > 1) { - for($i = 1; $i < count($terms); $i++) { - $grep.= " | grep -i -E '(^|\s)" . $terms[$i] . "'"; - } - } + foreach(array_slice($terms, 1) as $term) + $grep.= " | grep -i -E '(^|\s)" . $term . "'"; exec($grep, $out); return $out; } + + + } ?> \ No newline at end of file diff --git a/public/opac/java/search_autocomplete/search_autocomplete.js b/public/opac/java/search_autocomplete/search_autocomplete.js index ddda60a626ff480ce762fd9b748cfd7590da0451..e4acc71f606b92f9e0fcf7b791356f5481fdf3b3 100644 --- a/public/opac/java/search_autocomplete/search_autocomplete.js +++ b/public/opac/java/search_autocomplete/search_autocomplete.js @@ -21,6 +21,8 @@ $.fn.search_autocomplete = function (options) { var node = $(this); node.autocomplete({ + minLength: options.minLength, + html: true, source: function(query, response) { var datas = {}; datas[options.dataLabel] = node.val(); @@ -30,9 +32,21 @@ data: datas, success: function(data) {response(data);} }); + }, + focus: function(event, ui) { + return false; }, - minLength: options.minLength - }); + select: function(event, ui) { + node.val(ui.item.value); + node.closest('form').submit(); + return false; + } + }).data("ui-autocomplete")._renderItem = function(ul, item) { + return $("<li>") + .data("item.autocomplete", item) + .append("<a>" + item.label + "</a>") + .appendTo(ul); + }; }; } (jQuery)); diff --git a/tests/application/modules/opac/controllers/RechercheControllerTest.php b/tests/application/modules/opac/controllers/RechercheControllerTest.php index b0c52823ab9ee65d0bdbb3f2507554aa77ddbc0b..cb0c6253dd0faa079fba15efda00e5edbe99f6ba 100644 --- a/tests/application/modules/opac/controllers/RechercheControllerTest.php +++ b/tests/application/modules/opac/controllers/RechercheControllerTest.php @@ -1977,4 +1977,42 @@ class RechercheControllerSuggestAjaxActionTest extends AbstractControllerTestCas } } + + + +class RechercheController_IndexSearcherTest extends AbstractControllerTestCase { + public function setUp() { + parent::setUp(); + Class_Notice::beVolatile(); + } + + + protected function searchFor($terms) { + Class_Autocomplete_Index::setSearcher((new Class_Autocomplete_IndexSearcher()) + ->setBasePath(ROOT_PATH.'tests/library/Class/Autocomplete/index/')); + + $this->dispatch('/opac/recherche/suggestajax/startsWith/'.$terms, true); + return json_decode($this->_response->getBody())[0]->label; + } + + + public function searches() { + return [ + ['<span>Jean-P</span>ierre Cuq','jean-p'], + ["<span>L'île</span> <span>de</span> <span>maïté</span>", "l'ile dE Maite"], + ["<span>Ô</span> Mireille si tu savais, <span><span>o</span>ù</span> je suis.", 'o ou'], + ["Centre interdépartemental de gestion de la Petite couronne de la région <span>d'Ile</span>-de-France", "d'ile"], + ['Jacques <span>Réda</span>', 'reda'], + ['<span>First</span> impressions de The <span>Strokes</span>', 'first strokes'] + ]; + } + + /** @test + * @dataProvider searches() + */ + public function autocompleteTermShouldBeFound($expected_result, $search_expression) { + $this->assertEquals($expected_result, $this->searchFor($search_expression)); + } +} + ?> \ No newline at end of file diff --git a/tests/library/Class/Autocomplete/IndexTest.php b/tests/library/Class/Autocomplete/IndexTest.php index 4e91bca4290397bd1f45b11ee932125e34128fb3..c1674668eaef48c4d4605193764c872fd2fcf8c1 100644 --- a/tests/library/Class/Autocomplete/IndexTest.php +++ b/tests/library/Class/Autocomplete/IndexTest.php @@ -70,44 +70,4 @@ class Class_Autocomplete_IndexTest extends Storm_Test_ModelTestCase{ $this->_file_system->methodHasBeenCalledWithParams('fwrite', [$handle, "Bone of my bones de Charle\n"])); } } - - - -class Class_Autocomplete_IndexSearcherTest extends Storm_Test_ModelTestCase{ - public function setUp() { - parent::setUp(); - Class_Notice::beVOlatile(); - } - - - protected function searchFor($term) { - return (new Class_Autocomplete_IndexSearcher()) - ->search(ROOT_PATH.'tests/library/Class/Autocomplete/index/', $term); - } - - - /** @test */ - public function autocompleteTermShouldBeFound() { - $this->assertEquals('Jean-Pierre Cuq', $this->searchFor('jean-p')[0]); - } - - - /** @test */ - public function autocompleteTermWithSimpleQuoteShouldBeFound() { - $this->assertContains('Centre interdépartemental', $this->searchFor("d'ile")[0]); - } - - - /** @test */ - public function autocompleteTermAccentedShouldBeFound() { - $this->assertEquals('Jacques Réda', $this->searchFor('reda')[0]); - } - - - /** @test */ - public function autocompleteTwoTermsShouldBeFound() { - $this->assertEquals('First impressions de The Strokes', $this->searchFor('first strokes')[0]); - } -} - ?> \ No newline at end of file diff --git a/tests/library/Class/Autocomplete/index/titles b/tests/library/Class/Autocomplete/index/titles index eaf6c07ec783a86643694609318c8f5b9c0fbc6d..7e1202974e9528b378e5d3dda81eece5d4d2d2c5 100644 --- a/tests/library/Class/Autocomplete/index/titles +++ b/tests/library/Class/Autocomplete/index/titles @@ -6,4 +6,6 @@ Australie de Jeff Drewitz Dub fire blazing de Bush Chemists First impressions de The Strokes Blue Bay Palace de Nathacha Appanah -Bleu de chauffe de Nan Aurousseau \ No newline at end of file +Bleu de chauffe de Nan Aurousseau +L'île de maïté +Ô Mireille si tu savais, où je suis. \ No newline at end of file