From f82206a4d1af78ce0de9046cae03aadf86355562 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 16 Feb 2018 07:09:17 -0800 Subject: [PATCH] Add a rough Quick Search datasource for Phriction documents Summary: Depends on D19106. Fixes T5941. Ref T13077. Allows you to find Phriction documents as suggestions from global quick search. Also supports `w` to jump to Phriction and `w query` to query Phriction. The actual query logic for the datasource may need some tweaking after it collides with reality, but seems to produce fairly reasonable results in local testing against synthetic data. Test Plan: Searched for "Porcupine Facts", "Travel Companions", and other useful local pages. Searched for `w`. Searched for `w zebra facts`. Maniphest Tasks: T13077, T5941 Differential Revision: https://secure.phabricator.com/D19107 --- resources/celerity/map.php | 28 +++--- src/__phutil_library_map__.php | 4 + .../PhrictionDatasourceEngineExtension.php | 56 ++++++++++++ .../phriction/storage/PhrictionDocument.php | 4 + .../typeahead/PhrictionDocumentDatasource.php | 88 +++++++++++++++++++ .../rsrc/js/core/behavior-search-typeahead.js | 1 + 6 files changed, 167 insertions(+), 14 deletions(-) create mode 100644 src/applications/phriction/engineextension/PhrictionDatasourceEngineExtension.php create mode 100644 src/applications/phriction/typeahead/PhrictionDocumentDatasource.php diff --git a/resources/celerity/map.php b/resources/celerity/map.php index a08f04658e..fc3961604b 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,7 +10,7 @@ return array( 'conpherence.pkg.css' => 'e68cf1fa', 'conpherence.pkg.js' => '15191c65', 'core.pkg.css' => 'e4f098a5', - 'core.pkg.js' => '3ac6e174', + 'core.pkg.js' => 'bd19de1c', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '113e692c', 'differential.pkg.js' => 'f6d809c0', @@ -506,7 +506,7 @@ return array( 'rsrc/js/core/behavior-reorder-applications.js' => '76b9fc3e', 'rsrc/js/core/behavior-reveal-content.js' => '60821bc7', 'rsrc/js/core/behavior-scrollbar.js' => '834a1173', - 'rsrc/js/core/behavior-search-typeahead.js' => 'd0a99ab4', + 'rsrc/js/core/behavior-search-typeahead.js' => 'c3e917d9', 'rsrc/js/core/behavior-select-content.js' => 'bf5374ef', 'rsrc/js/core/behavior-select-on-click.js' => '4e3e79a6', 'rsrc/js/core/behavior-setup-check-https.js' => '491416b3', @@ -663,7 +663,7 @@ return array( 'javelin-behavior-phabricator-oncopy' => '2926fff2', 'javelin-behavior-phabricator-remarkup-assist' => 'acd29eee', 'javelin-behavior-phabricator-reveal-content' => '60821bc7', - 'javelin-behavior-phabricator-search-typeahead' => 'd0a99ab4', + 'javelin-behavior-phabricator-search-typeahead' => 'c3e917d9', 'javelin-behavior-phabricator-show-older-transactions' => '8f29b364', 'javelin-behavior-phabricator-tooltips' => 'c420b0b9', 'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6', @@ -1918,6 +1918,17 @@ return array( 'javelin-dom', 'javelin-vector', ), + 'c3e917d9' => array( + 'javelin-behavior', + 'javelin-typeahead-ondemand-source', + 'javelin-typeahead', + 'javelin-dom', + 'javelin-uri', + 'javelin-util', + 'javelin-stratcom', + 'phabricator-prefab', + 'phuix-icon-view', + ), 'c420b0b9' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -1969,17 +1980,6 @@ return array( 'phabricator-notification', 'conpherence-thread-manager', ), - 'd0a99ab4' => array( - 'javelin-behavior', - 'javelin-typeahead-ondemand-source', - 'javelin-typeahead', - 'javelin-dom', - 'javelin-uri', - 'javelin-util', - 'javelin-stratcom', - 'phabricator-prefab', - 'phuix-icon-view', - ), 'd0c516d5' => array( 'javelin-behavior', 'javelin-dom', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c2ee4966c1..29a957ae6e 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -4855,6 +4855,7 @@ phutil_register_library_map(array( 'PhrictionController' => 'applications/phriction/controller/PhrictionController.php', 'PhrictionCreateConduitAPIMethod' => 'applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php', 'PhrictionDAO' => 'applications/phriction/storage/PhrictionDAO.php', + 'PhrictionDatasourceEngineExtension' => 'applications/phriction/engineextension/PhrictionDatasourceEngineExtension.php', 'PhrictionDeleteController' => 'applications/phriction/controller/PhrictionDeleteController.php', 'PhrictionDiffController' => 'applications/phriction/controller/PhrictionDiffController.php', 'PhrictionDocument' => 'applications/phriction/storage/PhrictionDocument.php', @@ -4862,6 +4863,7 @@ phutil_register_library_map(array( 'PhrictionDocumentContentHeraldField' => 'applications/phriction/herald/PhrictionDocumentContentHeraldField.php', 'PhrictionDocumentContentTransaction' => 'applications/phriction/xaction/PhrictionDocumentContentTransaction.php', 'PhrictionDocumentController' => 'applications/phriction/controller/PhrictionDocumentController.php', + 'PhrictionDocumentDatasource' => 'applications/phriction/typeahead/PhrictionDocumentDatasource.php', 'PhrictionDocumentDeleteTransaction' => 'applications/phriction/xaction/PhrictionDocumentDeleteTransaction.php', 'PhrictionDocumentFerretEngine' => 'applications/phriction/search/PhrictionDocumentFerretEngine.php', 'PhrictionDocumentFulltextEngine' => 'applications/phriction/search/PhrictionDocumentFulltextEngine.php', @@ -10777,6 +10779,7 @@ phutil_register_library_map(array( 'PhrictionController' => 'PhabricatorController', 'PhrictionCreateConduitAPIMethod' => 'PhrictionConduitAPIMethod', 'PhrictionDAO' => 'PhabricatorLiskDAO', + 'PhrictionDatasourceEngineExtension' => 'PhabricatorDatasourceEngineExtension', 'PhrictionDeleteController' => 'PhrictionController', 'PhrictionDiffController' => 'PhrictionController', 'PhrictionDocument' => array( @@ -10796,6 +10799,7 @@ phutil_register_library_map(array( 'PhrictionDocumentContentHeraldField' => 'PhrictionDocumentHeraldField', 'PhrictionDocumentContentTransaction' => 'PhrictionDocumentTransactionType', 'PhrictionDocumentController' => 'PhrictionController', + 'PhrictionDocumentDatasource' => 'PhabricatorTypeaheadDatasource', 'PhrictionDocumentDeleteTransaction' => 'PhrictionDocumentTransactionType', 'PhrictionDocumentFerretEngine' => 'PhabricatorFerretEngine', 'PhrictionDocumentFulltextEngine' => 'PhabricatorFulltextEngine', diff --git a/src/applications/phriction/engineextension/PhrictionDatasourceEngineExtension.php b/src/applications/phriction/engineextension/PhrictionDatasourceEngineExtension.php new file mode 100644 index 0000000000..af262d541d --- /dev/null +++ b/src/applications/phriction/engineextension/PhrictionDatasourceEngineExtension.php @@ -0,0 +1,56 @@ +getViewer(); + + // Send "w" to Phriction. + if (preg_match('/^w\z/i', $query)) { + return '/w/'; + } + + // Send "w " to a search for similar wiki documents. + $matches = null; + if (preg_match('/^w\s+(.+)\z/i', $query, $matches)) { + $raw_query = $matches[1]; + + $engine = id(new PhrictionDocument()) + ->newFerretEngine(); + + $compiler = id(new PhutilSearchQueryCompiler()) + ->setEnableFunctions(true); + + $raw_tokens = $compiler->newTokens($raw_query); + + $fulltext_tokens = array(); + foreach ($raw_tokens as $raw_token) { + $fulltext_token = id(new PhabricatorFulltextToken()) + ->setToken($raw_token); + $fulltext_tokens[] = $fulltext_token; + } + + $documents = id(new PhrictionDocumentQuery()) + ->setViewer($viewer) + ->withFerretConstraint($engine, $fulltext_tokens) + ->execute(); + if (count($documents) == 1) { + return head($documents)->getURI(); + } else { + // More than one match, jump to search. + return urisprintf( + '/phriction/?order=relevance&query=%s#R', + $raw_query); + } + } + + return null; + } +} diff --git a/src/applications/phriction/storage/PhrictionDocument.php b/src/applications/phriction/storage/PhrictionDocument.php index 58d7cbee4a..729d3e4ca5 100644 --- a/src/applications/phriction/storage/PhrictionDocument.php +++ b/src/applications/phriction/storage/PhrictionDocument.php @@ -149,6 +149,10 @@ final class PhrictionDocument extends PhrictionDAO return $this; } + public function getURI() { + return self::getSlugURI($this->getSlug()); + } + /* -( Status )------------------------------------------------------------- */ diff --git a/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php b/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php new file mode 100644 index 0000000000..dc6af6de7a --- /dev/null +++ b/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php @@ -0,0 +1,88 @@ +getViewer(); + + $raw_query = $this->getRawQuery(); + + $engine = id(new PhrictionDocument()) + ->newFerretEngine(); + + $compiler = id(new PhutilSearchQueryCompiler()) + ->setEnableFunctions(true); + + $raw_tokens = $compiler->newTokens($raw_query); + + $fulltext_tokens = array(); + foreach ($raw_tokens as $raw_token) { + + // This is a little hacky and could maybe be cleaner. We're treating + // every search term as though the user had entered "title:dog" insead + // of "dog". + + $alternate_token = PhutilSearchQueryToken::newFromDictionary( + array( + 'quoted' => $raw_token->isQuoted(), + 'value' => $raw_token->getValue(), + 'operator' => PhutilSearchQueryCompiler::OPERATOR_SUBSTRING, + 'function' => 'title', + )); + + $fulltext_token = id(new PhabricatorFulltextToken()) + ->setToken($alternate_token); + $fulltext_tokens[] = $fulltext_token; + } + + $documents = id(new PhrictionDocumentQuery()) + ->setViewer($viewer) + ->withFerretConstraint($engine, $fulltext_tokens) + ->needContent(true) + ->execute(); + + $results = array(); + foreach ($documents as $document) { + $content = $document->getContent(); + + if (!$document->isActive()) { + $closed = $document->getStatusDisplayName(); + } else { + $closed = null; + } + + $slug = $document->getSlug(); + $title = $content->getTitle(); + + $sprite = 'phabricator-search-icon phui-font-fa phui-icon-view fa-book'; + + $result = id(new PhabricatorTypeaheadResult()) + ->setName($title) + ->setDisplayName($title) + ->setURI($document->getURI()) + ->setPHID($document->getPHID()) + ->setDisplayType($slug) + ->setPriorityType('wiki') + ->setImageSprite($sprite) + ->setClosed($closed); + + $results[] = $result; + } + + return $results; + } + +} diff --git a/webroot/rsrc/js/core/behavior-search-typeahead.js b/webroot/rsrc/js/core/behavior-search-typeahead.js index ddcc2e6aaa..0f5b4d1639 100644 --- a/webroot/rsrc/js/core/behavior-search-typeahead.js +++ b/webroot/rsrc/js/core/behavior-search-typeahead.js @@ -70,6 +70,7 @@ JX.behavior('phabricator-search-typeahead', function(config) { 'proj', 'user', 'repo', + 'wiki', 'symb', 'misc' ];