1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 01:08:50 +02:00

Projects - add mail to project updates

Summary:
...which lets all the fancy settings for Email | Notify | Off be possible. Fixes T8164. Wasn't too sure the best way to break things up but members vs watchers felt meaningful to break out to me.

Also fixes a small bug where we were generating bad slug updated stories by messing with the signature of the slug data. Perhaps this fix isn't even good enough (the array_keys()) call and instead we'll need to implement transaction has effect and do a sort?

Test Plan: used ./bin/mail list-outbound and ./bin/mail show-outbound --id XX to verify reasonable emails were being generated. saw new preferences in settings.

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T8164

Differential Revision: https://secure.phabricator.com/D12868
This commit is contained in:
Bob Trahan 2015-05-15 16:33:31 -07:00
parent 3ef0721ada
commit fa82c17079
5 changed files with 145 additions and 6 deletions

View file

@ -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(

View file

@ -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(

View file

@ -0,0 +1,20 @@
<?php
final class ProjectReplyHandler
extends PhabricatorApplicationTransactionReplyHandler {
public function validateMailReceiver($mail_receiver) {
if (!($mail_receiver instanceof PhabricatorProject)) {
throw new Exception('Mail receiver is not a PhabricatorProject.');
}
}
public function getObjectPrefix() {
return PhabricatorProjectProjectPHIDType::TYPECONST;
}
protected function shouldCreateCommentFromMailBody() {
return false;
}
}

View file

@ -14,6 +14,11 @@ final class PhabricatorProjectTransaction
// NOTE: This is deprecated, members are just a normal edge now.
const TYPE_MEMBERS = 'project:members';
const MAILTAG_METADATA = 'project-metadata';
const MAILTAG_MEMBERS = 'project-members';
const MAILTAG_WATCHERS = 'project-watchers';
const MAILTAG_OTHER = 'project-other';
public function getApplicationName() {
return 'project';
}
@ -106,6 +111,8 @@ final class PhabricatorProjectTransaction
$old,
$new);
}
break;
case self::TYPE_STATUS:
if ($old == 0) {
return pht(
@ -116,6 +123,8 @@ final class PhabricatorProjectTransaction
'%s activated this project.',
$author_handle);
}
break;
case self::TYPE_IMAGE:
// TODO: Some day, it would be nice to show the images.
if (!$old) {
@ -134,18 +143,21 @@ final class PhabricatorProjectTransaction
$this->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);
}

View file

@ -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) {