1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-01 19:22:42 +01:00

Port Differential mail features forward to transactions

Summary:
Ref T2222. Brings the major mail features (affected files, patches) forward.

This drops some of the minor integrations which just show object state (like "Maniphest Tasks") since I think they're not very important. I'll put them back if users miss them.

Test Plan: Sent mail with inline/attached patches.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2222

Differential Revision: https://secure.phabricator.com/D8459
This commit is contained in:
epriestley 2014-03-08 14:43:34 -08:00
parent 50331016f7
commit 9e8bbdb3a2
19 changed files with 104 additions and 823 deletions

View file

@ -335,7 +335,6 @@ phutil_register_library_map(array(
'DifferentialBlameRevisionFieldSpecification' => 'applications/differential/field/specification/DifferentialBlameRevisionFieldSpecification.php', 'DifferentialBlameRevisionFieldSpecification' => 'applications/differential/field/specification/DifferentialBlameRevisionFieldSpecification.php',
'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php', 'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php',
'DifferentialBranchFieldSpecification' => 'applications/differential/field/specification/DifferentialBranchFieldSpecification.php', 'DifferentialBranchFieldSpecification' => 'applications/differential/field/specification/DifferentialBranchFieldSpecification.php',
'DifferentialCCWelcomeMail' => 'applications/differential/mail/DifferentialCCWelcomeMail.php',
'DifferentialCCsFieldSpecification' => 'applications/differential/field/specification/DifferentialCCsFieldSpecification.php', 'DifferentialCCsFieldSpecification' => 'applications/differential/field/specification/DifferentialCCsFieldSpecification.php',
'DifferentialCapabilityDefaultView' => 'applications/differential/capability/DifferentialCapabilityDefaultView.php', 'DifferentialCapabilityDefaultView' => 'applications/differential/capability/DifferentialCapabilityDefaultView.php',
'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php', 'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php',
@ -382,7 +381,6 @@ phutil_register_library_map(array(
'DifferentialDependsOnField' => 'applications/differential/customfield/DifferentialDependsOnField.php', 'DifferentialDependsOnField' => 'applications/differential/customfield/DifferentialDependsOnField.php',
'DifferentialDependsOnFieldSpecification' => 'applications/differential/field/specification/DifferentialDependsOnFieldSpecification.php', 'DifferentialDependsOnFieldSpecification' => 'applications/differential/field/specification/DifferentialDependsOnFieldSpecification.php',
'DifferentialDiff' => 'applications/differential/storage/DifferentialDiff.php', 'DifferentialDiff' => 'applications/differential/storage/DifferentialDiff.php',
'DifferentialDiffContentMail' => 'applications/differential/mail/DifferentialDiffContentMail.php',
'DifferentialDiffCreateController' => 'applications/differential/controller/DifferentialDiffCreateController.php', 'DifferentialDiffCreateController' => 'applications/differential/controller/DifferentialDiffCreateController.php',
'DifferentialDiffProperty' => 'applications/differential/storage/DifferentialDiffProperty.php', 'DifferentialDiffProperty' => 'applications/differential/storage/DifferentialDiffProperty.php',
'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php', 'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php',
@ -436,7 +434,6 @@ phutil_register_library_map(array(
'DifferentialMailPhase' => 'applications/differential/constants/DifferentialMailPhase.php', 'DifferentialMailPhase' => 'applications/differential/constants/DifferentialMailPhase.php',
'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php', 'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php',
'DifferentialManiphestTasksFieldSpecification' => 'applications/differential/field/specification/DifferentialManiphestTasksFieldSpecification.php', 'DifferentialManiphestTasksFieldSpecification' => 'applications/differential/field/specification/DifferentialManiphestTasksFieldSpecification.php',
'DifferentialNewDiffMail' => 'applications/differential/mail/DifferentialNewDiffMail.php',
'DifferentialPHIDTypeDiff' => 'applications/differential/phid/DifferentialPHIDTypeDiff.php', 'DifferentialPHIDTypeDiff' => 'applications/differential/phid/DifferentialPHIDTypeDiff.php',
'DifferentialPHIDTypeRevision' => 'applications/differential/phid/DifferentialPHIDTypeRevision.php', 'DifferentialPHIDTypeRevision' => 'applications/differential/phid/DifferentialPHIDTypeRevision.php',
'DifferentialParseCacheGarbageCollector' => 'applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php', 'DifferentialParseCacheGarbageCollector' => 'applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php',
@ -456,7 +453,6 @@ phutil_register_library_map(array(
'DifferentialResultsTableView' => 'applications/differential/view/DifferentialResultsTableView.php', 'DifferentialResultsTableView' => 'applications/differential/view/DifferentialResultsTableView.php',
'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php', 'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php',
'DifferentialRevertPlanFieldSpecification' => 'applications/differential/field/specification/DifferentialRevertPlanFieldSpecification.php', 'DifferentialRevertPlanFieldSpecification' => 'applications/differential/field/specification/DifferentialRevertPlanFieldSpecification.php',
'DifferentialReviewRequestMail' => 'applications/differential/mail/DifferentialReviewRequestMail.php',
'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php', 'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php',
'DifferentialReviewedByFieldSpecification' => 'applications/differential/field/specification/DifferentialReviewedByFieldSpecification.php', 'DifferentialReviewedByFieldSpecification' => 'applications/differential/field/specification/DifferentialReviewedByFieldSpecification.php',
'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php', 'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php',
@ -2910,7 +2906,6 @@ phutil_register_library_map(array(
'DifferentialBlameRevisionFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialBlameRevisionFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialBranchField' => 'DifferentialCustomField', 'DifferentialBranchField' => 'DifferentialCustomField',
'DifferentialBranchFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialBranchFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
'DifferentialCCsFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialCCsFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialCapabilityDefaultView' => 'PhabricatorPolicyCapability', 'DifferentialCapabilityDefaultView' => 'PhabricatorPolicyCapability',
'DifferentialChangeset' => 'DifferentialDAO', 'DifferentialChangeset' => 'DifferentialDAO',
@ -2957,7 +2952,6 @@ phutil_register_library_map(array(
1 => 'PhabricatorPolicyInterface', 1 => 'PhabricatorPolicyInterface',
2 => 'HarbormasterBuildableInterface', 2 => 'HarbormasterBuildableInterface',
), ),
'DifferentialDiffContentMail' => 'DifferentialMail',
'DifferentialDiffCreateController' => 'DifferentialController', 'DifferentialDiffCreateController' => 'DifferentialController',
'DifferentialDiffProperty' => 'DifferentialDAO', 'DifferentialDiffProperty' => 'DifferentialDAO',
'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
@ -3004,7 +2998,6 @@ phutil_register_library_map(array(
'DifferentialMail' => 'PhabricatorMail', 'DifferentialMail' => 'PhabricatorMail',
'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField', 'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField',
'DifferentialManiphestTasksFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialManiphestTasksFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
'DifferentialPHIDTypeDiff' => 'PhabricatorPHIDType', 'DifferentialPHIDTypeDiff' => 'PhabricatorPHIDType',
'DifferentialPHIDTypeRevision' => 'PhabricatorPHIDType', 'DifferentialPHIDTypeRevision' => 'PhabricatorPHIDType',
'DifferentialParseCacheGarbageCollector' => 'PhabricatorGarbageCollector', 'DifferentialParseCacheGarbageCollector' => 'PhabricatorGarbageCollector',
@ -3023,7 +3016,6 @@ phutil_register_library_map(array(
'DifferentialResultsTableView' => 'AphrontView', 'DifferentialResultsTableView' => 'AphrontView',
'DifferentialRevertPlanField' => 'DifferentialStoredCustomField', 'DifferentialRevertPlanField' => 'DifferentialStoredCustomField',
'DifferentialRevertPlanFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialRevertPlanFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialReviewRequestMail' => 'DifferentialMail',
'DifferentialReviewedByField' => 'DifferentialCoreCustomField', 'DifferentialReviewedByField' => 'DifferentialCoreCustomField',
'DifferentialReviewedByFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialReviewedByFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialReviewersField' => 'DifferentialCoreCustomField', 'DifferentialReviewersField' => 'DifferentialCoreCustomField',

View file

@ -1019,6 +1019,46 @@ final class DifferentialTransactionEditor
pht('REVISION DETAIL'), pht('REVISION DETAIL'),
PhabricatorEnv::getProductionURI('/D'.$object->getID())); PhabricatorEnv::getProductionURI('/D'.$object->getID()));
$update_xaction = null;
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case DifferentialTransaction::TYPE_UPDATE:
$update_xaction = $xaction;
break;
}
}
if ($update_xaction) {
$diff = $this->loadDiff($update_xaction->getNewValue(), true);
$body->addTextSection(
pht('AFFECTED FILES'),
$this->renderAffectedFilesForMail($diff));
$config_key_inline = 'metamta.differential.inline-patches';
$config_inline = PhabricatorEnv::getEnvConfig($config_key_inline);
$config_key_attach = 'metamta.differential.attach-patches';
$config_attach = PhabricatorEnv::getEnvConfig($config_key_attach);
if ($config_inline || $config_attach) {
$patch = $this->renderPatchForMail($diff);
$lines = count(phutil_split_lines($patch));
if ($config_inline && ($lines <= $config_inline)) {
$body->addTextSection(
pht('CHANGE DETAILS'),
$patch);
}
if ($config_attach) {
$name = pht('D%s.%s.patch', $object->getID(), $diff->getID());
$mime_type = 'text/x-patch; charset=utf-8';
$body->addAttachment(
new PhabricatorMetaMTAAttachment($patch, $name, $mime_type));
}
}
}
return $body; return $body;
} }
@ -1491,4 +1531,30 @@ final class DifferentialTransactionEditor
} }
} }
private function renderAffectedFilesForMail(DifferentialDiff $diff) {
$changesets = $diff->getChangesets();
$filenames = mpull($changesets, 'getDisplayFilename');
sort($filenames);
$count = count($filenames);
$max = 250;
if ($count > $max) {
$filenames = array_slice($filenames, 0, $max);
$filenames[] = pht('(%d more files...)', ($count - $max));
}
return implode("\n", $filenames);
}
private function renderPatchForMail(DifferentialDiff $diff) {
$format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format');
return id(new DifferentialRawDiffRenderer())
->setViewer($this->getActor())
->setFormat($format)
->setChangesets($diff->getChangesets())
->buildPatch();
}
} }

View file

@ -35,18 +35,4 @@ final class DifferentialArcanistProjectFieldSpecification
return $diff->getArcanistProjectPHID(); return $diff->getArcanistProjectPHID();
} }
public function renderValueForMail($phase) {
$diff = $this->getRevision()->loadActiveDiff();
if ($diff) {
$phid = $diff->getArcanistProjectPHID();
if ($phid) {
$handle = id(new PhabricatorHandleQuery())
->setViewer($this->getUser())
->withPHIDs(array($phid))
->executeOne();
return "ARCANIST PROJECT\n ".$handle->getName();
}
}
}
} }

View file

@ -31,16 +31,4 @@ final class DifferentialBranchFieldSpecification
return $this->getBranchOrBookmarkDescription($diff); return $this->getBranchOrBookmarkDescription($diff);
} }
public function renderValueForMail($phase) {
$diff = $this->getRevision()->loadActiveDiff();
if ($diff) {
$description = $this->getBranchOrBookmarkDescription($diff);
if ($description) {
return "BRANCH\n {$description}";
}
}
return null;
}
} }

View file

@ -34,30 +34,4 @@ final class DifferentialCommitsFieldSpecification
return $revision->getCommitPHIDs(); return $revision->getCommitPHIDs();
} }
public function renderValueForMail($phase) {
$revision = $this->getRevision();
if ($revision->getStatus() != ArcanistDifferentialRevisionStatus::CLOSED) {
return null;
}
$phids = $revision->loadCommitPHIDs();
if (!$phids) {
return null;
}
$body = array();
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->getUser())
->withPHIDs($phids)
->execute();
$body[] = pht('COMMIT(S)', count($handles));
foreach ($handles as $handle) {
$body[] = ' '.PhabricatorEnv::getProductionURI($handle->getURI());
}
return implode("\n", $body);
}
} }

View file

@ -385,23 +385,6 @@ abstract class DifferentialFieldSpecification {
} }
/* -( Extending the E-mail Interface )------------------------------------- */
/**
* Return plain text to render in e-mail messages. The text may span
* multiple lines.
*
* @return int One of DifferentialMailPhase constants.
* @return string|null Plain text, or null for no message.
*
* @task mail
*/
public function renderValueForMail($phase) {
return null;
}
/* -( Extending the Conduit Interface )------------------------------------ */ /* -( Extending the Conduit Interface )------------------------------------ */

View file

@ -127,32 +127,4 @@ final class DifferentialManiphestTasksFieldSpecification
return $task_phids; return $task_phids;
} }
public function renderValueForMail($phase) {
if ($phase == DifferentialMailPhase::COMMENT) {
return null;
}
if (!$this->maniphestTasks) {
return null;
}
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->getUser())
->withPHIDs($this->maniphestTasks)
->execute();
$body = array();
$body[] = 'MANIPHEST TASKS';
foreach ($handles as $handle) {
$body[] = ' '.PhabricatorEnv::getProductionURI($handle->getURI());
}
return implode("\n", $body);
}
public function getCommitMessageTips() {
return array(
'Use "Fixes T123" in your summary to mark that the current '.
'revision completes a given task.'
);
}
} }

View file

@ -173,25 +173,4 @@ final class DifferentialReviewersFieldSpecification
return $revision->getReviewers(); return $revision->getReviewers();
} }
public function renderValueForMail($phase) {
if ($phase == DifferentialMailPhase::COMMENT) {
return null;
}
if (!$this->reviewers) {
return null;
}
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->getUser())
->withPHIDs($this->reviewers)
->execute();
$handles = array_select_keys(
$handles,
array($this->getRevision()->getPrimaryReviewer())) + $handles;
$names = mpull($handles, 'getObjectName');
return 'Reviewers: '.implode(', ', $names);
}
} }

View file

@ -96,25 +96,4 @@ final class DifferentialRevisionIDFieldSpecification
return 'D'.$revision->getID(); return 'D'.$revision->getID();
} }
public function renderValueForMail($phase) {
$body = array();
$body[] = 'REVISION DETAIL';
$body[] = ' '.PhabricatorEnv::getProductionURI('/D'.$this->id);
if ($phase == DifferentialMailPhase::UPDATE) {
$diffs = id(new DifferentialDiff())->loadAllWhere(
'revisionID = %d ORDER BY id DESC LIMIT 2',
$this->id);
if (count($diffs) == 2) {
list($new, $old) = array_values(mpull($diffs, 'getID'));
$body[] = null;
$body[] = 'CHANGE SINCE LAST DIFF';
$body[] = ' '.PhabricatorEnv::getProductionURI(
"/D{$this->id}?vs={$old}&id={$new}#toc");
}
}
return implode("\n", $body);
}
} }

View file

@ -67,18 +67,6 @@ final class DifferentialSummaryFieldSpecification
return (string)$value; return (string)$value;
} }
public function renderValueForMail($phase) {
if ($phase != DifferentialMailPhase::WELCOME) {
return null;
}
if ($this->summary == '') {
return null;
}
return $this->summary;
}
public function shouldAddToSearchIndex() { public function shouldAddToSearchIndex() {
return true; return true;
} }

View file

@ -91,18 +91,6 @@ final class DifferentialTestPlanFieldSpecification
return $value; return $value;
} }
public function renderValueForMail($phase) {
if ($phase != DifferentialMailPhase::WELCOME) {
return null;
}
if ($this->plan == '') {
return null;
}
return "TEST PLAN\n".preg_replace('/^/m', ' ', $this->plan);
}
public function shouldAddToSearchIndex() { public function shouldAddToSearchIndex() {
return true; return true;
} }

View file

@ -1,22 +0,0 @@
<?php
final class DifferentialCCWelcomeMail extends DifferentialReviewRequestMail {
protected function renderVaryPrefix() {
return '[Added to CC]';
}
protected function renderBody() {
$actor = $this->getActorName();
$name = $this->getRevision()->getTitle();
$body = array();
$body[] = "{$actor} added you to the CC list for the revision \"{$name}\".";
$body[] = null;
$body[] = $this->renderReviewRequestBody();
return implode("\n", $body);
}
}

View file

@ -1,19 +0,0 @@
<?php
final class DifferentialDiffContentMail extends DifferentialMail {
protected $content;
public function __construct(DifferentialRevision $revision, $content) {
$this->setRevision($revision);
$this->content = $content;
}
protected function renderVaryPrefix() {
return '[Content]';
}
protected function renderBody() {
return $this->content;
}
}

View file

@ -2,307 +2,6 @@
abstract class DifferentialMail extends PhabricatorMail { abstract class DifferentialMail extends PhabricatorMail {
protected $to = array();
protected $cc = array();
protected $excludePHIDs = array();
protected $actorHandle;
protected $revision;
protected $comments;
protected $changesets;
protected $inlineComments;
protected $isFirstMailAboutRevision;
protected $isFirstMailToRecipients;
protected $heraldTranscriptURI;
protected $heraldRulesHeader;
protected $replyHandler;
protected $parentMessageID;
private $rawMail;
public function getRawMail() {
if (!$this->rawMail) {
throw new Exception("Call send() before getRawMail()!");
}
return $this->rawMail;
}
protected function renderSubject() {
$revision = $this->getRevision();
$title = $revision->getTitle();
$id = $revision->getID();
return "D{$id}: {$title}";
}
abstract protected function renderVaryPrefix();
abstract protected function renderBody();
public function setActorHandle($actor_handle) {
$this->actorHandle = $actor_handle;
return $this;
}
public function getActorHandle() {
return $this->actorHandle;
}
protected function getActorName() {
$handle = $this->getActorHandle();
if ($handle) {
return $handle->getName();
}
return '???';
}
public function setParentMessageID($parent_message_id) {
$this->parentMessageID = $parent_message_id;
return $this;
}
public function setXHeraldRulesHeader($header) {
$this->heraldRulesHeader = $header;
return $this;
}
public function send() {
$to_phids = $this->getToPHIDs();
if (!$to_phids) {
throw new Exception('No "To:" users provided!');
}
$cc_phids = $this->getCCPHIDs();
$attachments = $this->buildAttachments();
$template = new PhabricatorMetaMTAMail();
$actor_handle = $this->getActorHandle();
$reply_handler = $this->getReplyHandler();
if ($actor_handle) {
$template->setFrom($actor_handle->getPHID());
}
$template
->setIsHTML($this->shouldMarkMailAsHTML())
->setParentMessageID($this->parentMessageID)
->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs())
->addHeader('Thread-Topic', $this->getThreadTopic());
$template->setAttachments($attachments);
$template->setThreadID(
$this->getThreadID(),
$this->isFirstMailAboutRevision());
if ($this->heraldRulesHeader) {
$template->addHeader('X-Herald-Rules', $this->heraldRulesHeader);
}
$revision = $this->revision;
if ($revision) {
if ($revision->getAuthorPHID()) {
$template->addHeader(
'X-Differential-Author',
'<'.$revision->getAuthorPHID().'>');
}
$reviewer_phids = $revision->getReviewers();
if ($reviewer_phids) {
// Add several headers to support e-mail clients which are not able to
// create rules using regular expressions or wildcards (namely Outlook).
$template->addPHIDHeaders('X-Differential-Reviewer', $reviewer_phids);
// Add it also as a list to allow matching of the first reviewer and
// also for backwards compatibility.
$template->addHeader(
'X-Differential-Reviewers',
'<'.implode('>, <', $reviewer_phids).'>');
}
if ($cc_phids) {
$template->addPHIDHeaders('X-Differential-CC', $cc_phids);
$template->addHeader(
'X-Differential-CCs',
'<'.implode('>, <', $cc_phids).'>');
// Determine explicit CCs (those added by humans) and put them in a
// header so users can differentiate between Herald CCs and human CCs.
$relation_subscribed = DifferentialRevision::RELATION_SUBSCRIBED;
$raw = $revision->getRawRelations($relation_subscribed);
$reason_phids = ipull($raw, 'reasonPHID');
$reason_handles = id(new PhabricatorHandleQuery())
->setViewer($this->getActor())
->withPHIDs($reason_phids)
->execute();
$explicit_cc = array();
foreach ($raw as $relation) {
if (!$relation['reasonPHID']) {
continue;
}
$type = $reason_handles[$relation['reasonPHID']]->getType();
if ($type == PhabricatorPeoplePHIDTypeUser::TYPECONST) {
$explicit_cc[] = $relation['objectPHID'];
}
}
if ($explicit_cc) {
$template->addPHIDHeaders('X-Differential-Explicit-CC', $explicit_cc);
$template->addHeader(
'X-Differential-Explicit-CCs',
'<'.implode('>, <', $explicit_cc).'>');
}
}
}
$template->setIsBulk(true);
$template->setRelatedPHID($this->getRevision()->getPHID());
$mailtags = $this->getMailTags();
if ($mailtags) {
$template->setMailTags($mailtags);
}
$phids = array();
foreach ($to_phids as $phid) {
$phids[$phid] = true;
}
foreach ($cc_phids as $phid) {
$phids[$phid] = true;
}
$phids = array_keys($phids);
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->getActor())
->withPHIDs($phids)
->execute();
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getActor())
->withPHIDs($phids)
->execute();
$to_handles = array_select_keys($handles, $to_phids);
$cc_handles = array_select_keys($handles, $cc_phids);
$this->prepareBody();
$this->rawMail = clone $template;
$this->rawMail->addTos($to_phids);
$this->rawMail->addCCs($cc_phids);
$mails = $reply_handler->multiplexMail($template, $to_handles, $cc_handles);
$original_translator = PhutilTranslator::getInstance();
if (!PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
$translation = PhabricatorEnv::newObjectFromConfig(
'translation.provider');
$translator = id(new PhutilTranslator())
->setLanguage($translation->getLanguage())
->addTranslations($translation->getTranslations());
}
try {
foreach ($mails as $mail) {
if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
$translation = newv($mail->getTranslation($objects), array());
$translator = id(new PhutilTranslator())
->setLanguage($translation->getLanguage())
->addTranslations($translation->getTranslations());
PhutilTranslator::setInstance($translator);
}
$body =
$this->buildBody()."\n".
$reply_handler->getRecipientsSummary($to_handles, $cc_handles);
$mail
->setSubject($this->renderSubject())
->setSubjectPrefix($this->getSubjectPrefix())
->setVarySubjectPrefix($this->renderVaryPrefix())
->setBody($body);
$event = new PhabricatorEvent(
PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL,
array(
'mail' => $mail,
)
);
PhutilEventEngine::dispatchEvent($event);
$mail = $event->getValue('mail');
$mail->saveAndSend();
}
} catch (Exception $ex) {
PhutilTranslator::setInstance($original_translator);
throw $ex;
}
PhutilTranslator::setInstance($original_translator);
return $this;
}
protected function getMailTags() {
return array();
}
protected function getSubjectPrefix() {
return PhabricatorEnv::getEnvConfig('metamta.differential.subject-prefix');
}
protected function shouldMarkMailAsHTML() {
return false;
}
/**
* @{method:buildBody} is called once for each e-mail recipient to allow
* translating text to his language. This method can be used to load data that
* don't need translation and use them later in @{method:buildBody}.
*
* @param
* @return
*/
protected function prepareBody() {
}
protected function buildBody() {
$main_body = $this->renderBody();
$body = new PhabricatorMetaMTAMailBody();
$body->addRawSection($main_body);
$reply_handler = $this->getReplyHandler();
$body->addReplySection($reply_handler->getReplyHandlerInstructions());
if ($this->getHeraldTranscriptURI() && $this->isFirstMailToRecipients()) {
$xscript_uri = $this->getHeraldTranscriptURI();
$body->addHeraldSection($xscript_uri);
}
return $body->render();
}
/**
* You can override this method in a subclass and return array of attachments
* to be sent with the email. Each attachment is an instance of
* PhabricatorMetaMTAAttachment.
*/
protected function buildAttachments() {
return array();
}
public function getReplyHandler() {
if (!$this->replyHandler) {
$this->replyHandler =
self::newReplyHandlerForRevision($this->getRevision());
}
return $this->replyHandler;
}
public static function newReplyHandlerForRevision( public static function newReplyHandlerForRevision(
DifferentialRevision $revision) { DifferentialRevision $revision) {
@ -313,146 +12,4 @@ abstract class DifferentialMail extends PhabricatorMail {
return $reply_handler; return $reply_handler;
} }
protected function formatText($text) {
$text = explode("\n", rtrim($text));
foreach ($text as &$line) {
$line = rtrim(' '.$line);
}
unset($line);
return implode("\n", $text);
}
public function setExcludeMailRecipientPHIDs(array $exclude) {
$this->excludePHIDs = $exclude;
return $this;
}
public function getExcludeMailRecipientPHIDs() {
return $this->excludePHIDs;
}
public function setToPHIDs(array $to) {
$this->to = $to;
return $this;
}
public function setCCPHIDs(array $cc) {
$this->cc = $cc;
return $this;
}
protected function getToPHIDs() {
return $this->to;
}
protected function getCCPHIDs() {
return $this->cc;
}
public function setRevision($revision) {
$this->revision = $revision;
return $this;
}
public function getRevision() {
return $this->revision;
}
protected function getThreadID() {
$phid = $this->getRevision()->getPHID();
return "differential-rev-{$phid}-req";
}
protected function getThreadTopic() {
$id = $this->getRevision()->getID();
$title = $this->getRevision()->getOriginalTitle();
return "D{$id}: {$title}";
}
public function setComments(array $comments) {
$this->comments = $comments;
return $this;
}
public function getComments() {
return $this->comments;
}
public function setChangesets($changesets) {
$this->changesets = $changesets;
return $this;
}
public function getChangesets() {
return $this->changesets;
}
public function setInlineComments(array $inline_comments) {
assert_instances_of($inline_comments, 'DifferentialTransaction');
$this->inlineComments = $inline_comments;
return $this;
}
public function getInlineComments() {
return $this->inlineComments;
}
protected function renderAuxFields($phase) {
$selector = DifferentialFieldSelector::newSelector();
$aux_fields = $selector->sortFieldsForMail(
$selector->getFieldSpecifications());
$body = array();
foreach ($aux_fields as $field) {
$field->setUser($this->getActor());
$field->setRevision($this->getRevision());
// TODO: Introduce and use getRequiredHandlePHIDsForMail() and load all
// handles in prepareBody().
$text = $field->renderValueForMail($phase);
if ($text !== null) {
$body[] = $text;
$body[] = null;
}
}
return implode("\n", $body);
}
public function setIsFirstMailToRecipients($first) {
$this->isFirstMailToRecipients = $first;
return $this;
}
public function isFirstMailToRecipients() {
return $this->isFirstMailToRecipients;
}
public function setIsFirstMailAboutRevision($first) {
$this->isFirstMailAboutRevision = $first;
return $this;
}
public function isFirstMailAboutRevision() {
return $this->isFirstMailAboutRevision;
}
public function setHeraldTranscriptURI($herald_transcript_uri) {
$this->heraldTranscriptURI = $herald_transcript_uri;
return $this;
}
public function getHeraldTranscriptURI() {
return $this->heraldTranscriptURI;
}
protected function renderHandleList(array $handles, array $phids) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$names = array();
foreach ($phids as $phid) {
$names[] = $handles[$phid]->getName();
}
return implode(', ', $names);
}
} }

View file

@ -1,37 +0,0 @@
<?php
final class DifferentialNewDiffMail extends DifferentialReviewRequestMail {
protected function renderVaryPrefix() {
$revision = $this->getRevision();
$line_count = $revision->getLineCount();
$lines = pht('%d line(s)', $line_count);
if ($this->isFirstMailToRecipients()) {
$verb = 'Request';
} else {
$verb = 'Updated';
}
return "[{$verb}, {$lines}]";
}
protected function renderBody() {
$actor = $this->getActorName();
$name = $this->getRevision()->getTitle();
$body = array();
if ($this->isFirstMailToRecipients()) {
$body[] = "{$actor} requested code review of \"{$name}\".";
} else {
$body[] = "{$actor} updated the revision \"{$name}\".";
}
$body[] = null;
$body[] = $this->renderReviewRequestBody();
return implode("\n", $body);
}
}

View file

@ -1,129 +0,0 @@
<?php
abstract class DifferentialReviewRequestMail extends DifferentialMail {
const MAX_AFFECTED_FILES = 1000;
protected $commentText;
private $patch;
public function setCommentText($comment_text) {
$this->commentText = $comment_text;
return $this;
}
public function getCommentText() {
return $this->commentText;
}
public function __construct(
DifferentialRevision $revision,
PhabricatorObjectHandle $actor,
array $changesets) {
assert_instances_of($changesets, 'DifferentialChangeset');
$this->setRevision($revision);
$this->setActorHandle($actor);
$this->setChangesets($changesets);
}
protected function prepareBody() {
parent::prepareBody();
$inline_max_length = PhabricatorEnv::getEnvConfig(
'metamta.differential.inline-patches');
if ($inline_max_length) {
$patch = $this->buildPatch();
if (count(explode("\n", $patch)) <= $inline_max_length) {
$this->patch = $patch;
}
}
}
protected function renderReviewRequestBody() {
$revision = $this->getRevision();
$body = array();
if (!$this->isFirstMailToRecipients()) {
if (strlen($this->getCommentText())) {
$body[] = $this->formatText($this->getCommentText());
$body[] = null;
}
}
$phase = ($this->isFirstMailToRecipients() ?
DifferentialMailPhase::WELCOME :
DifferentialMailPhase::UPDATE);
$body[] = $this->renderAuxFields($phase);
$changesets = $this->getChangesets();
if ($changesets) {
$body[] = 'AFFECTED FILES';
$max = self::MAX_AFFECTED_FILES;
foreach (array_values($changesets) as $i => $changeset) {
if ($i == $max) {
$body[] = ' ('.(count($changesets) - $max).' more files)';
break;
}
$body[] = ' '.$changeset->getFilename();
}
$body[] = null;
}
if ($this->patch) {
$body[] = 'CHANGE DETAILS';
$body[] = $this->patch;
}
return implode("\n", $body);
}
protected function buildAttachments() {
$attachments = array();
if (PhabricatorEnv::getEnvConfig('metamta.differential.attach-patches')) {
$revision = $this->getRevision();
$revision_id = $revision->getID();
$diffs = id(new DifferentialDiffQuery())
->setViewer($this->getActor())
->withRevisionIDs(array($revision_id))
->execute();
$diff_number = count($diffs);
$attachments[] = new PhabricatorMetaMTAAttachment(
$this->buildPatch(),
"D{$revision_id}.{$diff_number}.patch",
'text/x-patch; charset=utf-8'
);
}
return $attachments;
}
private function buildPatch() {
$renderer = new DifferentialRawDiffRenderer();
$renderer->setChangesets($this->getChangesets());
$renderer->setFormat(
PhabricatorEnv::getEnvConfig('metamta.differential.patch-format'));
// TODO: It would be nice to have a real viewer here eventually, but
// in the meantime anyone we're sending mail to can certainly see the
// patch.
$renderer->setViewer(PhabricatorUser::getOmnipotentUser());
return $renderer->buildPatch();
}
protected function getMailTags() {
$tags = array();
if ($this->isFirstMailToRecipients()) {
$tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEW_REQUEST;
} else {
$tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_UPDATED;
}
return $tags;
}
}

View file

@ -148,6 +148,14 @@ final class DifferentialTransaction extends PhabricatorApplicationTransaction {
break; break;
} }
break; break;
case self::TYPE_UPDATE:
$old = $this->getOldValue();
if ($old === null) {
$tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEW_REQUEST;
} else {
$tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_UPDATED;
}
break;
case PhabricatorTransactions::TYPE_EDGE: case PhabricatorTransactions::TYPE_EDGE:
switch ($this->getMetadataValue('edge:type')) { switch ($this->getMetadataValue('edge:type')) {
case PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER: case PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER:
@ -183,8 +191,7 @@ final class DifferentialTransaction extends PhabricatorApplicationTransaction {
case self::TYPE_UPDATE: case self::TYPE_UPDATE:
if ($new) { if ($new) {
// TODO: Migrate to PHIDs and use handles here? // TODO: Migrate to PHIDs and use handles here?
// TODO: Link this? if (phid_get_type($new) == DifferentialPHIDTypeDiff::TYPECONST) {
if (phid_get_type($new) == 'DIFF') {
return pht( return pht(
'%s updated this revision to %s.', '%s updated this revision to %s.',
$author_handle, $author_handle,

View file

@ -10,6 +10,7 @@
final class PhabricatorMetaMTAMailBody { final class PhabricatorMetaMTAMailBody {
private $sections = array(); private $sections = array();
private $attachments = array();
/* -( Composition )-------------------------------------------------------- */ /* -( Composition )-------------------------------------------------------- */
@ -88,6 +89,19 @@ final class PhabricatorMetaMTAMailBody {
} }
/**
* Add an attachment.
*
* @param PhabricatorMetaMTAAttachment Attachment.
* @return this
* @task compose
*/
public function addAttachment(PhabricatorMetaMTAAttachment $attachment) {
$this->attachments[] = $attachment;
return $this;
}
/* -( Rendering )---------------------------------------------------------- */ /* -( Rendering )---------------------------------------------------------- */
@ -102,6 +116,17 @@ final class PhabricatorMetaMTAMailBody {
} }
/**
* Retrieve attachments.
*
* @return list<PhabricatorMetaMTAAttachment> Attachments.
* @task render
*/
public function getAttachments() {
return $this->attachments;
}
/** /**
* Indent a block of text for rendering under a section heading. * Indent a block of text for rendering under a section heading.
* *

View file

@ -1662,6 +1662,10 @@ abstract class PhabricatorApplicationTransactionEditor
->setIsBulk(true) ->setIsBulk(true)
->setBody($body->render()); ->setBody($body->render());
foreach ($body->getAttachments() as $attachment) {
$template->addAttachment($attachment);
}
$herald_xscript = $this->getHeraldTranscript(); $herald_xscript = $this->getHeraldTranscript();
if ($herald_xscript) { if ($herald_xscript) {
$herald_header = $herald_xscript->getXHeraldRulesHeader(); $herald_header = $herald_xscript->getXHeraldRulesHeader();