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

Allow repositories to be associated with projects

Summary: Ref T4264. Ref T2628. Ref T3102. Allows you to associate repositories with projects. In the future, you'll be able to write Herald object rules against projects, use Herald fields like "Repository's projects", and search by project.

Test Plan: See screenshots.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T3102, T4264, T2628

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

+110 -2
+10
src/applications/diffusion/controller/DiffusionRepositoryController.php
··· 165 165 ->setUser($user); 166 166 $view->addProperty(pht('Callsign'), $repository->getCallsign()); 167 167 168 + $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 169 + $repository->getPHID(), 170 + PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT); 171 + if ($project_phids) { 172 + $this->loadHandles($project_phids); 173 + $view->addProperty( 174 + pht('Projects'), 175 + $this->renderHandlesForPHIDs($project_phids)); 176 + } 177 + 168 178 if ($repository->isHosted()) { 169 179 $serve_off = PhabricatorRepository::SERVE_OFF; 170 180 $callsign = $repository->getCallsign();
+21
src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php
··· 16 16 PhabricatorPolicyCapability::CAN_VIEW, 17 17 PhabricatorPolicyCapability::CAN_EDIT, 18 18 )) 19 + ->needProjectPHIDs(true) 19 20 ->withIDs(array($repository->getID())) 20 21 ->executeOne(); 21 22 ··· 33 34 if ($request->isFormPost()) { 34 35 $v_name = $request->getStr('name'); 35 36 $v_desc = $request->getStr('description'); 37 + $v_projects = $request->getArr('projectPHIDs'); 36 38 37 39 if (!strlen($v_name)) { 38 40 $e_name = pht('Required'); ··· 47 49 48 50 $type_name = PhabricatorRepositoryTransaction::TYPE_NAME; 49 51 $type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION; 52 + $type_edge = PhabricatorTransactions::TYPE_EDGE; 50 53 51 54 $xactions[] = id(clone $template) 52 55 ->setTransactionType($type_name) ··· 56 59 ->setTransactionType($type_desc) 57 60 ->setNewValue($v_desc); 58 61 62 + $xactions[] = id(clone $template) 63 + ->setTransactionType($type_edge) 64 + ->setMetadataValue( 65 + 'edge:type', 66 + PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT) 67 + ->setNewValue( 68 + array( 69 + '=' => array_fuse($v_projects), 70 + )); 71 + 59 72 id(new PhabricatorRepositoryEditor()) 60 73 ->setContinueOnNoEffect(true) 61 74 ->setContentSourceFromRequest($request) ··· 78 91 ->setErrors($errors); 79 92 } 80 93 94 + $project_handles = $this->loadViewerHandles($repository->getProjectPHIDs()); 95 + 81 96 $form = id(new AphrontFormView()) 82 97 ->setUser($user) 83 98 ->appendChild( ··· 91 106 ->setName('description') 92 107 ->setLabel(pht('Description')) 93 108 ->setValue($v_desc)) 109 + ->appendChild( 110 + id(new AphrontFormTokenizerControl()) 111 + ->setDatasource('/typeahead/common/projects/') 112 + ->setName('projectPHIDs') 113 + ->setLabel(pht('Projects')) 114 + ->setValue($project_handles)) 94 115 ->appendChild( 95 116 id(new AphrontFormSubmitControl()) 96 117 ->setValue(pht('Save'))
+10
src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
··· 253 253 $view->addProperty(pht('Type'), $type); 254 254 $view->addProperty(pht('Callsign'), $repository->getCallsign()); 255 255 256 + $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 257 + $repository->getPHID(), 258 + PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT); 259 + if ($project_phids) { 260 + $this->loadHandles($project_phids); 261 + $view->addProperty( 262 + pht('Projects'), 263 + $this->renderHandlesForPHIDs($project_phids)); 264 + } 265 + 256 266 $view->addProperty( 257 267 pht('Status'), 258 268 $this->buildRepositoryStatus($repository));
+16 -1
src/applications/diffusion/controller/DiffusionRepositoryListController.php
··· 30 30 31 31 $viewer = $this->getRequest()->getUser(); 32 32 33 + $project_phids = array_fuse( 34 + array_mergev( 35 + mpull($repositories, 'getProjectPHIDs'))); 36 + $project_handles = $this->loadViewerHandles($project_phids); 37 + 33 38 $list = new PHUIObjectItemListView(); 34 39 foreach ($repositories as $repository) { 35 40 $id = $repository->getID(); ··· 49 54 $item->setEpoch($commit->getEpoch()); 50 55 } 51 56 52 - $item->addAttribute( 57 + $item->addIcon( 58 + 'none', 53 59 PhabricatorRepositoryType::getNameForRepositoryType( 54 60 $repository->getVersionControlSystem())); 55 61 ··· 70 76 pht('%s Commit(s)', new PhutilNumber($size)))); 71 77 } else { 72 78 $item->addAttribute(pht('No Commits')); 79 + } 80 + 81 + $handles = array_select_keys( 82 + $project_handles, 83 + $repository->getProjectPHIDs()); 84 + if ($handles) { 85 + $item->addAttribute( 86 + id(new ManiphestTaskProjectsView()) 87 + ->setHandles($handles)); 73 88 } 74 89 75 90 if (!$repository->isTracked()) {
+1
src/applications/repository/editor/PhabricatorRepositoryEditor.php
··· 32 32 $types[] = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL; 33 33 $types[] = PhabricatorRepositoryTransaction::TYPE_DANGEROUS; 34 34 35 + $types[] = PhabricatorTransactions::TYPE_EDGE; 35 36 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 36 37 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 37 38
+27 -1
src/applications/repository/query/PhabricatorRepositoryQuery.php
··· 23 23 24 24 private $needMostRecentCommits; 25 25 private $needCommitCounts; 26 + private $needProjectPHIDs; 26 27 27 28 public function withIDs(array $ids) { 28 29 $this->ids = $ids; ··· 66 67 67 68 public function needMostRecentCommits($need_commits) { 68 69 $this->needMostRecentCommits = $need_commits; 70 + return $this; 71 + } 72 + 73 + public function needProjectPHIDs($need_phids) { 74 + $this->needProjectPHIDs = $need_phids; 69 75 return $this; 70 76 } 71 77 ··· 115 121 $repository->attachMostRecentCommit($commit); 116 122 } 117 123 } 118 - 119 124 120 125 return $repositories; 121 126 } ··· 142 147 break; 143 148 default: 144 149 throw new Exception("Unknown status '{$status}'!"); 150 + } 151 + } 152 + 153 + return $repositories; 154 + } 155 + 156 + public function didFilterPage(array $repositories) { 157 + if ($this->needProjectPHIDs) { 158 + $type_project = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT; 159 + 160 + $edge_query = id(new PhabricatorEdgeQuery()) 161 + ->withSourcePHIDs(mpull($repositories, 'getPHID')) 162 + ->withEdgeTypes(array($type_project)); 163 + $edge_query->execute(); 164 + 165 + foreach ($repositories as $repository) { 166 + $project_phids = $edge_query->getDestinationPHIDs( 167 + array( 168 + $repository->getPHID(), 169 + )); 170 + $repository->attachProjectPHIDs($project_phids); 145 171 } 146 172 } 147 173
+1
src/applications/repository/query/PhabricatorRepositorySearchEngine.php
··· 17 17 18 18 public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 19 19 $query = id(new PhabricatorRepositoryQuery()) 20 + ->needProjectPHIDs(true) 20 21 ->needCommitCounts(true) 21 22 ->needMostRecentCommits(true); 22 23
+10
src/applications/repository/storage/PhabricatorRepository.php
··· 43 43 44 44 private $commitCount = self::ATTACHABLE; 45 45 private $mostRecentCommit = self::ATTACHABLE; 46 + private $projectPHIDs = self::ATTACHABLE; 46 47 47 48 public static function initializeNewRepository(PhabricatorUser $actor) { 48 49 $app = id(new PhabricatorApplicationQuery()) ··· 191 192 } 192 193 193 194 return $uri; 195 + } 196 + 197 + public function attachProjectPHIDs(array $project_phids) { 198 + $this->projectPHIDs = $project_phids; 199 + return $this; 200 + } 201 + 202 + public function getProjectPHIDs() { 203 + return $this->assertAttached($this->projectPHIDs); 194 204 } 195 205 196 206
+14
src/infrastructure/edges/constants/PhabricatorEdgeConfig.php
··· 63 63 const TYPE_OBJECT_USES_CREDENTIAL = 39; 64 64 const TYPE_CREDENTIAL_USED_BY_OBJECT = 40; 65 65 66 + const TYPE_OBJECT_HAS_PROJECT = 41; 67 + const TYPE_PROJECT_HAS_OBJECT = 42; 68 + 66 69 const TYPE_TEST_NO_CYCLE = 9000; 67 70 68 71 const TYPE_PHOB_HAS_ASANATASK = 80001; ··· 142 145 143 146 self::TYPE_OBJECT_USES_CREDENTIAL => self::TYPE_CREDENTIAL_USED_BY_OBJECT, 144 147 self::TYPE_CREDENTIAL_USED_BY_OBJECT => self::TYPE_OBJECT_USES_CREDENTIAL, 148 + 149 + self::TYPE_OBJECT_HAS_PROJECT => self::TYPE_PROJECT_HAS_OBJECT, 150 + self::TYPE_PROJECT_HAS_OBJECT => self::TYPE_OBJECT_HAS_PROJECT, 145 151 ); 146 152 147 153 return idx($map, $edge_type); ··· 213 219 return '%s edited member(s), added %d: %s; removed %d: %s.'; 214 220 case self::TYPE_MEMBER_OF_PROJ: 215 221 case self::TYPE_COMMIT_HAS_PROJECT: 222 + case self::TYPE_OBJECT_HAS_PROJECT: 216 223 return '%s edited project(s), added %d: %s; removed %d: %s.'; 217 224 case self::TYPE_QUESTION_HAS_VOTING_USER: 218 225 case self::TYPE_ANSWER_HAS_VOTING_USER: ··· 227 234 case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: 228 235 case self::TYPE_FILE_HAS_OBJECT: 229 236 case self::TYPE_CONTRIBUTED_TO_OBJECT: 237 + case self::TYPE_PROJECT_HAS_OBJECT: 230 238 return '%s edited object(s), added %d: %s; removed %d: %s.'; 231 239 case self::TYPE_OBJECT_HAS_UNSUBSCRIBER: 232 240 return '%s edited unsubcriber(s), added %d: %s; removed %d: %s.'; ··· 287 295 return '%s added %d member(s): %s.'; 288 296 case self::TYPE_MEMBER_OF_PROJ: 289 297 case self::TYPE_COMMIT_HAS_PROJECT: 298 + case self::TYPE_OBJECT_HAS_PROJECT: 290 299 return '%s added %d project(s): %s.'; 291 300 case self::TYPE_QUESTION_HAS_VOTING_USER: 292 301 case self::TYPE_ANSWER_HAS_VOTING_USER: ··· 319 328 case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: 320 329 case self::TYPE_FILE_HAS_OBJECT: 321 330 case self::TYPE_CONTRIBUTED_TO_OBJECT: 331 + case self::TYPE_PROJECT_HAS_OBJECT: 322 332 default: 323 333 return '%s added %d object(s): %s.'; 324 334 ··· 356 366 return '%s removed %d member(s): %s.'; 357 367 case self::TYPE_MEMBER_OF_PROJ: 358 368 case self::TYPE_COMMIT_HAS_PROJECT: 369 + case self::TYPE_OBJECT_HAS_PROJECT: 359 370 return '%s removed %d project(s): %s.'; 360 371 case self::TYPE_QUESTION_HAS_VOTING_USER: 361 372 case self::TYPE_ANSWER_HAS_VOTING_USER: ··· 388 399 case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: 389 400 case self::TYPE_FILE_HAS_OBJECT: 390 401 case self::TYPE_CONTRIBUTED_TO_OBJECT: 402 + case self::TYPE_PROJECT_HAS_OBJECT: 391 403 default: 392 404 return '%s removed %d object(s): %s.'; 393 405 ··· 423 435 return '%s updated members of %s.'; 424 436 case self::TYPE_MEMBER_OF_PROJ: 425 437 case self::TYPE_COMMIT_HAS_PROJECT: 438 + case self::TYPE_OBJECT_HAS_PROJECT: 426 439 return '%s updated projects of %s.'; 427 440 case self::TYPE_QUESTION_HAS_VOTING_USER: 428 441 case self::TYPE_ANSWER_HAS_VOTING_USER: ··· 455 468 case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: 456 469 case self::TYPE_FILE_HAS_OBJECT: 457 470 case self::TYPE_CONTRIBUTED_TO_OBJECT: 471 + case self::TYPE_PROJECT_HAS_OBJECT: 458 472 default: 459 473 return '%s updated objects of %s.'; 460 474