@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+104 -823
-8
src/__phutil_library_map__.php
··· 335 335 'DifferentialBlameRevisionFieldSpecification' => 'applications/differential/field/specification/DifferentialBlameRevisionFieldSpecification.php', 336 336 'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php', 337 337 'DifferentialBranchFieldSpecification' => 'applications/differential/field/specification/DifferentialBranchFieldSpecification.php', 338 - 'DifferentialCCWelcomeMail' => 'applications/differential/mail/DifferentialCCWelcomeMail.php', 339 338 'DifferentialCCsFieldSpecification' => 'applications/differential/field/specification/DifferentialCCsFieldSpecification.php', 340 339 'DifferentialCapabilityDefaultView' => 'applications/differential/capability/DifferentialCapabilityDefaultView.php', 341 340 'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php', ··· 382 381 'DifferentialDependsOnField' => 'applications/differential/customfield/DifferentialDependsOnField.php', 383 382 'DifferentialDependsOnFieldSpecification' => 'applications/differential/field/specification/DifferentialDependsOnFieldSpecification.php', 384 383 'DifferentialDiff' => 'applications/differential/storage/DifferentialDiff.php', 385 - 'DifferentialDiffContentMail' => 'applications/differential/mail/DifferentialDiffContentMail.php', 386 384 'DifferentialDiffCreateController' => 'applications/differential/controller/DifferentialDiffCreateController.php', 387 385 'DifferentialDiffProperty' => 'applications/differential/storage/DifferentialDiffProperty.php', 388 386 'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php', ··· 436 434 'DifferentialMailPhase' => 'applications/differential/constants/DifferentialMailPhase.php', 437 435 'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php', 438 436 'DifferentialManiphestTasksFieldSpecification' => 'applications/differential/field/specification/DifferentialManiphestTasksFieldSpecification.php', 439 - 'DifferentialNewDiffMail' => 'applications/differential/mail/DifferentialNewDiffMail.php', 440 437 'DifferentialPHIDTypeDiff' => 'applications/differential/phid/DifferentialPHIDTypeDiff.php', 441 438 'DifferentialPHIDTypeRevision' => 'applications/differential/phid/DifferentialPHIDTypeRevision.php', 442 439 'DifferentialParseCacheGarbageCollector' => 'applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php', ··· 456 453 'DifferentialResultsTableView' => 'applications/differential/view/DifferentialResultsTableView.php', 457 454 'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php', 458 455 'DifferentialRevertPlanFieldSpecification' => 'applications/differential/field/specification/DifferentialRevertPlanFieldSpecification.php', 459 - 'DifferentialReviewRequestMail' => 'applications/differential/mail/DifferentialReviewRequestMail.php', 460 456 'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php', 461 457 'DifferentialReviewedByFieldSpecification' => 'applications/differential/field/specification/DifferentialReviewedByFieldSpecification.php', 462 458 'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php', ··· 2910 2906 'DifferentialBlameRevisionFieldSpecification' => 'DifferentialFieldSpecification', 2911 2907 'DifferentialBranchField' => 'DifferentialCustomField', 2912 2908 'DifferentialBranchFieldSpecification' => 'DifferentialFieldSpecification', 2913 - 'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail', 2914 2909 'DifferentialCCsFieldSpecification' => 'DifferentialFieldSpecification', 2915 2910 'DifferentialCapabilityDefaultView' => 'PhabricatorPolicyCapability', 2916 2911 'DifferentialChangeset' => 'DifferentialDAO', ··· 2957 2952 1 => 'PhabricatorPolicyInterface', 2958 2953 2 => 'HarbormasterBuildableInterface', 2959 2954 ), 2960 - 'DifferentialDiffContentMail' => 'DifferentialMail', 2961 2955 'DifferentialDiffCreateController' => 'DifferentialController', 2962 2956 'DifferentialDiffProperty' => 'DifferentialDAO', 2963 2957 'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', ··· 3004 2998 'DifferentialMail' => 'PhabricatorMail', 3005 2999 'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField', 3006 3000 'DifferentialManiphestTasksFieldSpecification' => 'DifferentialFieldSpecification', 3007 - 'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail', 3008 3001 'DifferentialPHIDTypeDiff' => 'PhabricatorPHIDType', 3009 3002 'DifferentialPHIDTypeRevision' => 'PhabricatorPHIDType', 3010 3003 'DifferentialParseCacheGarbageCollector' => 'PhabricatorGarbageCollector', ··· 3023 3016 'DifferentialResultsTableView' => 'AphrontView', 3024 3017 'DifferentialRevertPlanField' => 'DifferentialStoredCustomField', 3025 3018 'DifferentialRevertPlanFieldSpecification' => 'DifferentialFieldSpecification', 3026 - 'DifferentialReviewRequestMail' => 'DifferentialMail', 3027 3019 'DifferentialReviewedByField' => 'DifferentialCoreCustomField', 3028 3020 'DifferentialReviewedByFieldSpecification' => 'DifferentialFieldSpecification', 3029 3021 'DifferentialReviewersField' => 'DifferentialCoreCustomField',
+66
src/applications/differential/editor/DifferentialTransactionEditor.php
··· 1019 1019 pht('REVISION DETAIL'), 1020 1020 PhabricatorEnv::getProductionURI('/D'.$object->getID())); 1021 1021 1022 + $update_xaction = null; 1023 + foreach ($xactions as $xaction) { 1024 + switch ($xaction->getTransactionType()) { 1025 + case DifferentialTransaction::TYPE_UPDATE: 1026 + $update_xaction = $xaction; 1027 + break; 1028 + } 1029 + } 1030 + 1031 + if ($update_xaction) { 1032 + $diff = $this->loadDiff($update_xaction->getNewValue(), true); 1033 + 1034 + $body->addTextSection( 1035 + pht('AFFECTED FILES'), 1036 + $this->renderAffectedFilesForMail($diff)); 1037 + 1038 + $config_key_inline = 'metamta.differential.inline-patches'; 1039 + $config_inline = PhabricatorEnv::getEnvConfig($config_key_inline); 1040 + 1041 + $config_key_attach = 'metamta.differential.attach-patches'; 1042 + $config_attach = PhabricatorEnv::getEnvConfig($config_key_attach); 1043 + 1044 + if ($config_inline || $config_attach) { 1045 + $patch = $this->renderPatchForMail($diff); 1046 + $lines = count(phutil_split_lines($patch)); 1047 + 1048 + if ($config_inline && ($lines <= $config_inline)) { 1049 + $body->addTextSection( 1050 + pht('CHANGE DETAILS'), 1051 + $patch); 1052 + } 1053 + 1054 + if ($config_attach) { 1055 + $name = pht('D%s.%s.patch', $object->getID(), $diff->getID()); 1056 + $mime_type = 'text/x-patch; charset=utf-8'; 1057 + $body->addAttachment( 1058 + new PhabricatorMetaMTAAttachment($patch, $name, $mime_type)); 1059 + } 1060 + } 1061 + } 1022 1062 1023 1063 return $body; 1024 1064 } ··· 1489 1529 ArcanistDifferentialRevisionHash::TABLE_NAME, 1490 1530 implode(', ', $sql)); 1491 1531 } 1532 + } 1533 + 1534 + private function renderAffectedFilesForMail(DifferentialDiff $diff) { 1535 + $changesets = $diff->getChangesets(); 1536 + 1537 + $filenames = mpull($changesets, 'getDisplayFilename'); 1538 + sort($filenames); 1539 + 1540 + $count = count($filenames); 1541 + $max = 250; 1542 + if ($count > $max) { 1543 + $filenames = array_slice($filenames, 0, $max); 1544 + $filenames[] = pht('(%d more files...)', ($count - $max)); 1545 + } 1546 + 1547 + return implode("\n", $filenames); 1548 + } 1549 + 1550 + private function renderPatchForMail(DifferentialDiff $diff) { 1551 + $format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format'); 1552 + 1553 + return id(new DifferentialRawDiffRenderer()) 1554 + ->setViewer($this->getActor()) 1555 + ->setFormat($format) 1556 + ->setChangesets($diff->getChangesets()) 1557 + ->buildPatch(); 1492 1558 } 1493 1559 1494 1560 }
-14
src/applications/differential/field/specification/DifferentialArcanistProjectFieldSpecification.php
··· 35 35 return $diff->getArcanistProjectPHID(); 36 36 } 37 37 38 - public function renderValueForMail($phase) { 39 - $diff = $this->getRevision()->loadActiveDiff(); 40 - if ($diff) { 41 - $phid = $diff->getArcanistProjectPHID(); 42 - if ($phid) { 43 - $handle = id(new PhabricatorHandleQuery()) 44 - ->setViewer($this->getUser()) 45 - ->withPHIDs(array($phid)) 46 - ->executeOne(); 47 - return "ARCANIST PROJECT\n ".$handle->getName(); 48 - } 49 - } 50 - } 51 - 52 38 }
-12
src/applications/differential/field/specification/DifferentialBranchFieldSpecification.php
··· 31 31 return $this->getBranchOrBookmarkDescription($diff); 32 32 } 33 33 34 - public function renderValueForMail($phase) { 35 - $diff = $this->getRevision()->loadActiveDiff(); 36 - if ($diff) { 37 - $description = $this->getBranchOrBookmarkDescription($diff); 38 - if ($description) { 39 - return "BRANCH\n {$description}"; 40 - } 41 - } 42 - 43 - return null; 44 - } 45 - 46 34 }
-26
src/applications/differential/field/specification/DifferentialCommitsFieldSpecification.php
··· 34 34 return $revision->getCommitPHIDs(); 35 35 } 36 36 37 - public function renderValueForMail($phase) { 38 - $revision = $this->getRevision(); 39 - 40 - if ($revision->getStatus() != ArcanistDifferentialRevisionStatus::CLOSED) { 41 - return null; 42 - } 43 - 44 - $phids = $revision->loadCommitPHIDs(); 45 - if (!$phids) { 46 - return null; 47 - } 48 - 49 - $body = array(); 50 - $handles = id(new PhabricatorHandleQuery()) 51 - ->setViewer($this->getUser()) 52 - ->withPHIDs($phids) 53 - ->execute(); 54 - $body[] = pht('COMMIT(S)', count($handles)); 55 - 56 - foreach ($handles as $handle) { 57 - $body[] = ' '.PhabricatorEnv::getProductionURI($handle->getURI()); 58 - } 59 - 60 - return implode("\n", $body); 61 - } 62 - 63 37 }
-17
src/applications/differential/field/specification/DifferentialFieldSpecification.php
··· 385 385 } 386 386 387 387 388 - /* -( Extending the E-mail Interface )------------------------------------- */ 389 - 390 - 391 - /** 392 - * Return plain text to render in e-mail messages. The text may span 393 - * multiple lines. 394 - * 395 - * @return int One of DifferentialMailPhase constants. 396 - * @return string|null Plain text, or null for no message. 397 - * 398 - * @task mail 399 - */ 400 - public function renderValueForMail($phase) { 401 - return null; 402 - } 403 - 404 - 405 388 /* -( Extending the Conduit Interface )------------------------------------ */ 406 389 407 390
-28
src/applications/differential/field/specification/DifferentialManiphestTasksFieldSpecification.php
··· 127 127 return $task_phids; 128 128 } 129 129 130 - public function renderValueForMail($phase) { 131 - if ($phase == DifferentialMailPhase::COMMENT) { 132 - return null; 133 - } 134 - 135 - if (!$this->maniphestTasks) { 136 - return null; 137 - } 138 - 139 - $handles = id(new PhabricatorHandleQuery()) 140 - ->setViewer($this->getUser()) 141 - ->withPHIDs($this->maniphestTasks) 142 - ->execute(); 143 - $body = array(); 144 - $body[] = 'MANIPHEST TASKS'; 145 - foreach ($handles as $handle) { 146 - $body[] = ' '.PhabricatorEnv::getProductionURI($handle->getURI()); 147 - } 148 - return implode("\n", $body); 149 - } 150 - 151 - public function getCommitMessageTips() { 152 - return array( 153 - 'Use "Fixes T123" in your summary to mark that the current '. 154 - 'revision completes a given task.' 155 - ); 156 - } 157 - 158 130 }
-21
src/applications/differential/field/specification/DifferentialReviewersFieldSpecification.php
··· 173 173 return $revision->getReviewers(); 174 174 } 175 175 176 - public function renderValueForMail($phase) { 177 - if ($phase == DifferentialMailPhase::COMMENT) { 178 - return null; 179 - } 180 - 181 - if (!$this->reviewers) { 182 - return null; 183 - } 184 - 185 - $handles = id(new PhabricatorHandleQuery()) 186 - ->setViewer($this->getUser()) 187 - ->withPHIDs($this->reviewers) 188 - ->execute(); 189 - $handles = array_select_keys( 190 - $handles, 191 - array($this->getRevision()->getPrimaryReviewer())) + $handles; 192 - 193 - $names = mpull($handles, 'getObjectName'); 194 - return 'Reviewers: '.implode(', ', $names); 195 - } 196 - 197 176 }
-21
src/applications/differential/field/specification/DifferentialRevisionIDFieldSpecification.php
··· 96 96 return 'D'.$revision->getID(); 97 97 } 98 98 99 - public function renderValueForMail($phase) { 100 - $body = array(); 101 - $body[] = 'REVISION DETAIL'; 102 - $body[] = ' '.PhabricatorEnv::getProductionURI('/D'.$this->id); 103 - 104 - if ($phase == DifferentialMailPhase::UPDATE) { 105 - $diffs = id(new DifferentialDiff())->loadAllWhere( 106 - 'revisionID = %d ORDER BY id DESC LIMIT 2', 107 - $this->id); 108 - if (count($diffs) == 2) { 109 - list($new, $old) = array_values(mpull($diffs, 'getID')); 110 - $body[] = null; 111 - $body[] = 'CHANGE SINCE LAST DIFF'; 112 - $body[] = ' '.PhabricatorEnv::getProductionURI( 113 - "/D{$this->id}?vs={$old}&id={$new}#toc"); 114 - } 115 - } 116 - 117 - return implode("\n", $body); 118 - } 119 - 120 99 }
-12
src/applications/differential/field/specification/DifferentialSummaryFieldSpecification.php
··· 67 67 return (string)$value; 68 68 } 69 69 70 - public function renderValueForMail($phase) { 71 - if ($phase != DifferentialMailPhase::WELCOME) { 72 - return null; 73 - } 74 - 75 - if ($this->summary == '') { 76 - return null; 77 - } 78 - 79 - return $this->summary; 80 - } 81 - 82 70 public function shouldAddToSearchIndex() { 83 71 return true; 84 72 }
-12
src/applications/differential/field/specification/DifferentialTestPlanFieldSpecification.php
··· 91 91 return $value; 92 92 } 93 93 94 - public function renderValueForMail($phase) { 95 - if ($phase != DifferentialMailPhase::WELCOME) { 96 - return null; 97 - } 98 - 99 - if ($this->plan == '') { 100 - return null; 101 - } 102 - 103 - return "TEST PLAN\n".preg_replace('/^/m', ' ', $this->plan); 104 - } 105 - 106 94 public function shouldAddToSearchIndex() { 107 95 return true; 108 96 }
-22
src/applications/differential/mail/DifferentialCCWelcomeMail.php
··· 1 - <?php 2 - 3 - final class DifferentialCCWelcomeMail extends DifferentialReviewRequestMail { 4 - 5 - protected function renderVaryPrefix() { 6 - return '[Added to CC]'; 7 - } 8 - 9 - protected function renderBody() { 10 - 11 - $actor = $this->getActorName(); 12 - $name = $this->getRevision()->getTitle(); 13 - $body = array(); 14 - 15 - $body[] = "{$actor} added you to the CC list for the revision \"{$name}\"."; 16 - $body[] = null; 17 - 18 - $body[] = $this->renderReviewRequestBody(); 19 - 20 - return implode("\n", $body); 21 - } 22 - }
-19
src/applications/differential/mail/DifferentialDiffContentMail.php
··· 1 - <?php 2 - 3 - final class DifferentialDiffContentMail extends DifferentialMail { 4 - 5 - protected $content; 6 - 7 - public function __construct(DifferentialRevision $revision, $content) { 8 - $this->setRevision($revision); 9 - $this->content = $content; 10 - } 11 - 12 - protected function renderVaryPrefix() { 13 - return '[Content]'; 14 - } 15 - 16 - protected function renderBody() { 17 - return $this->content; 18 - } 19 - }
-443
src/applications/differential/mail/DifferentialMail.php
··· 2 2 3 3 abstract class DifferentialMail extends PhabricatorMail { 4 4 5 - protected $to = array(); 6 - protected $cc = array(); 7 - protected $excludePHIDs = array(); 8 - 9 - protected $actorHandle; 10 - 11 - protected $revision; 12 - protected $comments; 13 - protected $changesets; 14 - protected $inlineComments; 15 - protected $isFirstMailAboutRevision; 16 - protected $isFirstMailToRecipients; 17 - protected $heraldTranscriptURI; 18 - protected $heraldRulesHeader; 19 - protected $replyHandler; 20 - protected $parentMessageID; 21 - 22 - private $rawMail; 23 - 24 - public function getRawMail() { 25 - if (!$this->rawMail) { 26 - throw new Exception("Call send() before getRawMail()!"); 27 - } 28 - return $this->rawMail; 29 - } 30 - 31 - protected function renderSubject() { 32 - $revision = $this->getRevision(); 33 - $title = $revision->getTitle(); 34 - $id = $revision->getID(); 35 - return "D{$id}: {$title}"; 36 - } 37 - 38 - abstract protected function renderVaryPrefix(); 39 - abstract protected function renderBody(); 40 - 41 - public function setActorHandle($actor_handle) { 42 - $this->actorHandle = $actor_handle; 43 - return $this; 44 - } 45 - 46 - public function getActorHandle() { 47 - return $this->actorHandle; 48 - } 49 - 50 - protected function getActorName() { 51 - $handle = $this->getActorHandle(); 52 - if ($handle) { 53 - return $handle->getName(); 54 - } 55 - return '???'; 56 - } 57 - 58 - public function setParentMessageID($parent_message_id) { 59 - $this->parentMessageID = $parent_message_id; 60 - return $this; 61 - } 62 - 63 - public function setXHeraldRulesHeader($header) { 64 - $this->heraldRulesHeader = $header; 65 - return $this; 66 - } 67 - 68 - public function send() { 69 - $to_phids = $this->getToPHIDs(); 70 - if (!$to_phids) { 71 - throw new Exception('No "To:" users provided!'); 72 - } 73 - 74 - $cc_phids = $this->getCCPHIDs(); 75 - $attachments = $this->buildAttachments(); 76 - 77 - $template = new PhabricatorMetaMTAMail(); 78 - $actor_handle = $this->getActorHandle(); 79 - $reply_handler = $this->getReplyHandler(); 80 - 81 - if ($actor_handle) { 82 - $template->setFrom($actor_handle->getPHID()); 83 - } 84 - 85 - $template 86 - ->setIsHTML($this->shouldMarkMailAsHTML()) 87 - ->setParentMessageID($this->parentMessageID) 88 - ->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs()) 89 - ->addHeader('Thread-Topic', $this->getThreadTopic()); 90 - 91 - $template->setAttachments($attachments); 92 - 93 - $template->setThreadID( 94 - $this->getThreadID(), 95 - $this->isFirstMailAboutRevision()); 96 - 97 - if ($this->heraldRulesHeader) { 98 - $template->addHeader('X-Herald-Rules', $this->heraldRulesHeader); 99 - } 100 - 101 - $revision = $this->revision; 102 - if ($revision) { 103 - if ($revision->getAuthorPHID()) { 104 - $template->addHeader( 105 - 'X-Differential-Author', 106 - '<'.$revision->getAuthorPHID().'>'); 107 - } 108 - 109 - $reviewer_phids = $revision->getReviewers(); 110 - if ($reviewer_phids) { 111 - // Add several headers to support e-mail clients which are not able to 112 - // create rules using regular expressions or wildcards (namely Outlook). 113 - $template->addPHIDHeaders('X-Differential-Reviewer', $reviewer_phids); 114 - 115 - // Add it also as a list to allow matching of the first reviewer and 116 - // also for backwards compatibility. 117 - $template->addHeader( 118 - 'X-Differential-Reviewers', 119 - '<'.implode('>, <', $reviewer_phids).'>'); 120 - } 121 - 122 - if ($cc_phids) { 123 - $template->addPHIDHeaders('X-Differential-CC', $cc_phids); 124 - $template->addHeader( 125 - 'X-Differential-CCs', 126 - '<'.implode('>, <', $cc_phids).'>'); 127 - 128 - // Determine explicit CCs (those added by humans) and put them in a 129 - // header so users can differentiate between Herald CCs and human CCs. 130 - 131 - $relation_subscribed = DifferentialRevision::RELATION_SUBSCRIBED; 132 - $raw = $revision->getRawRelations($relation_subscribed); 133 - 134 - $reason_phids = ipull($raw, 'reasonPHID'); 135 - $reason_handles = id(new PhabricatorHandleQuery()) 136 - ->setViewer($this->getActor()) 137 - ->withPHIDs($reason_phids) 138 - ->execute(); 139 - 140 - $explicit_cc = array(); 141 - foreach ($raw as $relation) { 142 - if (!$relation['reasonPHID']) { 143 - continue; 144 - } 145 - $type = $reason_handles[$relation['reasonPHID']]->getType(); 146 - if ($type == PhabricatorPeoplePHIDTypeUser::TYPECONST) { 147 - $explicit_cc[] = $relation['objectPHID']; 148 - } 149 - } 150 - 151 - if ($explicit_cc) { 152 - $template->addPHIDHeaders('X-Differential-Explicit-CC', $explicit_cc); 153 - $template->addHeader( 154 - 'X-Differential-Explicit-CCs', 155 - '<'.implode('>, <', $explicit_cc).'>'); 156 - } 157 - } 158 - } 159 - 160 - $template->setIsBulk(true); 161 - $template->setRelatedPHID($this->getRevision()->getPHID()); 162 - 163 - $mailtags = $this->getMailTags(); 164 - if ($mailtags) { 165 - $template->setMailTags($mailtags); 166 - } 167 - 168 - $phids = array(); 169 - foreach ($to_phids as $phid) { 170 - $phids[$phid] = true; 171 - } 172 - foreach ($cc_phids as $phid) { 173 - $phids[$phid] = true; 174 - } 175 - $phids = array_keys($phids); 176 - 177 - $handles = id(new PhabricatorHandleQuery()) 178 - ->setViewer($this->getActor()) 179 - ->withPHIDs($phids) 180 - ->execute(); 181 - $objects = id(new PhabricatorObjectQuery()) 182 - ->setViewer($this->getActor()) 183 - ->withPHIDs($phids) 184 - ->execute(); 185 - 186 - $to_handles = array_select_keys($handles, $to_phids); 187 - $cc_handles = array_select_keys($handles, $cc_phids); 188 - 189 - $this->prepareBody(); 190 - 191 - $this->rawMail = clone $template; 192 - $this->rawMail->addTos($to_phids); 193 - $this->rawMail->addCCs($cc_phids); 194 - 195 - $mails = $reply_handler->multiplexMail($template, $to_handles, $cc_handles); 196 - 197 - $original_translator = PhutilTranslator::getInstance(); 198 - if (!PhabricatorMetaMTAMail::shouldMultiplexAllMail()) { 199 - $translation = PhabricatorEnv::newObjectFromConfig( 200 - 'translation.provider'); 201 - $translator = id(new PhutilTranslator()) 202 - ->setLanguage($translation->getLanguage()) 203 - ->addTranslations($translation->getTranslations()); 204 - } 205 - 206 - try { 207 - foreach ($mails as $mail) { 208 - if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) { 209 - $translation = newv($mail->getTranslation($objects), array()); 210 - $translator = id(new PhutilTranslator()) 211 - ->setLanguage($translation->getLanguage()) 212 - ->addTranslations($translation->getTranslations()); 213 - PhutilTranslator::setInstance($translator); 214 - } 215 - 216 - $body = 217 - $this->buildBody()."\n". 218 - $reply_handler->getRecipientsSummary($to_handles, $cc_handles); 219 - 220 - $mail 221 - ->setSubject($this->renderSubject()) 222 - ->setSubjectPrefix($this->getSubjectPrefix()) 223 - ->setVarySubjectPrefix($this->renderVaryPrefix()) 224 - ->setBody($body); 225 - 226 - $event = new PhabricatorEvent( 227 - PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL, 228 - array( 229 - 'mail' => $mail, 230 - ) 231 - ); 232 - PhutilEventEngine::dispatchEvent($event); 233 - $mail = $event->getValue('mail'); 234 - 235 - $mail->saveAndSend(); 236 - } 237 - 238 - } catch (Exception $ex) { 239 - PhutilTranslator::setInstance($original_translator); 240 - throw $ex; 241 - } 242 - 243 - PhutilTranslator::setInstance($original_translator); 244 - 245 - return $this; 246 - } 247 - 248 - protected function getMailTags() { 249 - return array(); 250 - } 251 - 252 - protected function getSubjectPrefix() { 253 - return PhabricatorEnv::getEnvConfig('metamta.differential.subject-prefix'); 254 - } 255 - 256 - protected function shouldMarkMailAsHTML() { 257 - return false; 258 - } 259 - 260 - /** 261 - * @{method:buildBody} is called once for each e-mail recipient to allow 262 - * translating text to his language. This method can be used to load data that 263 - * don't need translation and use them later in @{method:buildBody}. 264 - * 265 - * @param 266 - * @return 267 - */ 268 - protected function prepareBody() { 269 - } 270 - 271 - protected function buildBody() { 272 - $main_body = $this->renderBody(); 273 - 274 - $body = new PhabricatorMetaMTAMailBody(); 275 - $body->addRawSection($main_body); 276 - 277 - $reply_handler = $this->getReplyHandler(); 278 - $body->addReplySection($reply_handler->getReplyHandlerInstructions()); 279 - 280 - if ($this->getHeraldTranscriptURI() && $this->isFirstMailToRecipients()) { 281 - $xscript_uri = $this->getHeraldTranscriptURI(); 282 - $body->addHeraldSection($xscript_uri); 283 - } 284 - 285 - return $body->render(); 286 - } 287 - 288 - /** 289 - * You can override this method in a subclass and return array of attachments 290 - * to be sent with the email. Each attachment is an instance of 291 - * PhabricatorMetaMTAAttachment. 292 - */ 293 - protected function buildAttachments() { 294 - return array(); 295 - } 296 - 297 - public function getReplyHandler() { 298 - if (!$this->replyHandler) { 299 - $this->replyHandler = 300 - self::newReplyHandlerForRevision($this->getRevision()); 301 - } 302 - 303 - return $this->replyHandler; 304 - } 305 - 306 5 public static function newReplyHandlerForRevision( 307 6 DifferentialRevision $revision) { 308 7 ··· 311 10 $reply_handler->setMailReceiver($revision); 312 11 313 12 return $reply_handler; 314 - } 315 - 316 - 317 - protected function formatText($text) { 318 - $text = explode("\n", rtrim($text)); 319 - foreach ($text as &$line) { 320 - $line = rtrim(' '.$line); 321 - } 322 - unset($line); 323 - return implode("\n", $text); 324 - } 325 - 326 - public function setExcludeMailRecipientPHIDs(array $exclude) { 327 - $this->excludePHIDs = $exclude; 328 - return $this; 329 - } 330 - 331 - public function getExcludeMailRecipientPHIDs() { 332 - return $this->excludePHIDs; 333 - } 334 - 335 - public function setToPHIDs(array $to) { 336 - $this->to = $to; 337 - return $this; 338 - } 339 - 340 - public function setCCPHIDs(array $cc) { 341 - $this->cc = $cc; 342 - return $this; 343 - } 344 - 345 - protected function getToPHIDs() { 346 - return $this->to; 347 - } 348 - 349 - protected function getCCPHIDs() { 350 - return $this->cc; 351 - } 352 - 353 - public function setRevision($revision) { 354 - $this->revision = $revision; 355 - return $this; 356 - } 357 - 358 - public function getRevision() { 359 - return $this->revision; 360 - } 361 - 362 - protected function getThreadID() { 363 - $phid = $this->getRevision()->getPHID(); 364 - return "differential-rev-{$phid}-req"; 365 - } 366 - 367 - protected function getThreadTopic() { 368 - $id = $this->getRevision()->getID(); 369 - $title = $this->getRevision()->getOriginalTitle(); 370 - return "D{$id}: {$title}"; 371 - } 372 - 373 - public function setComments(array $comments) { 374 - $this->comments = $comments; 375 - return $this; 376 - } 377 - 378 - public function getComments() { 379 - return $this->comments; 380 - } 381 - 382 - public function setChangesets($changesets) { 383 - $this->changesets = $changesets; 384 - return $this; 385 - } 386 - 387 - public function getChangesets() { 388 - return $this->changesets; 389 - } 390 - 391 - public function setInlineComments(array $inline_comments) { 392 - assert_instances_of($inline_comments, 'DifferentialTransaction'); 393 - $this->inlineComments = $inline_comments; 394 - return $this; 395 - } 396 - 397 - public function getInlineComments() { 398 - return $this->inlineComments; 399 - } 400 - 401 - protected function renderAuxFields($phase) { 402 - $selector = DifferentialFieldSelector::newSelector(); 403 - $aux_fields = $selector->sortFieldsForMail( 404 - $selector->getFieldSpecifications()); 405 - 406 - $body = array(); 407 - foreach ($aux_fields as $field) { 408 - $field->setUser($this->getActor()); 409 - $field->setRevision($this->getRevision()); 410 - // TODO: Introduce and use getRequiredHandlePHIDsForMail() and load all 411 - // handles in prepareBody(). 412 - $text = $field->renderValueForMail($phase); 413 - if ($text !== null) { 414 - $body[] = $text; 415 - $body[] = null; 416 - } 417 - } 418 - 419 - return implode("\n", $body); 420 - } 421 - 422 - public function setIsFirstMailToRecipients($first) { 423 - $this->isFirstMailToRecipients = $first; 424 - return $this; 425 - } 426 - 427 - public function isFirstMailToRecipients() { 428 - return $this->isFirstMailToRecipients; 429 - } 430 - 431 - public function setIsFirstMailAboutRevision($first) { 432 - $this->isFirstMailAboutRevision = $first; 433 - return $this; 434 - } 435 - 436 - public function isFirstMailAboutRevision() { 437 - return $this->isFirstMailAboutRevision; 438 - } 439 - 440 - public function setHeraldTranscriptURI($herald_transcript_uri) { 441 - $this->heraldTranscriptURI = $herald_transcript_uri; 442 - return $this; 443 - } 444 - 445 - public function getHeraldTranscriptURI() { 446 - return $this->heraldTranscriptURI; 447 - } 448 - 449 - protected function renderHandleList(array $handles, array $phids) { 450 - assert_instances_of($handles, 'PhabricatorObjectHandle'); 451 - $names = array(); 452 - foreach ($phids as $phid) { 453 - $names[] = $handles[$phid]->getName(); 454 - } 455 - return implode(', ', $names); 456 13 } 457 14 458 15 }
-37
src/applications/differential/mail/DifferentialNewDiffMail.php
··· 1 - <?php 2 - 3 - final class DifferentialNewDiffMail extends DifferentialReviewRequestMail { 4 - 5 - protected function renderVaryPrefix() { 6 - $revision = $this->getRevision(); 7 - $line_count = $revision->getLineCount(); 8 - $lines = pht('%d line(s)', $line_count); 9 - 10 - if ($this->isFirstMailToRecipients()) { 11 - $verb = 'Request'; 12 - } else { 13 - $verb = 'Updated'; 14 - } 15 - 16 - return "[{$verb}, {$lines}]"; 17 - } 18 - 19 - protected function renderBody() { 20 - $actor = $this->getActorName(); 21 - 22 - $name = $this->getRevision()->getTitle(); 23 - 24 - $body = array(); 25 - 26 - if ($this->isFirstMailToRecipients()) { 27 - $body[] = "{$actor} requested code review of \"{$name}\"."; 28 - } else { 29 - $body[] = "{$actor} updated the revision \"{$name}\"."; 30 - } 31 - $body[] = null; 32 - 33 - $body[] = $this->renderReviewRequestBody(); 34 - 35 - return implode("\n", $body); 36 - } 37 - }
-129
src/applications/differential/mail/DifferentialReviewRequestMail.php
··· 1 - <?php 2 - 3 - abstract class DifferentialReviewRequestMail extends DifferentialMail { 4 - 5 - const MAX_AFFECTED_FILES = 1000; 6 - 7 - protected $commentText; 8 - 9 - private $patch; 10 - 11 - public function setCommentText($comment_text) { 12 - $this->commentText = $comment_text; 13 - return $this; 14 - } 15 - 16 - public function getCommentText() { 17 - return $this->commentText; 18 - } 19 - 20 - public function __construct( 21 - DifferentialRevision $revision, 22 - PhabricatorObjectHandle $actor, 23 - array $changesets) { 24 - assert_instances_of($changesets, 'DifferentialChangeset'); 25 - 26 - $this->setRevision($revision); 27 - $this->setActorHandle($actor); 28 - $this->setChangesets($changesets); 29 - } 30 - 31 - protected function prepareBody() { 32 - parent::prepareBody(); 33 - 34 - $inline_max_length = PhabricatorEnv::getEnvConfig( 35 - 'metamta.differential.inline-patches'); 36 - if ($inline_max_length) { 37 - $patch = $this->buildPatch(); 38 - if (count(explode("\n", $patch)) <= $inline_max_length) { 39 - $this->patch = $patch; 40 - } 41 - } 42 - } 43 - 44 - protected function renderReviewRequestBody() { 45 - $revision = $this->getRevision(); 46 - 47 - $body = array(); 48 - if (!$this->isFirstMailToRecipients()) { 49 - if (strlen($this->getCommentText())) { 50 - $body[] = $this->formatText($this->getCommentText()); 51 - $body[] = null; 52 - } 53 - } 54 - 55 - $phase = ($this->isFirstMailToRecipients() ? 56 - DifferentialMailPhase::WELCOME : 57 - DifferentialMailPhase::UPDATE); 58 - $body[] = $this->renderAuxFields($phase); 59 - 60 - $changesets = $this->getChangesets(); 61 - if ($changesets) { 62 - $body[] = 'AFFECTED FILES'; 63 - $max = self::MAX_AFFECTED_FILES; 64 - foreach (array_values($changesets) as $i => $changeset) { 65 - if ($i == $max) { 66 - $body[] = ' ('.(count($changesets) - $max).' more files)'; 67 - break; 68 - } 69 - $body[] = ' '.$changeset->getFilename(); 70 - } 71 - $body[] = null; 72 - } 73 - 74 - if ($this->patch) { 75 - $body[] = 'CHANGE DETAILS'; 76 - $body[] = $this->patch; 77 - } 78 - 79 - return implode("\n", $body); 80 - } 81 - 82 - protected function buildAttachments() { 83 - $attachments = array(); 84 - 85 - if (PhabricatorEnv::getEnvConfig('metamta.differential.attach-patches')) { 86 - 87 - $revision = $this->getRevision(); 88 - $revision_id = $revision->getID(); 89 - 90 - $diffs = id(new DifferentialDiffQuery()) 91 - ->setViewer($this->getActor()) 92 - ->withRevisionIDs(array($revision_id)) 93 - ->execute(); 94 - $diff_number = count($diffs); 95 - 96 - $attachments[] = new PhabricatorMetaMTAAttachment( 97 - $this->buildPatch(), 98 - "D{$revision_id}.{$diff_number}.patch", 99 - 'text/x-patch; charset=utf-8' 100 - ); 101 - } 102 - 103 - return $attachments; 104 - } 105 - 106 - private function buildPatch() { 107 - $renderer = new DifferentialRawDiffRenderer(); 108 - $renderer->setChangesets($this->getChangesets()); 109 - $renderer->setFormat( 110 - PhabricatorEnv::getEnvConfig('metamta.differential.patch-format')); 111 - 112 - // TODO: It would be nice to have a real viewer here eventually, but 113 - // in the meantime anyone we're sending mail to can certainly see the 114 - // patch. 115 - $renderer->setViewer(PhabricatorUser::getOmnipotentUser()); 116 - return $renderer->buildPatch(); 117 - } 118 - 119 - protected function getMailTags() { 120 - $tags = array(); 121 - if ($this->isFirstMailToRecipients()) { 122 - $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEW_REQUEST; 123 - } else { 124 - $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_UPDATED; 125 - } 126 - return $tags; 127 - } 128 - 129 - }
+9 -2
src/applications/differential/storage/DifferentialTransaction.php
··· 148 148 break; 149 149 } 150 150 break; 151 + case self::TYPE_UPDATE: 152 + $old = $this->getOldValue(); 153 + if ($old === null) { 154 + $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEW_REQUEST; 155 + } else { 156 + $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_UPDATED; 157 + } 158 + break; 151 159 case PhabricatorTransactions::TYPE_EDGE: 152 160 switch ($this->getMetadataValue('edge:type')) { 153 161 case PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER: ··· 183 191 case self::TYPE_UPDATE: 184 192 if ($new) { 185 193 // TODO: Migrate to PHIDs and use handles here? 186 - // TODO: Link this? 187 - if (phid_get_type($new) == 'DIFF') { 194 + if (phid_get_type($new) == DifferentialPHIDTypeDiff::TYPECONST) { 188 195 return pht( 189 196 '%s updated this revision to %s.', 190 197 $author_handle,
+25
src/applications/metamta/view/PhabricatorMetaMTAMailBody.php
··· 10 10 final class PhabricatorMetaMTAMailBody { 11 11 12 12 private $sections = array(); 13 + private $attachments = array(); 13 14 14 15 15 16 /* -( Composition )-------------------------------------------------------- */ ··· 88 89 } 89 90 90 91 92 + /** 93 + * Add an attachment. 94 + * 95 + * @param PhabricatorMetaMTAAttachment Attachment. 96 + * @return this 97 + * @task compose 98 + */ 99 + public function addAttachment(PhabricatorMetaMTAAttachment $attachment) { 100 + $this->attachments[] = $attachment; 101 + return $this; 102 + } 103 + 104 + 91 105 /* -( Rendering )---------------------------------------------------------- */ 92 106 93 107 ··· 99 113 */ 100 114 public function render() { 101 115 return implode("\n\n", $this->sections)."\n"; 116 + } 117 + 118 + 119 + /** 120 + * Retrieve attachments. 121 + * 122 + * @return list<PhabricatorMetaMTAAttachment> Attachments. 123 + * @task render 124 + */ 125 + public function getAttachments() { 126 + return $this->attachments; 102 127 } 103 128 104 129
+4
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 1662 1662 ->setIsBulk(true) 1663 1663 ->setBody($body->render()); 1664 1664 1665 + foreach ($body->getAttachments() as $attachment) { 1666 + $template->addAttachment($attachment); 1667 + } 1668 + 1665 1669 $herald_xscript = $this->getHeraldTranscript(); 1666 1670 if ($herald_xscript) { 1667 1671 $herald_header = $herald_xscript->getXHeraldRulesHeader();