diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index f1fa375736..d2ee235162 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -165,6 +165,16 @@ final class DiffusionRepositoryController extends DiffusionController { ->setUser($user); $view->addProperty(pht('Callsign'), $repository->getCallsign()); + $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( + $repository->getPHID(), + PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT); + if ($project_phids) { + $this->loadHandles($project_phids); + $view->addProperty( + pht('Projects'), + $this->renderHandlesForPHIDs($project_phids)); + } + if ($repository->isHosted()) { $serve_off = PhabricatorRepository::SERVE_OFF; $callsign = $repository->getCallsign(); diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php index 7729b5d164..f946b4dcdd 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php @@ -16,6 +16,7 @@ final class DiffusionRepositoryEditBasicController PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) + ->needProjectPHIDs(true) ->withIDs(array($repository->getID())) ->executeOne(); @@ -33,6 +34,7 @@ final class DiffusionRepositoryEditBasicController if ($request->isFormPost()) { $v_name = $request->getStr('name'); $v_desc = $request->getStr('description'); + $v_projects = $request->getArr('projectPHIDs'); if (!strlen($v_name)) { $e_name = pht('Required'); @@ -47,6 +49,7 @@ final class DiffusionRepositoryEditBasicController $type_name = PhabricatorRepositoryTransaction::TYPE_NAME; $type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION; + $type_edge = PhabricatorTransactions::TYPE_EDGE; $xactions[] = id(clone $template) ->setTransactionType($type_name) @@ -56,6 +59,16 @@ final class DiffusionRepositoryEditBasicController ->setTransactionType($type_desc) ->setNewValue($v_desc); + $xactions[] = id(clone $template) + ->setTransactionType($type_edge) + ->setMetadataValue( + 'edge:type', + PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT) + ->setNewValue( + array( + '=' => array_fuse($v_projects), + )); + id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) @@ -78,6 +91,8 @@ final class DiffusionRepositoryEditBasicController ->setErrors($errors); } + $project_handles = $this->loadViewerHandles($repository->getProjectPHIDs()); + $form = id(new AphrontFormView()) ->setUser($user) ->appendChild( @@ -91,6 +106,12 @@ final class DiffusionRepositoryEditBasicController ->setName('description') ->setLabel(pht('Description')) ->setValue($v_desc)) + ->appendChild( + id(new AphrontFormTokenizerControl()) + ->setDatasource('/typeahead/common/projects/') + ->setName('projectPHIDs') + ->setLabel(pht('Projects')) + ->setValue($project_handles)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save')) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php index 9c5cd93331..15ba1f2e73 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php @@ -253,6 +253,16 @@ final class DiffusionRepositoryEditMainController $view->addProperty(pht('Type'), $type); $view->addProperty(pht('Callsign'), $repository->getCallsign()); + $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( + $repository->getPHID(), + PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT); + if ($project_phids) { + $this->loadHandles($project_phids); + $view->addProperty( + pht('Projects'), + $this->renderHandlesForPHIDs($project_phids)); + } + $view->addProperty( pht('Status'), $this->buildRepositoryStatus($repository)); diff --git a/src/applications/diffusion/controller/DiffusionRepositoryListController.php b/src/applications/diffusion/controller/DiffusionRepositoryListController.php index 6883abdf6e..920d165e6f 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryListController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryListController.php @@ -30,6 +30,11 @@ final class DiffusionRepositoryListController extends DiffusionController $viewer = $this->getRequest()->getUser(); + $project_phids = array_fuse( + array_mergev( + mpull($repositories, 'getProjectPHIDs'))); + $project_handles = $this->loadViewerHandles($project_phids); + $list = new PHUIObjectItemListView(); foreach ($repositories as $repository) { $id = $repository->getID(); @@ -49,7 +54,8 @@ final class DiffusionRepositoryListController extends DiffusionController $item->setEpoch($commit->getEpoch()); } - $item->addAttribute( + $item->addIcon( + 'none', PhabricatorRepositoryType::getNameForRepositoryType( $repository->getVersionControlSystem())); @@ -72,6 +78,15 @@ final class DiffusionRepositoryListController extends DiffusionController $item->addAttribute(pht('No Commits')); } + $handles = array_select_keys( + $project_handles, + $repository->getProjectPHIDs()); + if ($handles) { + $item->addAttribute( + id(new ManiphestTaskProjectsView()) + ->setHandles($handles)); + } + if (!$repository->isTracked()) { $item->setDisabled(true); $item->addIcon('disable-grey', pht('Inactive')); diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php index f7af1a3598..19a747cbc0 100644 --- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -32,6 +32,7 @@ final class PhabricatorRepositoryEditor $types[] = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL; $types[] = PhabricatorRepositoryTransaction::TYPE_DANGEROUS; + $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php index 327fe36ba8..dad3595745 100644 --- a/src/applications/repository/query/PhabricatorRepositoryQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php @@ -23,6 +23,7 @@ final class PhabricatorRepositoryQuery private $needMostRecentCommits; private $needCommitCounts; + private $needProjectPHIDs; public function withIDs(array $ids) { $this->ids = $ids; @@ -69,6 +70,11 @@ final class PhabricatorRepositoryQuery return $this; } + public function needProjectPHIDs($need_phids) { + $this->needProjectPHIDs = $need_phids; + return $this; + } + public function setOrder($order) { $this->order = $order; return $this; @@ -116,7 +122,6 @@ final class PhabricatorRepositoryQuery } } - return $repositories; } @@ -148,6 +153,27 @@ final class PhabricatorRepositoryQuery return $repositories; } + public function didFilterPage(array $repositories) { + if ($this->needProjectPHIDs) { + $type_project = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT; + + $edge_query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(mpull($repositories, 'getPHID')) + ->withEdgeTypes(array($type_project)); + $edge_query->execute(); + + foreach ($repositories as $repository) { + $project_phids = $edge_query->getDestinationPHIDs( + array( + $repository->getPHID(), + )); + $repository->attachProjectPHIDs($project_phids); + } + } + + return $repositories; + } + public function getReversePaging() { switch ($this->order) { case self::ORDER_CALLSIGN: diff --git a/src/applications/repository/query/PhabricatorRepositorySearchEngine.php b/src/applications/repository/query/PhabricatorRepositorySearchEngine.php index 41dff1056e..98fb1af778 100644 --- a/src/applications/repository/query/PhabricatorRepositorySearchEngine.php +++ b/src/applications/repository/query/PhabricatorRepositorySearchEngine.php @@ -17,6 +17,7 @@ final class PhabricatorRepositorySearchEngine public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { $query = id(new PhabricatorRepositoryQuery()) + ->needProjectPHIDs(true) ->needCommitCounts(true) ->needMostRecentCommits(true); diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 64ce761e72..bcfd48c2b6 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -43,6 +43,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO private $commitCount = self::ATTACHABLE; private $mostRecentCommit = self::ATTACHABLE; + private $projectPHIDs = self::ATTACHABLE; public static function initializeNewRepository(PhabricatorUser $actor) { $app = id(new PhabricatorApplicationQuery()) @@ -193,6 +194,15 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO return $uri; } + public function attachProjectPHIDs(array $project_phids) { + $this->projectPHIDs = $project_phids; + return $this; + } + + public function getProjectPHIDs() { + return $this->assertAttached($this->projectPHIDs); + } + /* -( Remote Command Execution )------------------------------------------- */ diff --git a/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php b/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php index 16b33f373c..81e851625f 100644 --- a/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php +++ b/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php @@ -63,6 +63,9 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { const TYPE_OBJECT_USES_CREDENTIAL = 39; const TYPE_CREDENTIAL_USED_BY_OBJECT = 40; + const TYPE_OBJECT_HAS_PROJECT = 41; + const TYPE_PROJECT_HAS_OBJECT = 42; + const TYPE_TEST_NO_CYCLE = 9000; const TYPE_PHOB_HAS_ASANATASK = 80001; @@ -142,6 +145,9 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { self::TYPE_OBJECT_USES_CREDENTIAL => self::TYPE_CREDENTIAL_USED_BY_OBJECT, self::TYPE_CREDENTIAL_USED_BY_OBJECT => self::TYPE_OBJECT_USES_CREDENTIAL, + + self::TYPE_OBJECT_HAS_PROJECT => self::TYPE_PROJECT_HAS_OBJECT, + self::TYPE_PROJECT_HAS_OBJECT => self::TYPE_OBJECT_HAS_PROJECT, ); return idx($map, $edge_type); @@ -213,6 +219,7 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { return '%s edited member(s), added %d: %s; removed %d: %s.'; case self::TYPE_MEMBER_OF_PROJ: case self::TYPE_COMMIT_HAS_PROJECT: + case self::TYPE_OBJECT_HAS_PROJECT: return '%s edited project(s), added %d: %s; removed %d: %s.'; case self::TYPE_QUESTION_HAS_VOTING_USER: case self::TYPE_ANSWER_HAS_VOTING_USER: @@ -227,6 +234,7 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: case self::TYPE_CONTRIBUTED_TO_OBJECT: + case self::TYPE_PROJECT_HAS_OBJECT: return '%s edited object(s), added %d: %s; removed %d: %s.'; case self::TYPE_OBJECT_HAS_UNSUBSCRIBER: return '%s edited unsubcriber(s), added %d: %s; removed %d: %s.'; @@ -287,6 +295,7 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { return '%s added %d member(s): %s.'; case self::TYPE_MEMBER_OF_PROJ: case self::TYPE_COMMIT_HAS_PROJECT: + case self::TYPE_OBJECT_HAS_PROJECT: return '%s added %d project(s): %s.'; case self::TYPE_QUESTION_HAS_VOTING_USER: case self::TYPE_ANSWER_HAS_VOTING_USER: @@ -319,6 +328,7 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: case self::TYPE_CONTRIBUTED_TO_OBJECT: + case self::TYPE_PROJECT_HAS_OBJECT: default: return '%s added %d object(s): %s.'; @@ -356,6 +366,7 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { return '%s removed %d member(s): %s.'; case self::TYPE_MEMBER_OF_PROJ: case self::TYPE_COMMIT_HAS_PROJECT: + case self::TYPE_OBJECT_HAS_PROJECT: return '%s removed %d project(s): %s.'; case self::TYPE_QUESTION_HAS_VOTING_USER: case self::TYPE_ANSWER_HAS_VOTING_USER: @@ -388,6 +399,7 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: case self::TYPE_CONTRIBUTED_TO_OBJECT: + case self::TYPE_PROJECT_HAS_OBJECT: default: return '%s removed %d object(s): %s.'; @@ -423,6 +435,7 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { return '%s updated members of %s.'; case self::TYPE_MEMBER_OF_PROJ: case self::TYPE_COMMIT_HAS_PROJECT: + case self::TYPE_OBJECT_HAS_PROJECT: return '%s updated projects of %s.'; case self::TYPE_QUESTION_HAS_VOTING_USER: case self::TYPE_ANSWER_HAS_VOTING_USER: @@ -455,6 +468,7 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: case self::TYPE_CONTRIBUTED_TO_OBJECT: + case self::TYPE_PROJECT_HAS_OBJECT: default: return '%s updated objects of %s.';