diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 94512f3abd..3ccb046634 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3101,6 +3101,7 @@ phutil_register_library_map(array( 'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php', 'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php', 'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php', + 'ProjectReplyHandler' => 'applications/project/mail/ProjectReplyHandler.php', 'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php', 'ReleephAuthorFieldSpecification' => 'applications/releeph/field/specification/ReleephAuthorFieldSpecification.php', 'ReleephBranch' => 'applications/releeph/storage/ReleephBranch.php', @@ -6670,6 +6671,7 @@ phutil_register_library_map(array( 'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod', 'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase', + 'ProjectReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'QueryFormattingTestCase' => 'PhabricatorTestCase', 'ReleephAuthorFieldSpecification' => 'ReleephFieldSpecification', 'ReleephBranch' => array( diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index 98c4ad1797..502623685c 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -41,7 +41,7 @@ final class PhabricatorProjectTransactionEditor $slugs = $object->getSlugs(); $slugs = mpull($slugs, 'getSlug', 'getSlug'); unset($slugs[$object->getPrimarySlug()]); - return $slugs; + return array_keys($slugs); case PhabricatorProjectTransaction::TYPE_STATUS: return $object->getStatus(); case PhabricatorProjectTransaction::TYPE_IMAGE: @@ -403,11 +403,74 @@ final class PhabricatorProjectTransactionEditor return parent::requireCapabilities($object, $xaction); } - /** - * Note: this is implemented for Feed purposes. - */ + protected function loadEdges( + PhabricatorLiskDAO $object, + array $xactions) { + + $member_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( + $object->getPHID(), + PhabricatorProjectProjectHasMemberEdgeType::EDGECONST); + $object->attachMemberPHIDs($member_phids); + } + + protected function shouldSendMail( + PhabricatorLiskDAO $object, + array $xactions) { + return true; + } + + protected function getMailSubjectPrefix() { + return pht('[Project]'); + } + protected function getMailTo(PhabricatorLiskDAO $object) { - return array(); + return $object->getMemberPHIDs(); + } + + protected function getMailCC(PhabricatorLiskDAO $object) { + $all = parent::getMailCC($object); + return array_diff($all, $object->getMemberPHIDs()); + } + + public function getMailTagsMap() { + return array( + PhabricatorProjectTransaction::MAILTAG_METADATA => + pht('Project name, hashtags, icon, image, or color changes.'), + PhabricatorProjectTransaction::MAILTAG_MEMBERS => + pht('Project membership changes.'), + PhabricatorProjectTransaction::MAILTAG_WATCHERS => + pht('Project watcher list changes.'), + PhabricatorProjectTransaction::MAILTAG_OTHER => + pht('Other project activity not listed above occurs.'), + ); + } + + protected function buildReplyHandler(PhabricatorLiskDAO $object) { + return id(new ProjectReplyHandler()) + ->setMailReceiver($object); + } + + protected function buildMailTemplate(PhabricatorLiskDAO $object) { + $id = $object->getID(); + $name = $object->getName(); + + return id(new PhabricatorMetaMTAMail()) + ->setSubject("{$name}") + ->addHeader('Thread-Topic', "Project {$id}"); + } + + protected function buildMailBody( + PhabricatorLiskDAO $object, + array $xactions) { + + $body = parent::buildMailBody($object, $xactions); + + $uri = '/project/profile/'.$object->getID().'/'; + $body->addLinkSection( + pht('PROJECT DETAIL'), + PhabricatorEnv::getProductionURI($uri)); + + return $body; } protected function shouldPublishFeedStory( diff --git a/src/applications/project/mail/ProjectReplyHandler.php b/src/applications/project/mail/ProjectReplyHandler.php new file mode 100644 index 0000000000..ac2cf2b6eb --- /dev/null +++ b/src/applications/project/mail/ProjectReplyHandler.php @@ -0,0 +1,20 @@ +renderHandleLink($old), $this->renderHandleLink($new)); } + break; case self::TYPE_ICON: return pht( '%s set this project\'s icon to %s.', $author_handle, PhabricatorProjectIcon::getLabel($new)); + break; case self::TYPE_COLOR: return pht( '%s set this project\'s color to %s.', $author_handle, PHUITagView::getShadeName($new)); + break; case self::TYPE_LOCKED: if ($new) { @@ -157,6 +169,7 @@ final class PhabricatorProjectTransaction '%s unlocked this project\'s membership.', $author_handle); } + break; case self::TYPE_SLUGS: $add = array_diff($new, $old); @@ -183,6 +196,7 @@ final class PhabricatorProjectTransaction count($rem), $this->renderSlugList($rem)); } + break; case self::TYPE_MEMBERS: $add = array_diff($new, $old); @@ -221,6 +235,7 @@ final class PhabricatorProjectTransaction $this->renderHandleList($rem)); } } + break; } return parent::getTitle(); @@ -339,12 +354,43 @@ final class PhabricatorProjectTransaction $object_handle, $this->renderSlugList($rem)); } - } return parent::getTitleForFeed(); } + public function getMailTags() { + $tags = array(); + switch ($this->getTransactionType()) { + case self::TYPE_NAME: + case self::TYPE_SLUGS: + case self::TYPE_IMAGE: + case self::TYPE_ICON: + case self::TYPE_COLOR: + $tags[] = self::MAILTAG_METADATA; + break; + case PhabricatorTransactions::TYPE_EDGE: + $type = $this->getMetadata('edge:type'); + $type = head($type); + $type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; + $type_watcher = PhabricatorObjectHasWatcherEdgeType::EDGECONST; + if ($type == $type_member) { + $tags[] = self::MAILTAG_MEMBERS; + } else if ($type == $type_watcher) { + $tags[] = self::MAILTAG_WATCHERS; + } else { + $tags[] = self::MAILTAG_OTHER; + } + break; + case self::TYPE_STATUS: + case self::TYPE_LOCKED: + default: + $tags[] = self::MAILTAG_OTHER; + break; + } + return $tags; + } + private function renderSlugList($slugs) { return implode(', ', $slugs); } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index dfd4bba286..6b070efaf9 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -840,6 +840,8 @@ abstract class PhabricatorApplicationTransactionEditor // subscribers to pick up changes caused by Herald (or by other side effects // in various transaction phases). $this->loadSubscribers($object); + // Hook for other edges that may need (re-)loading + $this->loadEdges($object, $xactions); $this->loadHandles($xactions); @@ -965,6 +967,12 @@ abstract class PhabricatorApplicationTransactionEditor } } + protected function loadEdges( + PhabricatorLiskDAO $object, + array $xactions) { + return; + } + private function validateEditParameters( PhabricatorLiskDAO $object, array $xactions) {