mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 23:02:42 +01:00
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
This commit is contained in:
parent
112acf11cf
commit
e84f9f9ec9
7 changed files with 165 additions and 57 deletions
|
@ -62,9 +62,7 @@ abstract class PhabricatorController extends AphrontController {
|
||||||
|
|
||||||
$translation = $user->getTranslation();
|
$translation = $user->getTranslation();
|
||||||
if ($translation &&
|
if ($translation &&
|
||||||
$translation != PhabricatorEnv::getEnvConfig('translation.provider') &&
|
$translation != PhabricatorEnv::getEnvConfig('translation.provider')) {
|
||||||
class_exists($translation) &&
|
|
||||||
is_subclass_of($translation, 'PhabricatorTranslation')) {
|
|
||||||
$translation = newv($translation, array());
|
$translation = newv($translation, array());
|
||||||
PhutilTranslator::getInstance()
|
PhutilTranslator::getInstance()
|
||||||
->setLanguage($translation->getLanguage())
|
->setLanguage($translation->getLanguage())
|
||||||
|
|
|
@ -20,6 +20,9 @@ final class DifferentialCommentMail extends DifferentialMail {
|
||||||
|
|
||||||
protected $changedByCommit;
|
protected $changedByCommit;
|
||||||
|
|
||||||
|
private $addedReviewers;
|
||||||
|
private $addedCCs;
|
||||||
|
|
||||||
public function setChangedByCommit($changed_by_commit) {
|
public function setChangedByCommit($changed_by_commit) {
|
||||||
$this->changedByCommit = $changed_by_commit;
|
$this->changedByCommit = $changed_by_commit;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -87,20 +90,11 @@ final class DifferentialCommentMail extends DifferentialMail {
|
||||||
return $verb;
|
return $verb;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function renderBody() {
|
protected function prepareBody() {
|
||||||
|
parent::prepareBody();
|
||||||
$comment = $this->getComment();
|
|
||||||
|
|
||||||
$actor = $this->getActorName();
|
|
||||||
$name = $this->getRevision()->getTitle();
|
|
||||||
$verb = $this->getVerb();
|
|
||||||
|
|
||||||
$body = array();
|
|
||||||
|
|
||||||
$body[] = "{$actor} has {$verb} the revision \"{$name}\".";
|
|
||||||
|
|
||||||
// If the commented added reviewers or CCs, list them explicitly.
|
// If the commented added reviewers or CCs, list them explicitly.
|
||||||
$meta = $comment->getMetadata();
|
$meta = $this->getComment()->getMetadata();
|
||||||
$m_reviewers = idx(
|
$m_reviewers = idx(
|
||||||
$meta,
|
$meta,
|
||||||
DifferentialComment::METADATA_ADDED_REVIEWERS,
|
DifferentialComment::METADATA_ADDED_REVIEWERS,
|
||||||
|
@ -113,16 +107,32 @@ final class DifferentialCommentMail extends DifferentialMail {
|
||||||
if ($load) {
|
if ($load) {
|
||||||
$handles = id(new PhabricatorObjectHandleData($load))->loadHandles();
|
$handles = id(new PhabricatorObjectHandleData($load))->loadHandles();
|
||||||
if ($m_reviewers) {
|
if ($m_reviewers) {
|
||||||
$body[] = 'Added Reviewers: '.$this->renderHandleList(
|
$this->addedReviewers = $this->renderHandleList($handles, $m_reviewers);
|
||||||
$handles,
|
|
||||||
$m_reviewers);
|
|
||||||
}
|
}
|
||||||
if ($m_cc) {
|
if ($m_cc) {
|
||||||
$body[] = 'Added CCs: '.$this->renderHandleList(
|
$this->addedCCs = $this->renderHandleList($handles, $m_cc);
|
||||||
$handles,
|
|
||||||
$m_cc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderBody() {
|
||||||
|
|
||||||
|
$comment = $this->getComment();
|
||||||
|
|
||||||
|
$actor = $this->getActorName();
|
||||||
|
$name = $this->getRevision()->getTitle();
|
||||||
|
$verb = $this->getVerb();
|
||||||
|
|
||||||
|
$body = array();
|
||||||
|
|
||||||
|
$body[] = "{$actor} has {$verb} the revision \"{$name}\".";
|
||||||
|
|
||||||
|
if ($this->addedReviewers) {
|
||||||
|
$body[] = 'Added Reviewers: '.$this->addedReviewers;
|
||||||
|
}
|
||||||
|
if ($this->addedCCs) {
|
||||||
|
$body[] = 'Added CCs: '.$this->addedCCs;
|
||||||
|
}
|
||||||
|
|
||||||
$body[] = null;
|
$body[] = null;
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,6 @@ abstract class DifferentialMail {
|
||||||
}
|
}
|
||||||
|
|
||||||
$cc_phids = $this->getCCPHIDs();
|
$cc_phids = $this->getCCPHIDs();
|
||||||
$body = $this->buildBody();
|
|
||||||
$attachments = $this->buildAttachments();
|
$attachments = $this->buildAttachments();
|
||||||
|
|
||||||
$template = new PhabricatorMetaMTAMail();
|
$template = new PhabricatorMetaMTAMail();
|
||||||
|
@ -90,10 +89,6 @@ abstract class DifferentialMail {
|
||||||
}
|
}
|
||||||
|
|
||||||
$template
|
$template
|
||||||
->setSubject($this->renderSubject())
|
|
||||||
->setSubjectPrefix($this->getSubjectPrefix())
|
|
||||||
->setVarySubjectPrefix($this->renderVaryPrefix())
|
|
||||||
->setBody($body)
|
|
||||||
->setIsHTML($this->shouldMarkMailAsHTML())
|
->setIsHTML($this->shouldMarkMailAsHTML())
|
||||||
->setParentMessageID($this->parentMessageID)
|
->setParentMessageID($this->parentMessageID)
|
||||||
->addHeader('Thread-Topic', $this->getThreadTopic());
|
->addHeader('Thread-Topic', $this->getThreadTopic());
|
||||||
|
@ -172,25 +167,62 @@ abstract class DifferentialMail {
|
||||||
$phids = array_keys($phids);
|
$phids = array_keys($phids);
|
||||||
|
|
||||||
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
|
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
|
||||||
|
$objects = id(new PhabricatorObjectHandleData($phids))->loadObjects();
|
||||||
|
|
||||||
$event = new PhabricatorEvent(
|
$to_handles = array_select_keys($handles, $to_phids);
|
||||||
PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL,
|
$cc_handles = array_select_keys($handles, $cc_phids);
|
||||||
array(
|
|
||||||
'mail' => $template,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
PhutilEventEngine::dispatchEvent($event);
|
|
||||||
|
|
||||||
$template = $event->getValue('mail');
|
$this->prepareBody();
|
||||||
|
|
||||||
$mails = $reply_handler->multiplexMail(
|
$mails = $reply_handler->multiplexMail($template, $to_handles, $cc_handles);
|
||||||
$template,
|
|
||||||
array_select_keys($handles, $to_phids),
|
|
||||||
array_select_keys($handles, $cc_phids));
|
|
||||||
|
|
||||||
foreach ($mails as $mail) {
|
$original_translator = PhutilTranslator::getInstance();
|
||||||
$mail->saveAndSend();
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getMailTags() {
|
protected function getMailTags() {
|
||||||
|
@ -205,6 +237,17 @@ abstract class DifferentialMail {
|
||||||
return false;
|
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() {
|
protected function buildBody() {
|
||||||
|
|
||||||
$body = $this->renderBody();
|
$body = $this->renderBody();
|
||||||
|
@ -369,6 +412,8 @@ EOTEXT;
|
||||||
$body = array();
|
$body = array();
|
||||||
foreach ($aux_fields as $field) {
|
foreach ($aux_fields as $field) {
|
||||||
$field->setRevision($this->getRevision());
|
$field->setRevision($this->getRevision());
|
||||||
|
// TODO: Introduce and use getRequiredHandlePHIDsForMail() and load all
|
||||||
|
// handles in prepareBody().
|
||||||
$text = $field->renderValueForMail($phase);
|
$text = $field->renderValueForMail($phase);
|
||||||
if ($text !== null) {
|
if ($text !== null) {
|
||||||
$body[] = $text;
|
$body[] = $text;
|
||||||
|
|
|
@ -20,6 +20,8 @@ abstract class DifferentialReviewRequestMail extends DifferentialMail {
|
||||||
|
|
||||||
protected $comments;
|
protected $comments;
|
||||||
|
|
||||||
|
private $patch;
|
||||||
|
|
||||||
public function setComments($comments) {
|
public function setComments($comments) {
|
||||||
$this->comments = $comments;
|
$this->comments = $comments;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -40,6 +42,19 @@ abstract class DifferentialReviewRequestMail extends DifferentialMail {
|
||||||
$this->setChangesets($changesets);
|
$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() {
|
protected function renderReviewRequestBody() {
|
||||||
$revision = $this->getRevision();
|
$revision = $this->getRevision();
|
||||||
|
|
||||||
|
@ -65,14 +80,9 @@ abstract class DifferentialReviewRequestMail extends DifferentialMail {
|
||||||
$body[] = null;
|
$body[] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$inline_key = 'metamta.differential.inline-patches';
|
if ($this->patch) {
|
||||||
$inline_max_length = PhabricatorEnv::getEnvConfig($inline_key);
|
$body[] = 'CHANGE DETAILS';
|
||||||
if ($inline_max_length) {
|
$body[] = $this->patch;
|
||||||
$patch = $this->buildPatch();
|
|
||||||
if (count(explode("\n", $patch)) <= $inline_max_length) {
|
|
||||||
$body[] = 'CHANGE DETAILS';
|
|
||||||
$body[] = $patch;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return implode("\n", $body);
|
return implode("\n", $body);
|
||||||
|
@ -110,14 +120,9 @@ abstract class DifferentialReviewRequestMail extends DifferentialMail {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildPatch() {
|
private function buildPatch() {
|
||||||
$revision = $this->getRevision();
|
$diff = new DifferentialDiff();
|
||||||
$revision_id = $revision->getID();
|
|
||||||
|
|
||||||
$diffs = $revision->loadDiffs();
|
$diff->attachChangesets($this->getChangesets());
|
||||||
$diff_number = count($diffs);
|
|
||||||
$diff = array_pop($diffs);
|
|
||||||
|
|
||||||
$diff->attachChangesets($diff->loadChangesets());
|
|
||||||
// TODO: We could batch this to improve performance.
|
// TODO: We could batch this to improve performance.
|
||||||
foreach ($diff->getChangesets() as $changeset) {
|
foreach ($diff->getChangesets() as $changeset) {
|
||||||
$changeset->attachHunks($changeset->loadHunks());
|
$changeset->attachHunks($changeset->loadHunks());
|
||||||
|
|
|
@ -71,6 +71,22 @@ abstract class PhabricatorMailReplyHandler {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function getRecipientsSummary(
|
||||||
|
array $to_handles,
|
||||||
|
array $cc_handles) {
|
||||||
|
assert_instances_of($to_handles, 'PhabricatorObjectHandle');
|
||||||
|
assert_instances_of($cc_handles, 'PhabricatorObjectHandle');
|
||||||
|
|
||||||
|
$body = '';
|
||||||
|
if ($to_handles) {
|
||||||
|
$body .= "To: ".implode(', ', mpull($to_handles, 'getName'))."\n";
|
||||||
|
}
|
||||||
|
if ($cc_handles) {
|
||||||
|
$body .= "Cc: ".implode(', ', mpull($cc_handles, 'getName'))."\n";
|
||||||
|
}
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
final public function multiplexMail(
|
final public function multiplexMail(
|
||||||
PhabricatorMetaMTAMail $mail_template,
|
PhabricatorMetaMTAMail $mail_template,
|
||||||
array $to_handles,
|
array $to_handles,
|
||||||
|
@ -115,13 +131,12 @@ abstract class PhabricatorMailReplyHandler {
|
||||||
$body = $mail_template->getBody();
|
$body = $mail_template->getBody();
|
||||||
$body .= "\n";
|
$body .= "\n";
|
||||||
if ($to_handles) {
|
if ($to_handles) {
|
||||||
$body .= "To: ".implode(', ', mpull($to_handles, 'getName'))."\n";
|
|
||||||
$add_headers['X-Phabricator-To'] = $this->formatPHIDList($to_handles);
|
$add_headers['X-Phabricator-To'] = $this->formatPHIDList($to_handles);
|
||||||
}
|
}
|
||||||
if ($cc_handles) {
|
if ($cc_handles) {
|
||||||
$body .= "Cc: ".implode(', ', mpull($cc_handles, 'getName'))."\n";
|
|
||||||
$add_headers['X-Phabricator-Cc'] = $this->formatPHIDList($cc_handles);
|
$add_headers['X-Phabricator-Cc'] = $this->formatPHIDList($cc_handles);
|
||||||
}
|
}
|
||||||
|
$body .= $this->getRecipientsSummary($to_handles, $cc_handles);
|
||||||
|
|
||||||
foreach ($recipients as $recipient) {
|
foreach ($recipients as $recipient) {
|
||||||
$mail = clone $mail_template;
|
$mail = clone $mail_template;
|
||||||
|
|
|
@ -119,6 +119,28 @@ final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTranslation(array $objects) {
|
||||||
|
$default_translation = PhabricatorEnv::getEnvConfig('translation.provider');
|
||||||
|
$return = null;
|
||||||
|
$recipients = array_merge(
|
||||||
|
idx($this->parameters, 'to', array()),
|
||||||
|
idx($this->parameters, 'cc', array()));
|
||||||
|
foreach (array_select_keys($objects, $recipients) as $object) {
|
||||||
|
$translation = null;
|
||||||
|
if ($object instanceof PhabricatorUser) {
|
||||||
|
$translation = $object->getTranslation();
|
||||||
|
}
|
||||||
|
if (!$translation) {
|
||||||
|
$translation = $default_translation;
|
||||||
|
}
|
||||||
|
if ($return && $translation != $return) {
|
||||||
|
return $default_translation;
|
||||||
|
}
|
||||||
|
$return = $translation;
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
public function addHeader($name, $value) {
|
public function addHeader($name, $value) {
|
||||||
$this->parameters['headers'][$name] = $value;
|
$this->parameters['headers'][$name] = $value;
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
@ -96,6 +96,19 @@ final class PhabricatorUser extends PhabricatorUserDAO implements PhutilPerson {
|
||||||
return $this->sex;
|
return $this->sex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTranslation() {
|
||||||
|
try {
|
||||||
|
if ($this->translation &&
|
||||||
|
class_exists($this->translation) &&
|
||||||
|
is_subclass_of($this->translation, 'PhabricatorTranslation')) {
|
||||||
|
return $this->translation;
|
||||||
|
}
|
||||||
|
} catch (PhutilMissingSymbolException $ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public function isLoggedIn() {
|
public function isLoggedIn() {
|
||||||
return !($this->getPHID() === null);
|
return !($this->getPHID() === null);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue