From 637e3f38f3a03de5c60f12a707329867fb47b552 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jan 2014 12:24:09 -0800 Subject: [PATCH] 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 --- .../DiffusionRepositoryController.php | 10 +++++++ ...DiffusionRepositoryEditBasicController.php | 21 ++++++++++++++ .../DiffusionRepositoryEditMainController.php | 10 +++++++ .../DiffusionRepositoryListController.php | 17 ++++++++++- .../editor/PhabricatorRepositoryEditor.php | 1 + .../query/PhabricatorRepositoryQuery.php | 28 ++++++++++++++++++- .../PhabricatorRepositorySearchEngine.php | 1 + .../storage/PhabricatorRepository.php | 10 +++++++ .../edges/constants/PhabricatorEdgeConfig.php | 14 ++++++++++ 9 files changed, 110 insertions(+), 2 deletions(-) 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.';