@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Make Portals indexable with Ferret

Summary:
Ref T13275. Add portals to the search index so that:

- they show up in fulltext global search; and
- the typeahead actually uses an index.

Also make them taggable with projects as an organizational aid.

Test Plan: Indexed portals with `bin/serach index`, searched for a portal with "Query", with fulltext search in main menu, with typehead on "Install Dashboard...", changed the name of a portal and searched again to check that the index updates properly.

Reviewers: amckinley

Reviewed By: amckinley

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T13275

Differential Revision: https://secure.phabricator.com/D20389

+156 -38
+9
resources/sql/autopatches/20190410.portals.01.ferret.doc.sql
··· 1 + CREATE TABLE {$NAMESPACE}_dashboard.dashboard_portal_fdocument ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + objectPHID VARBINARY(64) NOT NULL, 4 + isClosed BOOL NOT NULL, 5 + authorPHID VARBINARY(64), 6 + ownerPHID VARBINARY(64), 7 + epochCreated INT UNSIGNED NOT NULL, 8 + epochModified INT UNSIGNED NOT NULL 9 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+8
resources/sql/autopatches/20190410.portals.02.ferret.field.sql
··· 1 + CREATE TABLE {$NAMESPACE}_dashboard.dashboard_portal_ffield ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + documentID INT UNSIGNED NOT NULL, 4 + fieldKey VARCHAR(4) NOT NULL COLLATE {$COLLATE_TEXT}, 5 + rawCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT}, 6 + termCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT}, 7 + normalCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT} 8 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+5
resources/sql/autopatches/20190410.portals.03.ferret.ngrams.sql
··· 1 + CREATE TABLE {$NAMESPACE}_dashboard.dashboard_portal_fngrams ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + documentID INT UNSIGNED NOT NULL, 4 + ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT} 5 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+7
resources/sql/autopatches/20190410.portals.04.ferret.cngrams.sql
··· 1 + CREATE TABLE {$NAMESPACE}_dashboard.dashboard_portal_fngrams_common ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT}, 4 + needsCollection BOOL NOT NULL, 5 + UNIQUE KEY `key_ngram` (ngram), 6 + KEY `key_collect` (needsCollection) 7 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+7
src/__phutil_library_map__.php
··· 2957 2957 'PhabricatorDashboardPortalEditController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalEditController.php', 2958 2958 'PhabricatorDashboardPortalEditEngine' => 'applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php', 2959 2959 'PhabricatorDashboardPortalEditor' => 'applications/dashboard/editor/PhabricatorDashboardPortalEditor.php', 2960 + 'PhabricatorDashboardPortalFerretEngine' => 'applications/dashboard/engine/PhabricatorDashboardPortalFerretEngine.php', 2961 + 'PhabricatorDashboardPortalFulltextEngine' => 'applications/dashboard/engine/PhabricatorDashboardPortalFulltextEngine.php', 2960 2962 'PhabricatorDashboardPortalInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardPortalInstallWorkflow.php', 2961 2963 'PhabricatorDashboardPortalListController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalListController.php', 2962 2964 'PhabricatorDashboardPortalMenuItem' => 'applications/dashboard/menuitem/PhabricatorDashboardPortalMenuItem.php', ··· 8938 8940 'PhabricatorApplicationTransactionInterface', 8939 8941 'PhabricatorPolicyInterface', 8940 8942 'PhabricatorDestructibleInterface', 8943 + 'PhabricatorProjectInterface', 8944 + 'PhabricatorFulltextInterface', 8945 + 'PhabricatorFerretInterface', 8941 8946 ), 8942 8947 'PhabricatorDashboardPortalController' => 'PhabricatorDashboardController', 8943 8948 'PhabricatorDashboardPortalDatasource' => 'PhabricatorTypeaheadDatasource', ··· 8945 8950 'PhabricatorDashboardPortalEditController' => 'PhabricatorDashboardPortalController', 8946 8951 'PhabricatorDashboardPortalEditEngine' => 'PhabricatorEditEngine', 8947 8952 'PhabricatorDashboardPortalEditor' => 'PhabricatorApplicationTransactionEditor', 8953 + 'PhabricatorDashboardPortalFerretEngine' => 'PhabricatorFerretEngine', 8954 + 'PhabricatorDashboardPortalFulltextEngine' => 'PhabricatorFulltextEngine', 8948 8955 'PhabricatorDashboardPortalInstallWorkflow' => 'PhabricatorDashboardObjectInstallWorkflow', 8949 8956 'PhabricatorDashboardPortalListController' => 'PhabricatorDashboardPortalController', 8950 8957 'PhabricatorDashboardPortalMenuItem' => 'PhabricatorProfileMenuItem',
+4
src/applications/dashboard/editor/PhabricatorDashboardPortalEditor.php
··· 28 28 return $types; 29 29 } 30 30 31 + protected function supportsSearch() { 32 + return true; 33 + } 34 + 31 35 }
+18
src/applications/dashboard/engine/PhabricatorDashboardPortalFerretEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorDashboardPortalFerretEngine 4 + extends PhabricatorFerretEngine { 5 + 6 + public function getApplicationName() { 7 + return 'dashboard'; 8 + } 9 + 10 + public function getScopeName() { 11 + return 'portal'; 12 + } 13 + 14 + public function newSearchEngine() { 15 + return new PhabricatorDashboardPortalSearchEngine(); 16 + } 17 + 18 + }
+23
src/applications/dashboard/engine/PhabricatorDashboardPortalFulltextEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorDashboardPortalFulltextEngine 4 + extends PhabricatorFulltextEngine { 5 + 6 + protected function buildAbstractDocument( 7 + PhabricatorSearchAbstractDocument $document, 8 + $object) { 9 + 10 + $portal = $object; 11 + 12 + $document->setDocumentTitle($portal->getName()); 13 + 14 + $document->addRelationship( 15 + $portal->isArchived() 16 + ? PhabricatorSearchRelationship::RELATIONSHIP_CLOSED 17 + : PhabricatorSearchRelationship::RELATIONSHIP_OPEN, 18 + $portal->getPHID(), 19 + PhabricatorDashboardPortalPHIDType::TYPECONST, 20 + PhabricatorTime::getNow()); 21 + } 22 + 23 + }
+7 -3
src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php
··· 36 36 if ($this->ids !== null) { 37 37 $where[] = qsprintf( 38 38 $conn, 39 - 'id IN (%Ld)', 39 + 'portal.id IN (%Ld)', 40 40 $this->ids); 41 41 } 42 42 43 43 if ($this->phids !== null) { 44 44 $where[] = qsprintf( 45 45 $conn, 46 - 'phid IN (%Ls)', 46 + 'portal.phid IN (%Ls)', 47 47 $this->phids); 48 48 } 49 49 50 50 if ($this->statuses !== null) { 51 51 $where[] = qsprintf( 52 52 $conn, 53 - 'status IN (%Ls)', 53 + 'portal.status IN (%Ls)', 54 54 $this->statuses); 55 55 } 56 56 ··· 59 59 60 60 public function getQueryApplicationClass() { 61 61 return 'PhabricatorDashboardApplication'; 62 + } 63 + 64 + protected function getPrimaryTableAlias() { 65 + return 'portal'; 62 66 } 63 67 64 68 }
+20 -1
src/applications/dashboard/storage/PhabricatorDashboardPortal.php
··· 5 5 implements 6 6 PhabricatorApplicationTransactionInterface, 7 7 PhabricatorPolicyInterface, 8 - PhabricatorDestructibleInterface { 8 + PhabricatorDestructibleInterface, 9 + PhabricatorProjectInterface, 10 + PhabricatorFulltextInterface, 11 + PhabricatorFerretInterface { 9 12 10 13 protected $name; 11 14 protected $viewPolicy; ··· 55 58 return '/portal/view/'.$this->getID().'/'; 56 59 } 57 60 61 + public function isArchived() { 62 + $status_archived = PhabricatorDashboardPortalStatus::STATUS_ARCHIVED; 63 + return ($this->getStatus() === $status_archived); 64 + } 65 + 58 66 59 67 /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 60 68 ··· 100 108 $this->delete(); 101 109 } 102 110 111 + /* -( PhabricatorFulltextInterface )--------------------------------------- */ 112 + 113 + public function newFulltextEngine() { 114 + return new PhabricatorDashboardPortalFulltextEngine(); 115 + } 116 + 117 + /* -( PhabricatorFerretInterface )----------------------------------------- */ 118 + 119 + public function newFerretEngine() { 120 + return new PhabricatorDashboardPortalFerretEngine(); 121 + } 103 122 104 123 }
+5 -1
src/applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php
··· 27 27 public function buildResults() { 28 28 $query = new PhabricatorDashboardPortalQuery(); 29 29 30 - // TODO: Actually query by name so this scales past 100 portals. 30 + $this->applyFerretConstraints( 31 + $query, 32 + id(new PhabricatorDashboardPortal())->newFerretEngine(), 33 + 'title', 34 + $this->getRawQuery()); 31 35 32 36 $portals = $this->executeQuery($query); 33 37
+9 -33
src/applications/phriction/typeahead/PhrictionDocumentDatasource.php
··· 18 18 public function loadResults() { 19 19 $viewer = $this->getViewer(); 20 20 21 - $raw_query = $this->getRawQuery(); 22 - 23 - $engine = id(new PhrictionDocument()) 24 - ->newFerretEngine(); 25 - 26 - $compiler = id(new PhutilSearchQueryCompiler()) 27 - ->setEnableFunctions(true); 28 - 29 - $raw_tokens = $compiler->newTokens($raw_query); 30 - 31 - $fulltext_tokens = array(); 32 - foreach ($raw_tokens as $raw_token) { 33 - 34 - // This is a little hacky and could maybe be cleaner. We're treating 35 - // every search term as though the user had entered "title:dog" insead 36 - // of "dog". 37 - 38 - $alternate_token = PhutilSearchQueryToken::newFromDictionary( 39 - array( 40 - 'quoted' => $raw_token->isQuoted(), 41 - 'value' => $raw_token->getValue(), 42 - 'operator' => PhutilSearchQueryCompiler::OPERATOR_SUBSTRING, 43 - 'function' => 'title', 44 - )); 21 + $query = id(new PhrictionDocumentQuery()) 22 + ->setViewer($viewer) 23 + ->needContent(true); 45 24 46 - $fulltext_token = id(new PhabricatorFulltextToken()) 47 - ->setToken($alternate_token); 48 - $fulltext_tokens[] = $fulltext_token; 49 - } 25 + $this->applyFerretConstraints( 26 + $query, 27 + id(new PhrictionDocument())->newFerretEngine(), 28 + 'title', 29 + $this->getRawQuery()); 50 30 51 - $documents = id(new PhrictionDocumentQuery()) 52 - ->setViewer($viewer) 53 - ->withFerretConstraint($engine, $fulltext_tokens) 54 - ->needContent(true) 55 - ->execute(); 31 + $documents = $query->execute(); 56 32 57 33 $results = array(); 58 34 foreach ($documents as $document) {
+34
src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
··· 604 604 return mpull($tokens, 'getWireFormat', 'getPHID'); 605 605 } 606 606 607 + final protected function applyFerretConstraints( 608 + PhabricatorCursorPagedPolicyAwareQuery $query, 609 + PhabricatorFerretEngine $engine, 610 + $ferret_function, 611 + $raw_query) { 612 + 613 + $compiler = id(new PhutilSearchQueryCompiler()) 614 + ->setEnableFunctions(true); 615 + 616 + $raw_tokens = $compiler->newTokens($raw_query); 617 + 618 + $fulltext_tokens = array(); 619 + foreach ($raw_tokens as $raw_token) { 620 + // This is a little hacky and could maybe be cleaner. We're treating 621 + // every search term as though the user had entered "title:dog" instead 622 + // of "dog". 623 + 624 + $alternate_token = PhutilSearchQueryToken::newFromDictionary( 625 + array( 626 + 'quoted' => $raw_token->isQuoted(), 627 + 'value' => $raw_token->getValue(), 628 + 'operator' => PhutilSearchQueryCompiler::OPERATOR_SUBSTRING, 629 + 'function' => $ferret_function, 630 + )); 631 + 632 + $fulltext_token = id(new PhabricatorFulltextToken()) 633 + ->setToken($alternate_token); 634 + $fulltext_tokens[] = $fulltext_token; 635 + } 636 + 637 + $query->withFerretConstraint($engine, $fulltext_tokens); 638 + } 639 + 640 + 607 641 }