@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.

Add repositories to Diviner

Summary: Fixes T8352. Associate Diviner books and atoms with a repository. This relationship is not really surfaced anywhere in the UI but provides metadata that contextualises search results. Depends on D13091.

Test Plan: Ran `diviner generate --repository ARC` and then went to `/diviner/book/arcanist/`.

Reviewers: #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T7703, T8352

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

+262 -19
+5
resources/sql/autopatches/20150616.divinerrepository.sql
··· 1 + ALTER TABLE {$NAMESPACE}_diviner.diviner_livebook 2 + ADD COLUMN repositoryPHID VARBINARY(64) AFTER name; 3 + 4 + ALTER TABLE {$NAMESPACE}_diviner.diviner_livesymbol 5 + ADD COLUMN repositoryPHID VARBINARY(64) AFTER bookPHID;
+10
src/applications/diviner/controller/DivinerAtomController.php
··· 85 85 if ($atom) { 86 86 $this->buildDefined($properties, $symbol); 87 87 $this->buildExtendsAndImplements($properties, $symbol); 88 + $this->buildRepository($properties, $symbol); 88 89 89 90 $warnings = $atom->getWarnings(); 90 91 if ($warnings) { ··· 293 294 pht('Implements'), 294 295 phutil_implode_html(phutil_tag('br'), $items)); 295 296 } 297 + } 298 + 299 + private function buildRepository( 300 + PHUIPropertyListView $view, 301 + DivinerLiveSymbol $symbol) { 302 + 303 + $view->addProperty( 304 + pht('Repository'), 305 + $this->getViewer()->renderHandle($symbol->getRepositoryPHID())); 296 306 } 297 307 298 308 private function renderAtomTag(DivinerLiveSymbol $symbol) {
+10
src/applications/diviner/controller/DivinerBookController.php
··· 14 14 $book = id(new DivinerBookQuery()) 15 15 ->setViewer($viewer) 16 16 ->withNames(array($book_name)) 17 + ->needRepositories(true) 17 18 ->executeOne(); 18 19 19 20 if (!$book) { ··· 42 43 ->setPolicyObject($book) 43 44 ->setEpoch($book->getDateModified()) 44 45 ->addActionLink($action_button); 46 + 47 + // TODO: This could probably look better. 48 + if ($book->getRepositoryPHID()) { 49 + $header->addTag( 50 + id(new PHUITagView()) 51 + ->setType(PHUITagView::TYPE_STATE) 52 + ->setBackgroundColor(PHUITagView::COLOR_BLUE) 53 + ->setName($book->getRepository()->getMonogram())); 54 + } 45 55 46 56 $document = new PHUIDocumentView(); 47 57 $document->setHeader($header);
+10
src/applications/diviner/controller/DivinerBookEditController.php
··· 75 75 ->setName('projectPHIDs') 76 76 ->setLabel(pht('Projects')) 77 77 ->setValue($book->getProjectPHIDs())) 78 + ->appendControl( 79 + id(new AphrontFormTokenizerControl()) 80 + ->setDatasource(new DiffusionRepositoryDatasource()) 81 + ->setName('repositoryPHIDs') 82 + ->setLabel(pht('Repository')) 83 + ->setDisableBehavior(true) 84 + ->setLimit(1) 85 + ->setValue($book->getRepositoryPHID() 86 + ? array($book->getRepositoryPHID()) 87 + : null)) 78 88 ->appendChild( 79 89 id(new AphrontFormPolicyControl()) 80 90 ->setName('viewPolicy')
+23 -5
src/applications/diviner/publisher/DivinerLivePublisher.php
··· 4 4 5 5 private $book; 6 6 7 - private function loadBook() { 7 + protected function getBook() { 8 8 if (!$this->book) { 9 9 $book_name = $this->getConfig('name'); 10 10 ··· 20 20 ->save(); 21 21 } 22 22 23 - $book->setConfigurationData($this->getConfigurationData())->save(); 23 + $conn_w = $book->establishConnection('w'); 24 + $conn_w->openTransaction(); 25 + 26 + $book 27 + ->setRepositoryPHID($this->getRepositoryPHID()) 28 + ->setConfigurationData($this->getConfigurationData()) 29 + ->save(); 30 + 31 + // TODO: This is gross. Without this, the repository won't be updated for 32 + // atoms which have already been published. 33 + queryfx( 34 + $conn_w, 35 + 'UPDATE %T SET repositoryPHID = %s WHERE bookPHID = %s', 36 + id(new DivinerLiveSymbol())->getTableName(), 37 + $this->getRepositoryPHID(), 38 + $book->getPHID()); 39 + 40 + $conn_w->saveTransaction(); 24 41 $this->book = $book; 25 42 26 43 id(new PhabricatorSearchIndexer()) ··· 33 50 private function loadSymbolForAtom(DivinerAtom $atom) { 34 51 $symbol = id(new DivinerAtomQuery()) 35 52 ->setViewer(PhabricatorUser::getOmnipotentUser()) 36 - ->withBookPHIDs(array($this->loadBook()->getPHID())) 53 + ->withBookPHIDs(array($atom->getBook())) 37 54 ->withTypes(array($atom->getType())) 38 55 ->withNames(array($atom->getName())) 39 56 ->withContexts(array($atom->getContext())) ··· 45 62 } 46 63 47 64 return id(new DivinerLiveSymbol()) 48 - ->setBookPHID($this->loadBook()->getPHID()) 65 + ->setBookPHID($this->getBook()->getPHID()) 49 66 ->setType($atom->getType()) 50 67 ->setName($atom->getName()) 51 68 ->setContext($atom->getContext()) ··· 68 85 protected function loadAllPublishedHashes() { 69 86 $symbols = id(new DivinerAtomQuery()) 70 87 ->setViewer(PhabricatorUser::getOmnipotentUser()) 71 - ->withBookPHIDs(array($this->loadBook()->getPHID())) 88 + ->withBookPHIDs(array($this->getBook()->getPHID())) 72 89 ->withGhosts(false) 73 90 ->execute(); 74 91 ··· 113 130 $is_documentable = $this->shouldGenerateDocumentForAtom($atom); 114 131 115 132 $symbol 133 + ->setRepositoryPHID($this->getRepositoryPHID()) 116 134 ->setGraphHash($hash) 117 135 ->setIsDocumentable((int)$is_documentable) 118 136 ->setTitle($ref->getTitle())
+10
src/applications/diviner/publisher/DivinerPublisher.php
··· 9 9 private $config; 10 10 private $symbolReverseMap; 11 11 private $dropCaches; 12 + private $repositoryPHID; 12 13 13 14 final public function setDropCaches($drop_caches) { 14 15 $this->dropCaches = $drop_caches; ··· 161 162 } 162 163 163 164 return true; 165 + } 166 + 167 + final public function getRepositoryPHID() { 168 + return $this->repositoryPHID; 169 + } 170 + 171 + final public function setRepositoryPHID($repository_phid) { 172 + $this->repositoryPHID = $repository_phid; 173 + return $this; 164 174 } 165 175 166 176 }
+46
src/applications/diviner/query/DivinerAtomQuery.php
··· 14 14 private $nodeHashes; 15 15 private $titles; 16 16 private $nameContains; 17 + private $repositoryPHIDs; 17 18 18 19 private $needAtoms; 19 20 private $needExtends; 20 21 private $needChildren; 22 + private $needRepositories; 21 23 22 24 public function withIDs(array $ids) { 23 25 $this->ids = $ids; ··· 106 108 107 109 public function withIsDocumentable($documentable) { 108 110 $this->isDocumentable = $documentable; 111 + return $this; 112 + } 113 + 114 + public function withRepositoryPHIDs(array $repository_phids) { 115 + $this->repositoryPHIDs = $repository_phids; 116 + return $this; 117 + } 118 + 119 + public function needRepositories($need_repositories) { 120 + $this->needRepositories = $need_repositories; 109 121 return $this; 110 122 } 111 123 ··· 125 137 } 126 138 127 139 protected function willFilterPage(array $atoms) { 140 + assert_instances_of($atoms, 'DivinerLiveSymbol'); 141 + 128 142 $books = array_unique(mpull($atoms, 'getBookPHID')); 129 143 130 144 $books = id(new DivinerBookQuery()) ··· 257 271 $this->attachAllChildren($atoms, $children, $this->needExtends); 258 272 } 259 273 274 + if ($this->needRepositories) { 275 + $repositories = id(new PhabricatorRepositoryQuery()) 276 + ->setViewer($this->getViewer()) 277 + ->withPHIDs(mpull($atoms, 'getRepositoryPHID')) 278 + ->execute(); 279 + $repositories = mpull($repositories, null, 'getPHID'); 280 + 281 + foreach ($atoms as $key => $atom) { 282 + if ($atom->getRepositoryPHID() === null) { 283 + $atom->attachRepository(null); 284 + continue; 285 + } 286 + 287 + $repository = idx($repositories, $atom->getRepositoryPHID()); 288 + 289 + if (!$repository) { 290 + $this->didRejectResult($atom); 291 + unset($atom[$key]); 292 + continue; 293 + } 294 + 295 + $atom->attachRepository($repository); 296 + } 297 + } 298 + 260 299 return $atoms; 261 300 } 262 301 ··· 379 418 $conn_r, 380 419 'CONVERT(name USING utf8) LIKE %~', 381 420 $this->nameContains); 421 + } 422 + 423 + if ($this->repositoryPHIDs) { 424 + $where[] = qsprintf( 425 + $conn_r, 426 + 'repositoryPHID IN (%Ls)', 427 + $this->repositoryPHIDs); 382 428 } 383 429 384 430 $where[] = $this->buildPagingClause($conn_r);
+25 -12
src/applications/diviner/query/DivinerAtomSearchEngine.php
··· 14 14 $saved = new PhabricatorSavedQuery(); 15 15 16 16 $saved->setParameter( 17 + 'repositoryPHIDs', 18 + $this->readPHIDsFromRequest($request, 'repositoryPHIDs')); 19 + $saved->setParameter('name', $request->getStr('name')); 20 + $saved->setParameter( 17 21 'types', 18 22 $this->readListFromRequest($request, 'types')); 19 23 20 - $saved->setParameter('name', $request->getStr('name')); 21 - 22 24 return $saved; 23 25 } 24 26 25 27 public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 26 28 $query = id(new DivinerAtomQuery()); 27 29 28 - $types = $saved->getParameter('types'); 29 - if ($types) { 30 - $query->withTypes($types); 30 + $repository_phids = $saved->getParameter('repositoryPHIDs'); 31 + if ($repository_phids) { 32 + $query->withRepositoryPHIDs($repository_phids); 31 33 } 32 34 33 35 $name = $saved->getParameter('name'); ··· 35 37 $query->withNameContains($name); 36 38 } 37 39 40 + $types = $saved->getParameter('types'); 41 + if ($types) { 42 + $query->withTypes($types); 43 + } 44 + 38 45 return $query; 39 46 } 40 47 ··· 42 49 AphrontFormView $form, 43 50 PhabricatorSavedQuery $saved) { 44 51 52 + $form->appendChild( 53 + id(new AphrontFormTextControl()) 54 + ->setLabel(pht('Name Contains')) 55 + ->setName('name') 56 + ->setValue($saved->getParameter('name'))); 57 + 45 58 $all_types = array(); 46 59 foreach (DivinerAtom::getAllTypes() as $type) { 47 60 $all_types[$type] = DivinerAtom::getAtomTypeNameString($type); ··· 59 72 $name, 60 73 isset($types[$type])); 61 74 } 75 + $form->appendChild($type_control); 62 76 63 - $form 64 - ->appendChild( 65 - id(new AphrontFormTextControl()) 66 - ->setLabel(pht('Name Contains')) 67 - ->setName('name') 68 - ->setValue($saved->getParameter('name'))) 69 - ->appendChild($type_control); 77 + $form->appendControl( 78 + id(new AphrontFormTokenizerControl()) 79 + ->setLabel(pht('Repositories')) 80 + ->setName('repositoryPHIDs') 81 + ->setDatasource(new DiffusionRepositoryDatasource()) 82 + ->setValue($saved->getParameter('repositoryPHIDs'))); 70 83 } 71 84 72 85 protected function getURI($path) {
+44
src/applications/diviner/query/DivinerBookQuery.php
··· 5 5 private $ids; 6 6 private $phids; 7 7 private $names; 8 + private $repositoryPHIDs; 8 9 9 10 private $needProjectPHIDs; 11 + private $needRepositories; 10 12 11 13 public function withIDs(array $ids) { 12 14 $this->ids = $ids; ··· 23 25 return $this; 24 26 } 25 27 28 + public function withRepositoryPHIDs(array $repository_phids) { 29 + $this->repositoryPHIDs = $repository_phids; 30 + return $this; 31 + } 32 + 26 33 public function needProjectPHIDs($need_phids) { 27 34 $this->needProjectPHIDs = $need_phids; 28 35 return $this; 29 36 } 30 37 38 + public function needRepositories($need_repositories) { 39 + $this->needRepositories = $need_repositories; 40 + return $this; 41 + } 42 + 31 43 protected function loadPage() { 32 44 $table = new DivinerLiveBook(); 33 45 $conn_r = $table->establishConnection('r'); ··· 46 58 protected function didFilterPage(array $books) { 47 59 assert_instances_of($books, 'DivinerLiveBook'); 48 60 61 + if ($this->needRepositories) { 62 + $repositories = id(new PhabricatorRepositoryQuery()) 63 + ->setViewer($this->getViewer()) 64 + ->withPHIDs(mpull($books, 'getRepositoryPHID')) 65 + ->execute(); 66 + $repositories = mpull($repositories, null, 'getPHID'); 67 + 68 + foreach ($books as $key => $book) { 69 + if ($book->getRepositoryPHID() === null) { 70 + $book->attachRepository(null); 71 + continue; 72 + } 73 + 74 + $repository = idx($repositories, $book->getRepositoryPHID()); 75 + 76 + if (!$repository) { 77 + $this->didRejectResult($book); 78 + unset($books[$key]); 79 + continue; 80 + } 81 + 82 + $book->attachRepository($repository); 83 + } 84 + } 85 + 49 86 if ($this->needProjectPHIDs) { 50 87 $edge_query = id(new PhabricatorEdgeQuery()) 51 88 ->withSourcePHIDs(mpull($books, 'getPHID')) ··· 89 126 $conn_r, 90 127 'name IN (%Ls)', 91 128 $this->names); 129 + } 130 + 131 + if ($this->repositoryPHIDs !== null) { 132 + $where[] = qsprintf( 133 + $conn_r, 134 + 'repositoryPHID IN (%Ls)', 135 + $this->repositoryPHIDs); 92 136 } 93 137 94 138 $where[] = $this->buildPagingClause($conn_r);
+6
src/applications/diviner/search/DivinerAtomSearchIndexer.php
··· 30 30 PhabricatorTime::getNow()); 31 31 32 32 $doc->addRelationship( 33 + PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY, 34 + $atom->getRepositoryPHID(), 35 + PhabricatorRepositoryRepositoryPHIDType::TYPECONST, 36 + PhabricatorTime::getNow()); 37 + 38 + $doc->addRelationship( 33 39 $atom->getGraphHash() 34 40 ? PhabricatorSearchRelationship::RELATIONSHIP_CLOSED 35 41 : PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
+6
src/applications/diviner/search/DivinerBookSearchIndexer.php
··· 18 18 PhabricatorSearchDocumentFieldType::FIELD_BODY, 19 19 $book->getPreface()); 20 20 21 + $doc->addRelationship( 22 + PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY, 23 + $book->getRepositoryPHID(), 24 + PhabricatorRepositoryRepositoryPHIDType::TYPECONST, 25 + $book->getDateCreated()); 26 + 21 27 $this->indexTransactions( 22 28 $doc, 23 29 new DivinerLiveBookTransactionQuery(),
+13 -1
src/applications/diviner/storage/DivinerLiveBook.php
··· 8 8 PhabricatorApplicationTransactionInterface { 9 9 10 10 protected $name; 11 + protected $repositoryPHID; 11 12 protected $viewPolicy; 12 13 protected $editPolicy; 13 14 protected $configurationData = array(); 14 15 15 16 private $projectPHIDs = self::ATTACHABLE; 17 + private $repository = self::ATTACHABLE; 16 18 17 19 protected function getConfiguration() { 18 20 return array( ··· 22 24 ), 23 25 self::CONFIG_COLUMN_SCHEMA => array( 24 26 'name' => 'text64', 27 + 'repositoryPHID' => 'phid?', 25 28 ), 26 29 self::CONFIG_KEY_SCHEMA => array( 27 30 'key_phid' => null, ··· 68 71 return idx($spec, 'name', $group); 69 72 } 70 73 74 + public function attachRepository(PhabricatorRepository $repository = null) { 75 + $this->repository = $repository; 76 + return $this; 77 + } 78 + 79 + public function getRepository() { 80 + return $this->assertAttached($this->repository); 81 + } 82 + 71 83 public function attachProjectPHIDs(array $project_phids) { 72 84 $this->projectPHIDs = $project_phids; 73 85 return $this; ··· 98 110 } 99 111 100 112 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 101 - return false; 113 + return false; 102 114 } 103 115 104 116 public function describeAutomaticCapability($capability) {
+12
src/applications/diviner/storage/DivinerLiveSymbol.php
··· 7 7 PhabricatorDestructibleInterface { 8 8 9 9 protected $bookPHID; 10 + protected $repositoryPHID; 10 11 protected $context; 11 12 protected $type; 12 13 protected $name; ··· 22 23 protected $isDocumentable = 0; 23 24 24 25 private $book = self::ATTACHABLE; 26 + private $repository = self::ATTACHABLE; 25 27 private $atom = self::ATTACHABLE; 26 28 private $extends = self::ATTACHABLE; 27 29 private $children = self::ATTACHABLE; ··· 43 45 'summary' => 'text?', 44 46 'isDocumentable' => 'bool', 45 47 'nodeHash' => 'text64?', 48 + 'repositoryPHID' => 'phid?', 46 49 ), 47 50 self::CONFIG_KEY_SCHEMA => array( 48 51 'key_phid' => null, ··· 91 94 92 95 public function attachBook(DivinerLiveBook $book) { 93 96 $this->book = $book; 97 + return $this; 98 + } 99 + 100 + public function getRepository() { 101 + return $this->assertAttached($this->repository); 102 + } 103 + 104 + public function attachRepository(PhabricatorRepository $repository = null) { 105 + $this->repository = $repository; 94 106 return $this; 95 107 } 96 108
+23
src/applications/diviner/workflow/DivinerGenerateWorkflow.php
··· 25 25 'help' => pht('Specify a subclass of %s.', 'DivinerPublisher'), 26 26 'default' => 'DivinerLivePublisher', 27 27 ), 28 + array( 29 + 'name' => 'repository', 30 + 'param' => 'callsign', 31 + 'help' => pht('Repository that the documentation belongs to.'), 32 + ), 28 33 )); 29 34 } 30 35 ··· 186 191 'DivinerPublisher')); 187 192 } 188 193 $publisher = newv($publisher_class, array()); 194 + 195 + $callsign = $args->getArg('repository'); 196 + $repository = null; 197 + if ($callsign) { 198 + $repository = id(new PhabricatorRepositoryQuery()) 199 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 200 + ->withCallsigns(array($callsign)) 201 + ->executeOne(); 202 + 203 + if (!$repository) { 204 + throw new PhutilArgumentUsageException( 205 + pht( 206 + "Repository '%s' does not exist.", 207 + $callsign)); 208 + } 209 + 210 + $publisher->setRepositoryPHID($repository->getPHID()); 211 + } 189 212 190 213 $this->publishDocumentation($args->getArg('clean'), $publisher); 191 214 }
+19 -1
src/applications/repository/storage/PhabricatorRepository.php
··· 1913 1913 PhabricatorDestructionEngine $engine) { 1914 1914 1915 1915 $this->openTransaction(); 1916 - $this->delete(); 1916 + 1917 + $this->delete(); 1918 + 1919 + $books = id(new DivinerBookQuery()) 1920 + ->setViewer($engine->getViewer()) 1921 + ->withRepositoryPHIDs(array($this->getPHID())) 1922 + ->execute(); 1923 + foreach ($books as $book) { 1924 + $engine->destroyObject($book); 1925 + } 1926 + 1927 + $atoms = id(new DivinerAtomQuery()) 1928 + ->setViewer($engine->getViewer()) 1929 + ->withRepositoryPHIDs(array($this->getPHID())) 1930 + ->execute(); 1931 + foreach ($atoms as $atom) { 1932 + $engine->destroyObject($atom); 1933 + } 1934 + 1917 1935 $this->saveTransaction(); 1918 1936 } 1919 1937