mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +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:
parent
50331016f7
commit
9e8bbdb3a2
19 changed files with 104 additions and 823 deletions
|
@ -335,7 +335,6 @@ phutil_register_library_map(array(
|
|||
'DifferentialBlameRevisionFieldSpecification' => 'applications/differential/field/specification/DifferentialBlameRevisionFieldSpecification.php',
|
||||
'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php',
|
||||
'DifferentialBranchFieldSpecification' => 'applications/differential/field/specification/DifferentialBranchFieldSpecification.php',
|
||||
'DifferentialCCWelcomeMail' => 'applications/differential/mail/DifferentialCCWelcomeMail.php',
|
||||
'DifferentialCCsFieldSpecification' => 'applications/differential/field/specification/DifferentialCCsFieldSpecification.php',
|
||||
'DifferentialCapabilityDefaultView' => 'applications/differential/capability/DifferentialCapabilityDefaultView.php',
|
||||
'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php',
|
||||
|
@ -382,7 +381,6 @@ phutil_register_library_map(array(
|
|||
'DifferentialDependsOnField' => 'applications/differential/customfield/DifferentialDependsOnField.php',
|
||||
'DifferentialDependsOnFieldSpecification' => 'applications/differential/field/specification/DifferentialDependsOnFieldSpecification.php',
|
||||
'DifferentialDiff' => 'applications/differential/storage/DifferentialDiff.php',
|
||||
'DifferentialDiffContentMail' => 'applications/differential/mail/DifferentialDiffContentMail.php',
|
||||
'DifferentialDiffCreateController' => 'applications/differential/controller/DifferentialDiffCreateController.php',
|
||||
'DifferentialDiffProperty' => 'applications/differential/storage/DifferentialDiffProperty.php',
|
||||
'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php',
|
||||
|
@ -436,7 +434,6 @@ phutil_register_library_map(array(
|
|||
'DifferentialMailPhase' => 'applications/differential/constants/DifferentialMailPhase.php',
|
||||
'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php',
|
||||
'DifferentialManiphestTasksFieldSpecification' => 'applications/differential/field/specification/DifferentialManiphestTasksFieldSpecification.php',
|
||||
'DifferentialNewDiffMail' => 'applications/differential/mail/DifferentialNewDiffMail.php',
|
||||
'DifferentialPHIDTypeDiff' => 'applications/differential/phid/DifferentialPHIDTypeDiff.php',
|
||||
'DifferentialPHIDTypeRevision' => 'applications/differential/phid/DifferentialPHIDTypeRevision.php',
|
||||
'DifferentialParseCacheGarbageCollector' => 'applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php',
|
||||
|
@ -456,7 +453,6 @@ phutil_register_library_map(array(
|
|||
'DifferentialResultsTableView' => 'applications/differential/view/DifferentialResultsTableView.php',
|
||||
'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php',
|
||||
'DifferentialRevertPlanFieldSpecification' => 'applications/differential/field/specification/DifferentialRevertPlanFieldSpecification.php',
|
||||
'DifferentialReviewRequestMail' => 'applications/differential/mail/DifferentialReviewRequestMail.php',
|
||||
'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php',
|
||||
'DifferentialReviewedByFieldSpecification' => 'applications/differential/field/specification/DifferentialReviewedByFieldSpecification.php',
|
||||
'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php',
|
||||
|
@ -2910,7 +2906,6 @@ phutil_register_library_map(array(
|
|||
'DifferentialBlameRevisionFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialBranchField' => 'DifferentialCustomField',
|
||||
'DifferentialBranchFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
|
||||
'DifferentialCCsFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialCapabilityDefaultView' => 'PhabricatorPolicyCapability',
|
||||
'DifferentialChangeset' => 'DifferentialDAO',
|
||||
|
@ -2957,7 +2952,6 @@ phutil_register_library_map(array(
|
|||
1 => 'PhabricatorPolicyInterface',
|
||||
2 => 'HarbormasterBuildableInterface',
|
||||
),
|
||||
'DifferentialDiffContentMail' => 'DifferentialMail',
|
||||
'DifferentialDiffCreateController' => 'DifferentialController',
|
||||
'DifferentialDiffProperty' => 'DifferentialDAO',
|
||||
'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
|
@ -3004,7 +2998,6 @@ phutil_register_library_map(array(
|
|||
'DifferentialMail' => 'PhabricatorMail',
|
||||
'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField',
|
||||
'DifferentialManiphestTasksFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
|
||||
'DifferentialPHIDTypeDiff' => 'PhabricatorPHIDType',
|
||||
'DifferentialPHIDTypeRevision' => 'PhabricatorPHIDType',
|
||||
'DifferentialParseCacheGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||
|
@ -3023,7 +3016,6 @@ phutil_register_library_map(array(
|
|||
'DifferentialResultsTableView' => 'AphrontView',
|
||||
'DifferentialRevertPlanField' => 'DifferentialStoredCustomField',
|
||||
'DifferentialRevertPlanFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialReviewRequestMail' => 'DifferentialMail',
|
||||
'DifferentialReviewedByField' => 'DifferentialCoreCustomField',
|
||||
'DifferentialReviewedByFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialReviewersField' => 'DifferentialCoreCustomField',
|
||||
|
|
|
@ -1019,6 +1019,46 @@ final class DifferentialTransactionEditor
|
|||
pht('REVISION DETAIL'),
|
||||
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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,18 +35,4 @@ final class DifferentialArcanistProjectFieldSpecification
|
|||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,16 +31,4 @@ final class DifferentialBranchFieldSpecification
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,30 +34,4 @@ final class DifferentialCommitsFieldSpecification
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 )------------------------------------ */
|
||||
|
||||
|
||||
|
|
|
@ -127,32 +127,4 @@ final class DifferentialManiphestTasksFieldSpecification
|
|||
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.'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -173,25 +173,4 @@ final class DifferentialReviewersFieldSpecification
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -96,25 +96,4 @@ final class DifferentialRevisionIDFieldSpecification
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,18 +67,6 @@ final class DifferentialSummaryFieldSpecification
|
|||
return (string)$value;
|
||||
}
|
||||
|
||||
public function renderValueForMail($phase) {
|
||||
if ($phase != DifferentialMailPhase::WELCOME) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->summary == '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->summary;
|
||||
}
|
||||
|
||||
public function shouldAddToSearchIndex() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -91,18 +91,6 @@ final class DifferentialTestPlanFieldSpecification
|
|||
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() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -2,307 +2,6 @@
|
|||
|
||||
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(
|
||||
DifferentialRevision $revision) {
|
||||
|
||||
|
@ -313,146 +12,4 @@ abstract class DifferentialMail extends PhabricatorMail {
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -148,6 +148,14 @@ final class DifferentialTransaction extends PhabricatorApplicationTransaction {
|
|||
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:
|
||||
switch ($this->getMetadataValue('edge:type')) {
|
||||
case PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER:
|
||||
|
@ -183,8 +191,7 @@ final class DifferentialTransaction extends PhabricatorApplicationTransaction {
|
|||
case self::TYPE_UPDATE:
|
||||
if ($new) {
|
||||
// TODO: Migrate to PHIDs and use handles here?
|
||||
// TODO: Link this?
|
||||
if (phid_get_type($new) == 'DIFF') {
|
||||
if (phid_get_type($new) == DifferentialPHIDTypeDiff::TYPECONST) {
|
||||
return pht(
|
||||
'%s updated this revision to %s.',
|
||||
$author_handle,
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
final class PhabricatorMetaMTAMailBody {
|
||||
|
||||
private $sections = array();
|
||||
private $attachments = array();
|
||||
|
||||
|
||||
/* -( 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 )---------------------------------------------------------- */
|
||||
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -1662,6 +1662,10 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
->setIsBulk(true)
|
||||
->setBody($body->render());
|
||||
|
||||
foreach ($body->getAttachments() as $attachment) {
|
||||
$template->addAttachment($attachment);
|
||||
}
|
||||
|
||||
$herald_xscript = $this->getHeraldTranscript();
|
||||
if ($herald_xscript) {
|
||||
$herald_header = $herald_xscript->getXHeraldRulesHeader();
|
||||
|
|
Loading…
Reference in a new issue