diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1957,6 +1957,7 @@ 'PhabricatorProjectEditPictureController' => 'applications/project/controller/PhabricatorProjectEditPictureController.php', 'PhabricatorProjectEditorTestCase' => 'applications/project/editor/__tests__/PhabricatorProjectEditorTestCase.php', 'PhabricatorProjectIcon' => 'applications/project/icon/PhabricatorProjectIcon.php', + 'PhabricatorProjectInterface' => 'applications/project/interface/PhabricatorProjectInterface.php', 'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.php', 'PhabricatorProjectMembersEditController' => 'applications/project/controller/PhabricatorProjectMembersEditController.php', 'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php', @@ -1975,6 +1976,7 @@ 'PhabricatorProjectTransaction' => 'applications/project/storage/PhabricatorProjectTransaction.php', 'PhabricatorProjectTransactionEditor' => 'applications/project/editor/PhabricatorProjectTransactionEditor.php', 'PhabricatorProjectTransactionQuery' => 'applications/project/query/PhabricatorProjectTransactionQuery.php', + 'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php', 'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php', 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', 'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php', @@ -4809,6 +4811,7 @@ 'PhabricatorProjectTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorProjectTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorProjectTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener', 'PhabricatorProjectUpdateController' => 'PhabricatorProjectController', 'PhabricatorProjectWatchController' => 'PhabricatorProjectController', 'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions', diff --git a/src/applications/project/application/PhabricatorApplicationProject.php b/src/applications/project/application/PhabricatorApplicationProject.php --- a/src/applications/project/application/PhabricatorApplicationProject.php +++ b/src/applications/project/application/PhabricatorApplicationProject.php @@ -32,6 +32,12 @@ ); } + public function getEventListeners() { + return array( + new PhabricatorProjectUIEventListener(), + ); + } + public function getRoutes() { return array( '/project/' => array( diff --git a/src/applications/project/events/PhabricatorProjectUIEventListener.php b/src/applications/project/events/PhabricatorProjectUIEventListener.php new file mode 100644 --- /dev/null +++ b/src/applications/project/events/PhabricatorProjectUIEventListener.php @@ -0,0 +1,59 @@ +listen(PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES); + } + + public function handleEvent(PhutilEvent $event) { + switch ($event->getType()) { + case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES: + $this->handlePropertyEvent($event); + break; + } + } + + private function handlePropertyEvent($event) { + $user = $event->getUser(); + $object = $event->getValue('object'); + + if (!$object || !$object->getPHID()) { + // No object, or the object has no PHID yet.. + return; + } + + if (!($object instanceof PhabricatorProjectInterface)) { + // This object doesn't have projects. + return; + } + + $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( + $object->getPHID(), + PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT); + if ($project_phids) { + $project_phids = array_reverse($project_phids); + $handles = id(new PhabricatorHandleQuery()) + ->setViewer($user) + ->withPHIDs($project_phids) + ->execute(); + } else { + $handles = array(); + } + + if ($handles) { + $list = array(); + foreach ($handles as $handle) { + $list[] = $handle->renderLink(); + } + $list = phutil_implode_html(phutil_tag('br'), $list); + } else { + $list = phutil_tag('em', array(), pht('None')); + } + + $view = $event->getValue('view'); + $view->addProperty(pht('Projects'), $list); + } + +} diff --git a/src/applications/project/interface/PhabricatorProjectInterface.php b/src/applications/project/interface/PhabricatorProjectInterface.php new file mode 100644 --- /dev/null +++ b/src/applications/project/interface/PhabricatorProjectInterface.php @@ -0,0 +1,5 @@ +object instanceof PhabricatorProjectInterface) { + $types[] = PhabricatorTransactions::TYPE_EDGE; + } + return $types; } @@ -1079,6 +1083,7 @@ // TODO: For now, this is just a placeholder. $engine = PhabricatorMarkupEngine::getEngine('extract'); + $engine->setConfig('viewer', $this->requireActor()); $block_xactions = $this->expandRemarkupBlockTransactions( $object, @@ -1098,11 +1103,41 @@ array $xactions, $blocks, PhutilMarkupEngine $engine) { - return $this->expandCustomRemarkupBlockTransactions( + + $block_xactions = $this->expandCustomRemarkupBlockTransactions( $object, $xactions, $blocks, $engine); + + if ($object instanceof PhabricatorProjectInterface) { + $phids = array(); + foreach ($blocks as $key => $xaction_blocks) { + foreach ($xaction_blocks as $block) { + $engine->markupText($block); + $phids += $engine->getTextMetadata( + PhabricatorRemarkupRuleObject::KEY_MENTIONED_OBJECTS, + array()); + } + } + + $project_type = PhabricatorProjectPHIDTypeProject::TYPECONST; + foreach ($phids as $key => $phid) { + if (phid_get_type($phid) != $project_type) { + unset($phids[$key]); + } + } + + if ($phids) { + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT; + $block_xactions[] = newv(get_class(head($xactions)), array()) + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) + ->setMetadataValue('edge:type', $edge_type) + ->setNewValue(array('+' => $phids)); + } + } + + return $block_xactions; } protected function expandCustomRemarkupBlockTransactions( @@ -1899,13 +1934,12 @@ $has_support = true; } - // TODO: This should be some interface which specifies that the object - // has project associations. - if ($object instanceof ManiphestTask) { + // TODO: The Maniphest legacy stuff should get cleaned up here. + + if (($object instanceof ManiphestTask) || + ($object instanceof PhabricatorProjectInterface)) { - // TODO: This is what normal objects would do, but Maniphest is still - // behind the times. - if (false) { + if ($object instanceof PhabricatorProjectInterface) { $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( $object->getPHID(), PhabricatorEdgeConfig::TYPE_OBJECT_HAS_PROJECT); diff --git a/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php b/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php --- a/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php +++ b/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php @@ -7,6 +7,7 @@ extends PhutilRemarkupRule { const KEY_RULE_OBJECT = 'rule.object'; + const KEY_MENTIONED_OBJECTS = 'rule.object.mentioned'; abstract protected function getObjectNamePrefix(); abstract protected function loadObjects(array $ids); @@ -188,6 +189,12 @@ } } + $phids = $engine->getTextMetadata(self::KEY_MENTIONED_OBJECTS, array()); + foreach ($objects as $object) { + $phids[$object->getPHID()] = $object->getPHID(); + } + $engine->setTextMetadata(self::KEY_MENTIONED_OBJECTS, $phids); + $handles = $this->loadHandles($objects); foreach ($metadata as $key => $spec) { $handle = $handles[$spec['id']];