1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 00:42:41 +01:00

Add mail support to generic transactions

Summary:
  - Adds mail support to the generic transaction construct.
  - Restores mail support to Pholio (now much improved; the mails are actually useful).

Test Plan: Updated a Pholio mock, got mail.

Reviewers: btrahan, chad, vrana

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2104

Differential Revision: https://secure.phabricator.com/D4139
This commit is contained in:
epriestley 2012-12-11 14:00:07 -08:00
parent 7341c74276
commit 1d5ace45bd
4 changed files with 282 additions and 85 deletions

View file

@ -19,7 +19,6 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor {
protected function didApplyTransactions( protected function didApplyTransactions(
PhabricatorLiskDAO $object, PhabricatorLiskDAO $object,
array $xactions) { array $xactions) {
// $this->sendMail($object, $xactions);
// PholioIndexer::indexMock($mock); // PholioIndexer::indexMock($mock);
return; return;
} }
@ -84,92 +83,46 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor {
return parent::mergeTransactions($u, $v); return parent::mergeTransactions($u, $v);
} }
protected function supportsMail() {
return true;
}
private function sendMail( protected function buildReplyHandler(PhabricatorLiskDAO $object) {
PholioMock $mock, return id(new PholioReplyHandler())
array $xactions, ->setMailReceiver($object);
$is_new, }
array $mentioned_phids) {
$subscribed_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID( protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$mock->getPHID()); $id = $object->getID();
$name = $object->getName();
$original_name = $object->getOriginalName();
$email_to = array( return id(new PhabricatorMetaMTAMail())
$mock->getAuthorPHID(), ->setSubject("M{$id}: {$name}")
->addHeader('Thread-Topic', "M{$id}: {$original_name}");
}
protected function getMailTo(PhabricatorLiskDAO $object) {
return array(
$object->getAuthorPHID(),
$this->requireActor()->getPHID(), $this->requireActor()->getPHID(),
); );
$email_cc = $subscribed_phids;
$phids = array_merge($email_to, $email_cc);
$handles = id(new PhabricatorObjectHandleData($phids))
->setViewer($this->requireActor())
->loadHandles();
$mock_id = $mock->getID();
$name = $mock->getName();
$original_name = $mock->getOriginalName();
$thread_id = 'pholio-mock-'.$mock->getPHID();
$mail_tags = $this->getMailTags($mock, $xactions);
$body = new PhabricatorMetaMTAMailBody();
$body->addRawSection('lorem ipsum');
$mock_uri = PhabricatorEnv::getProductionURI('/M'.$mock->getID());
$body->addTextSection(pht('MOCK DETAIL'), $mock_uri);
$reply_handler = $this->buildReplyHandler($mock);
$template = id(new PhabricatorMetaMTAMail())
->setSubject("M{$mock_id}: {$name}")
->setSubjectPrefix($this->getMailSubjectPrefix())
->setVarySubjectPrefix('[edit/create?]')
->setFrom($this->requireActor()->getPHID())
->addHeader('Thread-Topic', "M{$mock_id}: {$original_name}")
->setThreadID($thread_id, $is_new)
->setRelatedPHID($mock->getPHID())
->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs())
->setIsBulk(true)
->setMailTags($mail_tags)
->setBody($body->render());
// TODO
// ->setParentMessageID(...)
$mails = $reply_handler->multiplexMail(
$template,
array_select_keys($handles, $email_to),
array_select_keys($handles, $email_cc));
foreach ($mails as $mail) {
$mail->saveAndSend();
} }
$template->addTos($email_to); protected function buildMailBody(
$template->addCCs($email_cc); PhabricatorLiskDAO $object,
array $xactions) {
return $template; $body = parent::buildMailBody($object, $xactions);
$body->addTextSection(
pht('MOCK DETAIL'),
PhabricatorEnv::getProductionURI('/M'.$object->getID()));
return $body;
} }
private function getMailTags(PholioMock $mock, array $xactions) { protected function getMailSubjectPrefix() {
assert_instances_of($xactions, 'PholioTransaction');
$tags = array();
return $tags;
}
public function buildReplyHandler(PholioMock $mock) {
$handler_object = new PholioReplyHandler();
$handler_object->setMailReceiver($mock);
return $handler_object;
}
private function getMailSubjectPrefix() {
return PhabricatorEnv::getEnvConfig('metamta.pholio.subject-prefix'); return PhabricatorEnv::getEnvConfig('metamta.pholio.subject-prefix');
} }
} }

View file

@ -1,5 +1,8 @@
<?php <?php
/**
* @task mail Sending Mail
*/
abstract class PhabricatorApplicationTransactionEditor abstract class PhabricatorApplicationTransactionEditor
extends PhabricatorEditor { extends PhabricatorEditor {
@ -202,6 +205,10 @@ abstract class PhabricatorApplicationTransactionEditor
} }
} }
if (!$xactions) {
return $this;
}
$xactions = $this->sortTransactions($xactions); $xactions = $this->sortTransactions($xactions);
$comment_editor = id(new PhabricatorApplicationTransactionCommentEditor()) $comment_editor = id(new PhabricatorApplicationTransactionCommentEditor())
@ -230,7 +237,15 @@ abstract class PhabricatorApplicationTransactionEditor
} }
$object->saveTransaction(); $object->saveTransaction();
// TODO: Send mail.
$this->loadHandles($xactions);
$mail = null;
if ($this->supportsMail()) {
$mail = $this->sendMail($object, $xactions);
}
// TODO: Index object. // TODO: Index object.
// TODO: Publish feed/notifications. // TODO: Publish feed/notifications.
@ -239,6 +254,26 @@ abstract class PhabricatorApplicationTransactionEditor
return $this; return $this;
} }
private function loadHandles(array $xactions) {
$phids = array();
foreach ($xactions as $xaction) {
$phids[$xaction->getPHID()] = $xaction->getRequiredHandlePHIDs();
}
$handles = array();
$merged = array_mergev($phids);
if ($merged) {
$handles = id(new PhabricatorObjectHandleData($merged))
->setViewer($this->requireActor())
->loadHandles();
}
foreach ($xactions as $xaction) {
$xaction->setHandles(
array_select_keys(
$handles,
$phids[$xaction->getPHID()]));
}
}
private function validateEditParameters( private function validateEditParameters(
PhabricatorLiskDAO $object, PhabricatorLiskDAO $object,
array $xactions) { array $xactions) {
@ -484,4 +519,168 @@ abstract class PhabricatorApplicationTransactionEditor
} }
/* -( Sending Mail )------------------------------------------------------- */
/**
* @task mail
*/
protected function supportsMail() {
return false;
}
/**
* @task mail
*/
protected function sendMail(
PhabricatorLiskDAO $object,
array $xactions) {
$email_to = $this->getMailTo($object);
$email_cc = $this->getMailCC($object);
$phids = array_merge($email_to, $email_cc);
$handles = id(new PhabricatorObjectHandleData($phids))
->setViewer($this->requireActor())
->loadHandles();
$template = $this->buildMailTemplate($object);
$body = $this->buildMailBody($object, $xactions);
$mail_tags = $this->getMailTags($object, $xactions);
$action = $this->getStrongestAction($object, $xactions);
$template
->setFrom($this->requireActor()->getPHID())
->setSubjectPrefix($this->getMailSubjectPrefix())
->setVarySubjectPrefix('['.$action.']')
->setThreadID($object->getPHID(), $this->getIsNewObject())
->setRelatedPHID($object->getPHID())
->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs())
->setMailTags($mail_tags)
->setIsBulk(true)
->setBody($body->render());
// TODO
// ->setParentMessageID(...)
$mails = $this
->buildReplyHandler($object)
->multiplexMail(
$template,
array_select_keys($handles, $email_to),
array_select_keys($handles, $email_cc));
foreach ($mails as $mail) {
$mail->saveAndSend();
}
$template->addTos($email_to);
$template->addCCs($email_cc);
return $template;
}
/**
* @task mail
*/
protected function getStrongestAction(
PhabricatorLiskDAO $object,
array $xactions) {
return last(msort($xactions, 'getActionStrength'))->getActionName();
}
/**
* @task mail
*/
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
throw new Exception("Capability not supported.");
}
/**
* @task mail
*/
protected function getMailSubjectPrefix() {
throw new Exception("Capability not supported.");
}
/**
* @task mail
*/
protected function getMailTags(
PhabricatorLiskDAO $object,
array $xactions) {
$tags = array();
foreach ($xactions as $xaction) {
$tags[] = $xaction->getMailTags();
}
return array_mergev($tags);
}
/**
* @task mail
*/
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
throw new Exception("Capability not supported.");
}
/**
* @task mail
*/
protected function getMailTo(PhabricatorLiskDAO $object) {
throw new Exception("Capability not supported.");
}
/**
* @task mail
*/
protected function getMailCC(PhabricatorLiskDAO $object) {
if ($object instanceof PhabricatorSubscribableInterface) {
$phid = $object->getPHID();
return PhabricatorSubscribersQuery::loadSubscribersForPHID($phid);
}
throw new Exception("Capability not supported.");
}
/**
* @task mail
*/
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$headers = array();
$comments = array();
foreach ($xactions as $xaction) {
$headers[] = id(clone $xaction)->setRenderingTarget('text')->getTitle();
$comment = $xaction->getComment();
if ($comment && strlen($comment->getContent())) {
$comments[] = $comment->getContent();
}
}
$body = new PhabricatorMetaMTAMailBody();
$body->addRawSection(implode("\n", $headers));
foreach ($comments as $comment) {
$body->addRawSection($comment);
}
return $body;
}
} }

View file

@ -6,6 +6,9 @@ abstract class PhabricatorApplicationTransaction
const MARKUP_FIELD_COMMENT = 'markup:comment'; const MARKUP_FIELD_COMMENT = 'markup:comment';
const TARGET_TEXT = 'text';
const TARGET_HTML = 'html';
protected $phid; protected $phid;
protected $objectPHID; protected $objectPHID;
protected $authorPHID; protected $authorPHID;
@ -25,6 +28,7 @@ abstract class PhabricatorApplicationTransaction
private $commentNotLoaded; private $commentNotLoaded;
private $handles; private $handles;
private $renderingTarget = self::TARGET_HTML;
abstract public function getApplicationTransactionType(); abstract public function getApplicationTransactionType();
abstract public function getApplicationTransactionCommentObject(); abstract public function getApplicationTransactionCommentObject();
@ -77,6 +81,15 @@ abstract class PhabricatorApplicationTransaction
/* -( Rendering )---------------------------------------------------------- */ /* -( Rendering )---------------------------------------------------------- */
public function setRenderingTarget($rendering_target) {
$this->renderingTarget = $rendering_target;
return $this;
}
public function getRenderingTarget() {
return $this->renderingTarget;
}
public function getRequiredHandlePHIDs() { public function getRequiredHandlePHIDs() {
$phids = array(); $phids = array();
@ -108,7 +121,11 @@ abstract class PhabricatorApplicationTransaction
} }
protected function renderHandleLink($phid) { protected function renderHandleLink($phid) {
if ($this->renderingTarget == self::TARGET_HTML) {
return $this->getHandle($phid)->renderLink(); return $this->getHandle($phid)->renderLink();
} else {
return $this->getHandle($phid)->getName();
}
} }
protected function renderHandleList(array $phids) { protected function renderHandleList(array $phids) {
@ -203,6 +220,32 @@ abstract class PhabricatorApplicationTransaction
} }
} }
public function getActionStrength() {
switch ($this->getTransactionType()) {
case PhabricatorTransactions::TYPE_COMMENT:
return 0.5;
}
return 1.0;
}
public function getActionName() {
switch ($this->getTransactionType()) {
case PhabricatorTransactions::TYPE_COMMENT:
return pht('Commented On');
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
return pht('Changed Policy');
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
return pht('Changed Subscribers');
default:
return pht('Updated');
}
}
public function getMailTags() {
return array();
}
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */ /* -( PhabricatorPolicyInterface Implementation )-------------------------- */

View file

@ -112,6 +112,7 @@ JX.behavior('phabricator-nav', function(config) {
// When the user scrolls down on the desktop, we move the local nav up until // When the user scrolls down on the desktop, we move the local nav up until
// it hits the top of the page. // it hits the top of the page.
if (local) {
JX.Stratcom.listen(['scroll', 'resize'], null, function(e) { JX.Stratcom.listen(['scroll', 'resize'], null, function(e) {
if (JX.Device.getDevice() != 'desktop') { if (JX.Device.getDevice() != 'desktop') {
return; return;
@ -120,6 +121,7 @@ JX.behavior('phabricator-nav', function(config) {
var y = Math.max(0, config.menuSize - JX.Vector.getScroll().y); var y = Math.max(0, config.menuSize - JX.Vector.getScroll().y);
local.style.top = y + 'px'; local.style.top = y + 'px';
}); });
}
// - Navigation Reset ---------------------------------------------------------- // - Navigation Reset ----------------------------------------------------------