Skip to content

Commit e84f9f9

Browse files
author
vrana
committed
Send Differential e-mails in user's language
Summary: Works this way: - Select users' language with multiplexing. - Select default language otherwise (it can be different from current user's language). - Build body and subject for each user individually. - Set the original language after sending the mails. Test Plan: - Comment on a diff of user with custom translation. - Set default to a custom translation. Comment on a diff of user with default translation. - Set default to a default translation. Comment on a diff of user with default translation. Repeat with/without multiplexing. Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Maniphest Tasks: T1139 Differential Revision: https://secure.phabricator.com/D2774
1 parent 112acf1 commit e84f9f9

File tree

7 files changed

+165
-57
lines changed

7 files changed

+165
-57
lines changed

src/applications/base/controller/PhabricatorController.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ final public function willBeginExecution() {
6262

6363
$translation = $user->getTranslation();
6464
if ($translation &&
65-
$translation != PhabricatorEnv::getEnvConfig('translation.provider') &&
66-
class_exists($translation) &&
67-
is_subclass_of($translation, 'PhabricatorTranslation')) {
65+
$translation != PhabricatorEnv::getEnvConfig('translation.provider')) {
6866
$translation = newv($translation, array());
6967
PhutilTranslator::getInstance()
7068
->setLanguage($translation->getLanguage())

src/applications/differential/mail/DifferentialCommentMail.php

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ final class DifferentialCommentMail extends DifferentialMail {
2020

2121
protected $changedByCommit;
2222

23+
private $addedReviewers;
24+
private $addedCCs;
25+
2326
public function setChangedByCommit($changed_by_commit) {
2427
$this->changedByCommit = $changed_by_commit;
2528
return $this;
@@ -87,20 +90,11 @@ protected function getVerb() {
8790
return $verb;
8891
}
8992

90-
protected function renderBody() {
91-
92-
$comment = $this->getComment();
93-
94-
$actor = $this->getActorName();
95-
$name = $this->getRevision()->getTitle();
96-
$verb = $this->getVerb();
97-
98-
$body = array();
99-
100-
$body[] = "{$actor} has {$verb} the revision \"{$name}\".";
93+
protected function prepareBody() {
94+
parent::prepareBody();
10195

10296
// If the commented added reviewers or CCs, list them explicitly.
103-
$meta = $comment->getMetadata();
97+
$meta = $this->getComment()->getMetadata();
10498
$m_reviewers = idx(
10599
$meta,
106100
DifferentialComment::METADATA_ADDED_REVIEWERS,
@@ -113,16 +107,32 @@ protected function renderBody() {
113107
if ($load) {
114108
$handles = id(new PhabricatorObjectHandleData($load))->loadHandles();
115109
if ($m_reviewers) {
116-
$body[] = 'Added Reviewers: '.$this->renderHandleList(
117-
$handles,
118-
$m_reviewers);
110+
$this->addedReviewers = $this->renderHandleList($handles, $m_reviewers);
119111
}
120112
if ($m_cc) {
121-
$body[] = 'Added CCs: '.$this->renderHandleList(
122-
$handles,
123-
$m_cc);
113+
$this->addedCCs = $this->renderHandleList($handles, $m_cc);
124114
}
125115
}
116+
}
117+
118+
protected function renderBody() {
119+
120+
$comment = $this->getComment();
121+
122+
$actor = $this->getActorName();
123+
$name = $this->getRevision()->getTitle();
124+
$verb = $this->getVerb();
125+
126+
$body = array();
127+
128+
$body[] = "{$actor} has {$verb} the revision \"{$name}\".";
129+
130+
if ($this->addedReviewers) {
131+
$body[] = 'Added Reviewers: '.$this->addedReviewers;
132+
}
133+
if ($this->addedCCs) {
134+
$body[] = 'Added CCs: '.$this->addedCCs;
135+
}
126136

127137
$body[] = null;
128138

src/applications/differential/mail/DifferentialMail.php

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ public function send() {
7878
}
7979

8080
$cc_phids = $this->getCCPHIDs();
81-
$body = $this->buildBody();
8281
$attachments = $this->buildAttachments();
8382

8483
$template = new PhabricatorMetaMTAMail();
@@ -90,10 +89,6 @@ public function send() {
9089
}
9190

9291
$template
93-
->setSubject($this->renderSubject())
94-
->setSubjectPrefix($this->getSubjectPrefix())
95-
->setVarySubjectPrefix($this->renderVaryPrefix())
96-
->setBody($body)
9792
->setIsHTML($this->shouldMarkMailAsHTML())
9893
->setParentMessageID($this->parentMessageID)
9994
->addHeader('Thread-Topic', $this->getThreadTopic());
@@ -172,25 +167,62 @@ public function send() {
172167
$phids = array_keys($phids);
173168

174169
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
170+
$objects = id(new PhabricatorObjectHandleData($phids))->loadObjects();
175171

176-
$event = new PhabricatorEvent(
177-
PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL,
178-
array(
179-
'mail' => $template,
180-
)
181-
);
182-
PhutilEventEngine::dispatchEvent($event);
172+
$to_handles = array_select_keys($handles, $to_phids);
173+
$cc_handles = array_select_keys($handles, $cc_phids);
183174

184-
$template = $event->getValue('mail');
175+
$this->prepareBody();
185176

186-
$mails = $reply_handler->multiplexMail(
187-
$template,
188-
array_select_keys($handles, $to_phids),
189-
array_select_keys($handles, $cc_phids));
177+
$mails = $reply_handler->multiplexMail($template, $to_handles, $cc_handles);
190178

191-
foreach ($mails as $mail) {
192-
$mail->saveAndSend();
179+
$original_translator = PhutilTranslator::getInstance();
180+
if (!PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
181+
$translation = PhabricatorEnv::newObjectFromConfig(
182+
'translation.provider');
183+
$translator = id(new PhutilTranslator())
184+
->setLanguage($translation->getLanguage())
185+
->addTranslations($translation->getTranslations());
193186
}
187+
188+
try {
189+
foreach ($mails as $mail) {
190+
if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
191+
$translation = newv($mail->getTranslation($objects), array());
192+
$translator = id(new PhutilTranslator())
193+
->setLanguage($translation->getLanguage())
194+
->addTranslations($translation->getTranslations());
195+
PhutilTranslator::setInstance($translator);
196+
}
197+
198+
$body =
199+
$this->buildBody()."\n".
200+
$reply_handler->getRecipientsSummary($to_handles, $cc_handles);
201+
202+
$mail
203+
->setSubject($this->renderSubject())
204+
->setSubjectPrefix($this->getSubjectPrefix())
205+
->setVarySubjectPrefix($this->renderVaryPrefix())
206+
->setBody($body);
207+
208+
$event = new PhabricatorEvent(
209+
PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL,
210+
array(
211+
'mail' => $mail,
212+
)
213+
);
214+
PhutilEventEngine::dispatchEvent($event);
215+
$mail = $event->getValue('mail');
216+
217+
$mail->saveAndSend();
218+
}
219+
220+
} catch (Exception $ex) {
221+
PhutilTranslator::setInstance($original_translator);
222+
throw $ex;
223+
}
224+
225+
PhutilTranslator::setInstance($original_translator);
194226
}
195227

196228
protected function getMailTags() {
@@ -205,6 +237,17 @@ protected function shouldMarkMailAsHTML() {
205237
return false;
206238
}
207239

240+
/**
241+
* @{method:buildBody} is called once for each e-mail recipient to allow
242+
* translating text to his language. This method can be used to load data that
243+
* don't need translation and use them later in @{method:buildBody}.
244+
*
245+
* @param
246+
* @return
247+
*/
248+
protected function prepareBody() {
249+
}
250+
208251
protected function buildBody() {
209252

210253
$body = $this->renderBody();
@@ -369,6 +412,8 @@ protected function renderAuxFields($phase) {
369412
$body = array();
370413
foreach ($aux_fields as $field) {
371414
$field->setRevision($this->getRevision());
415+
// TODO: Introduce and use getRequiredHandlePHIDsForMail() and load all
416+
// handles in prepareBody().
372417
$text = $field->renderValueForMail($phase);
373418
if ($text !== null) {
374419
$body[] = $text;

src/applications/differential/mail/DifferentialReviewRequestMail.php

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ abstract class DifferentialReviewRequestMail extends DifferentialMail {
2020

2121
protected $comments;
2222

23+
private $patch;
24+
2325
public function setComments($comments) {
2426
$this->comments = $comments;
2527
return $this;
@@ -40,6 +42,19 @@ public function __construct(
4042
$this->setChangesets($changesets);
4143
}
4244

45+
protected function prepareBody() {
46+
parent::prepareBody();
47+
48+
$inline_max_length = PhabricatorEnv::getEnvConfig(
49+
'metamta.differential.inline-patches');
50+
if ($inline_max_length) {
51+
$patch = $this->buildPatch();
52+
if (count(explode("\n", $patch)) <= $inline_max_length) {
53+
$this->patch = $patch;
54+
}
55+
}
56+
}
57+
4358
protected function renderReviewRequestBody() {
4459
$revision = $this->getRevision();
4560

@@ -65,14 +80,9 @@ protected function renderReviewRequestBody() {
6580
$body[] = null;
6681
}
6782

68-
$inline_key = 'metamta.differential.inline-patches';
69-
$inline_max_length = PhabricatorEnv::getEnvConfig($inline_key);
70-
if ($inline_max_length) {
71-
$patch = $this->buildPatch();
72-
if (count(explode("\n", $patch)) <= $inline_max_length) {
73-
$body[] = 'CHANGE DETAILS';
74-
$body[] = $patch;
75-
}
83+
if ($this->patch) {
84+
$body[] = 'CHANGE DETAILS';
85+
$body[] = $this->patch;
7686
}
7787

7888
return implode("\n", $body);
@@ -110,14 +120,9 @@ public function loadFileByPHID($phid) {
110120
}
111121

112122
private function buildPatch() {
113-
$revision = $this->getRevision();
114-
$revision_id = $revision->getID();
115-
116-
$diffs = $revision->loadDiffs();
117-
$diff_number = count($diffs);
118-
$diff = array_pop($diffs);
123+
$diff = new DifferentialDiff();
119124

120-
$diff->attachChangesets($diff->loadChangesets());
125+
$diff->attachChangesets($this->getChangesets());
121126
// TODO: We could batch this to improve performance.
122127
foreach ($diff->getChangesets() as $changeset) {
123128
$changeset->attachHunks($changeset->loadHunks());

src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,22 @@ public function getPublicReplyHandlerEmailAddress() {
7171
return null;
7272
}
7373

74+
final public function getRecipientsSummary(
75+
array $to_handles,
76+
array $cc_handles) {
77+
assert_instances_of($to_handles, 'PhabricatorObjectHandle');
78+
assert_instances_of($cc_handles, 'PhabricatorObjectHandle');
79+
80+
$body = '';
81+
if ($to_handles) {
82+
$body .= "To: ".implode(', ', mpull($to_handles, 'getName'))."\n";
83+
}
84+
if ($cc_handles) {
85+
$body .= "Cc: ".implode(', ', mpull($cc_handles, 'getName'))."\n";
86+
}
87+
return $body;
88+
}
89+
7490
final public function multiplexMail(
7591
PhabricatorMetaMTAMail $mail_template,
7692
array $to_handles,
@@ -115,13 +131,12 @@ final public function multiplexMail(
115131
$body = $mail_template->getBody();
116132
$body .= "\n";
117133
if ($to_handles) {
118-
$body .= "To: ".implode(', ', mpull($to_handles, 'getName'))."\n";
119134
$add_headers['X-Phabricator-To'] = $this->formatPHIDList($to_handles);
120135
}
121136
if ($cc_handles) {
122-
$body .= "Cc: ".implode(', ', mpull($cc_handles, 'getName'))."\n";
123137
$add_headers['X-Phabricator-Cc'] = $this->formatPHIDList($cc_handles);
124138
}
139+
$body .= $this->getRecipientsSummary($to_handles, $cc_handles);
125140

126141
foreach ($recipients as $recipient) {
127142
$mail = clone $mail_template;

src/applications/metamta/storage/PhabricatorMetaMTAMail.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,28 @@ public function addCCs(array $phids) {
119119
return $this;
120120
}
121121

122+
public function getTranslation(array $objects) {
123+
$default_translation = PhabricatorEnv::getEnvConfig('translation.provider');
124+
$return = null;
125+
$recipients = array_merge(
126+
idx($this->parameters, 'to', array()),
127+
idx($this->parameters, 'cc', array()));
128+
foreach (array_select_keys($objects, $recipients) as $object) {
129+
$translation = null;
130+
if ($object instanceof PhabricatorUser) {
131+
$translation = $object->getTranslation();
132+
}
133+
if (!$translation) {
134+
$translation = $default_translation;
135+
}
136+
if ($return && $translation != $return) {
137+
return $default_translation;
138+
}
139+
$return = $translation;
140+
}
141+
return $return;
142+
}
143+
122144
public function addHeader($name, $value) {
123145
$this->parameters['headers'][$name] = $value;
124146
return $this;

src/applications/people/storage/PhabricatorUser.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,19 @@ public function getSex() {
9696
return $this->sex;
9797
}
9898

99+
public function getTranslation() {
100+
try {
101+
if ($this->translation &&
102+
class_exists($this->translation) &&
103+
is_subclass_of($this->translation, 'PhabricatorTranslation')) {
104+
return $this->translation;
105+
}
106+
} catch (PhutilMissingSymbolException $ex) {
107+
return null;
108+
}
109+
return null;
110+
}
111+
99112
public function isLoggedIn() {
100113
return !($this->getPHID() === null);
101114
}

0 commit comments

Comments
 (0)