diff --git a/src/applications/diffusion/controller/commit/DiffusionCommitController.php b/src/applications/diffusion/controller/commit/DiffusionCommitController.php
index 414f0494b8..73e8a8d3c0 100644
--- a/src/applications/diffusion/controller/commit/DiffusionCommitController.php
+++ b/src/applications/diffusion/controller/commit/DiffusionCommitController.php
@@ -270,7 +270,11 @@ final class DiffusionCommitController extends DiffusionController {
assert_instances_of($parents, 'PhabricatorRepositoryCommit');
$user = $this->getRequest()->getUser();
- $phids = array();
+ $task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
+ $commit->getPHID(),
+ PhabricatorEdgeConfig::TYPE_COMMIT_HAS_TASK);
+
+ $phids = $task_phids;
if ($data->getCommitDetail('authorPHID')) {
$phids[] = $data->getCommitDetail('authorPHID');
}
@@ -333,6 +337,7 @@ final class DiffusionCommitController extends DiffusionController {
$props['Parents'] = implode(' · ', $parent_links);
}
+
$request = $this->getDiffusionRequest();
$contains = DiffusionContainsQuery::newFromDiffusionRequest($request);
@@ -345,6 +350,15 @@ final class DiffusionCommitController extends DiffusionController {
$props['Branches'] = $branches;
}
+ if ($task_phids) {
+ $task_list = array();
+ foreach ($task_phids as $phid) {
+ $task_list[] = $handles[$phid]->renderLink();
+ }
+ $task_list = implode('
', $task_list);
+ $props['Tasks'] = $task_list;
+ }
+
return $props;
}
@@ -634,18 +648,13 @@ final class DiffusionCommitController extends DiffusionController {
require_celerity_resource('phabricator-object-selector-css');
require_celerity_resource('javelin-behavior-phabricator-object-selector');
- /*
- TODO: Implement this.
-
$action = new AphrontHeadsupActionView();
$action->setName('Edit Maniphest Tasks');
- $action->setURI('/search/attach/'.$commit->getPHID().'/TASK/');
+ $action->setURI('/search/attach/'.$commit->getPHID().'/TASK/edge/');
$action->setWorkflow(true);
$action->setClass('attach-maniphest');
$actions[] = $action;
- */
-
if ($user->getIsAdmin()) {
$action = new AphrontHeadsupActionView();
$action->setName('MetaMTA Transcripts');
diff --git a/src/applications/diffusion/controller/commit/__init__.php b/src/applications/diffusion/controller/commit/__init__.php
index b90af27377..cfcfa786cb 100644
--- a/src/applications/diffusion/controller/commit/__init__.php
+++ b/src/applications/diffusion/controller/commit/__init__.php
@@ -36,6 +36,8 @@ phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
phutil_require_module('phabricator', 'applications/repository/storage/repository');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
+phutil_require_module('phabricator', 'infrastructure/edges/constants/config');
+phutil_require_module('phabricator', 'infrastructure/edges/query/edge');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'storage/queryfx');
diff --git a/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php
index 582a80a1c0..ffa02ccbaa 100644
--- a/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php
+++ b/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php
@@ -51,7 +51,11 @@ final class ManiphestTaskDetailController extends ManiphestController {
'taskID = %d ORDER BY id ASC',
$task->getID());
- $phids = array();
+ $commit_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
+ $task->getPHID(),
+ PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT);
+
+ $phids = array_fill_keys($commit_phids, true);
foreach ($transactions as $transaction) {
foreach ($transaction->extractPHIDs() as $phid) {
$phids[$phid] = true;
@@ -168,6 +172,15 @@ final class ManiphestTaskDetailController extends ManiphestController {
$dict['Revisions'] = $rev_links;
}
+ if ($commit_phids) {
+ $commit_links = array();
+ foreach ($commit_phids as $phid) {
+ $commit_links[] = $handles[$phid]->renderLink();
+ }
+ $commit_links = implode('
', $commit_links);
+ $dict['Commits'] = $commit_links;
+ }
+
$file_infos = idx($attached, PhabricatorPHIDConstants::PHID_TYPE_FILE);
if ($file_infos) {
$file_phids = array_keys($file_infos);
diff --git a/src/applications/maniphest/controller/taskdetail/__init__.php b/src/applications/maniphest/controller/taskdetail/__init__.php
index 4bd8605259..a2e37fc8bd 100644
--- a/src/applications/maniphest/controller/taskdetail/__init__.php
+++ b/src/applications/maniphest/controller/taskdetail/__init__.php
@@ -23,6 +23,8 @@ phutil_require_module('phabricator', 'applications/markup/engine');
phutil_require_module('phabricator', 'applications/phid/constants');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
+phutil_require_module('phabricator', 'infrastructure/edges/constants/config');
+phutil_require_module('phabricator', 'infrastructure/edges/query/edge');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'view/form/base');
diff --git a/src/applications/search/controller/attach/PhabricatorSearchAttachController.php b/src/applications/search/controller/attach/PhabricatorSearchAttachController.php
index 97ae5a8bb0..9037b40567 100644
--- a/src/applications/search/controller/attach/PhabricatorSearchAttachController.php
+++ b/src/applications/search/controller/attach/PhabricatorSearchAttachController.php
@@ -29,6 +29,7 @@ final class PhabricatorSearchAttachController
const ACTION_ATTACH = 'attach';
const ACTION_MERGE = 'merge';
const ACTION_DEPENDENCIES = 'dependencies';
+ const ACTION_EDGE = 'edge';
public function willProcessRequest(array $data) {
$this->phid = $data['phid'];
@@ -64,6 +65,16 @@ final class PhabricatorSearchAttachController
case self::ACTION_MERGE:
return $this->performMerge($object, $handle, $phids);
+ case self::ACTION_EDGE:
+ $edge_type = $this->getEdgeType($object_type, $attach_type);
+
+ $editor = id(new PhabricatorEdgeEditor());
+ foreach ($phids as $phid) {
+ $editor->addEdge($this->phid, $edge_type, $phid);
+ }
+ $editor->save();
+
+ return id(new AphrontReloadResponse())->setURI($handle->getURI());
case self::ACTION_DEPENDENCIES:
case self::ACTION_ATTACH:
$two_way = true;
@@ -94,6 +105,13 @@ final class PhabricatorSearchAttachController
case self::ACTION_DEPENDENCIES:
$phids = $object->getAttachedPHIDs($attach_type);
break;
+ case self::ACTION_EDGE:
+ $edge_type = $this->getEdgeType($object_type, $attach_type);
+
+ $phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
+ $this->phid,
+ $edge_type);
+ break;
default:
$phids = array();
break;
@@ -202,9 +220,14 @@ final class PhabricatorSearchAttachController
$noun = 'Tasks';
$selected = 'assigned';
break;
+ case PhabricatorPHIDConstants::PHID_TYPE_CMIT:
+ $noun = 'Commits';
+ $selected = 'created';
+ break;
}
switch ($this->action) {
+ case self::ACTION_EDGE:
case self::ACTION_ATTACH:
$dialog_title = "Manage Attached {$noun}";
$header_text = "Currently Attached {$noun}";
@@ -272,4 +295,24 @@ final class PhabricatorSearchAttachController
}
}
+ private function getEdgeType($src_type, $dst_type) {
+ $t_cmit = PhabricatorPHIDConstants::PHID_TYPE_CMIT;
+ $t_task = PhabricatorPHIDConstants::PHID_TYPE_TASK;
+
+ $map = array(
+ $t_cmit => array(
+ $t_task => PhabricatorEdgeConfig::TYPE_COMMIT_HAS_TASK,
+ ),
+ $t_task => array(
+ $t_cmit => PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT,
+ ),
+ );
+
+ if (empty($map[$src_type][$dst_type])) {
+ throw new Exception("Unknown edge type!");
+ }
+
+ return $map[$src_type][$dst_type];
+ }
+
}
diff --git a/src/applications/search/controller/attach/__init__.php b/src/applications/search/controller/attach/__init__.php
index 95997943f5..6ebca02a96 100644
--- a/src/applications/search/controller/attach/__init__.php
+++ b/src/applications/search/controller/attach/__init__.php
@@ -19,6 +19,9 @@ phutil_require_module('phabricator', 'applications/phid/graph');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'applications/search/controller/base');
phutil_require_module('phabricator', 'applications/search/editor/attach');
+phutil_require_module('phabricator', 'infrastructure/edges/constants/config');
+phutil_require_module('phabricator', 'infrastructure/edges/editor/edge');
+phutil_require_module('phabricator', 'infrastructure/edges/query/edge');
phutil_require_module('phabricator', 'view/control/objectselector');
phutil_require_module('phutil', 'utils');
diff --git a/src/infrastructure/edges/query/edge/PhabricatorEdgeQuery.php b/src/infrastructure/edges/query/edge/PhabricatorEdgeQuery.php
index bef97ebd9a..fdeb8fda8b 100644
--- a/src/infrastructure/edges/query/edge/PhabricatorEdgeQuery.php
+++ b/src/infrastructure/edges/query/edge/PhabricatorEdgeQuery.php
@@ -92,6 +92,24 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery {
/* -( Executing the Query )------------------------------------------------ */
+ /**
+ * Convenience method for loading destination PHIDs with one source and one
+ * edge type. Equivalent to building a full query, but simplifies a common
+ * use case.
+ *
+ * @param phid Source PHID.
+ * @param const Edge type.
+ * @return list List of destination PHIDs.
+ */
+ public static function loadDestinationPHIDs($src_phid, $edge_type) {
+ $edges = id(new PhabricatorEdgeQuery())
+ ->withSourcePHIDs(array($src_phid))
+ ->withEdgeTypes(array($edge_type))
+ ->execute();
+ return array_keys($edges[$src_phid][$edge_type]);
+ }
+
+
/**
* Load specified edges.
*