diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php index 33d7d84fa3..ad0055f3aa 100644 --- a/src/applications/base/controller/PhabricatorController.php +++ b/src/applications/base/controller/PhabricatorController.php @@ -62,9 +62,7 @@ abstract class PhabricatorController extends AphrontController { $translation = $user->getTranslation(); if ($translation && - $translation != PhabricatorEnv::getEnvConfig('translation.provider') && - class_exists($translation) && - is_subclass_of($translation, 'PhabricatorTranslation')) { + $translation != PhabricatorEnv::getEnvConfig('translation.provider')) { $translation = newv($translation, array()); PhutilTranslator::getInstance() ->setLanguage($translation->getLanguage()) diff --git a/src/applications/differential/mail/DifferentialCommentMail.php b/src/applications/differential/mail/DifferentialCommentMail.php index b8d32b93ab..412dcd8b63 100644 --- a/src/applications/differential/mail/DifferentialCommentMail.php +++ b/src/applications/differential/mail/DifferentialCommentMail.php @@ -20,6 +20,9 @@ final class DifferentialCommentMail extends DifferentialMail { protected $changedByCommit; + private $addedReviewers; + private $addedCCs; + public function setChangedByCommit($changed_by_commit) { $this->changedByCommit = $changed_by_commit; return $this; @@ -87,20 +90,11 @@ final class DifferentialCommentMail extends DifferentialMail { return $verb; } - 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}\"."; + protected function prepareBody() { + parent::prepareBody(); // If the commented added reviewers or CCs, list them explicitly. - $meta = $comment->getMetadata(); + $meta = $this->getComment()->getMetadata(); $m_reviewers = idx( $meta, DifferentialComment::METADATA_ADDED_REVIEWERS, @@ -113,16 +107,32 @@ final class DifferentialCommentMail extends DifferentialMail { if ($load) { $handles = id(new PhabricatorObjectHandleData($load))->loadHandles(); if ($m_reviewers) { - $body[] = 'Added Reviewers: '.$this->renderHandleList( - $handles, - $m_reviewers); + $this->addedReviewers = $this->renderHandleList($handles, $m_reviewers); } if ($m_cc) { - $body[] = 'Added CCs: '.$this->renderHandleList( - $handles, - $m_cc); + $this->addedCCs = $this->renderHandleList($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; diff --git a/src/applications/differential/mail/DifferentialMail.php b/src/applications/differential/mail/DifferentialMail.php index 6b5a1e6d10..7cebc79069 100644 --- a/src/applications/differential/mail/DifferentialMail.php +++ b/src/applications/differential/mail/DifferentialMail.php @@ -78,7 +78,6 @@ abstract class DifferentialMail { } $cc_phids = $this->getCCPHIDs(); - $body = $this->buildBody(); $attachments = $this->buildAttachments(); $template = new PhabricatorMetaMTAMail(); @@ -90,10 +89,6 @@ abstract class DifferentialMail { } $template - ->setSubject($this->renderSubject()) - ->setSubjectPrefix($this->getSubjectPrefix()) - ->setVarySubjectPrefix($this->renderVaryPrefix()) - ->setBody($body) ->setIsHTML($this->shouldMarkMailAsHTML()) ->setParentMessageID($this->parentMessageID) ->addHeader('Thread-Topic', $this->getThreadTopic()); @@ -172,25 +167,62 @@ abstract class DifferentialMail { $phids = array_keys($phids); $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); + $objects = id(new PhabricatorObjectHandleData($phids))->loadObjects(); - $event = new PhabricatorEvent( - PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL, - array( - 'mail' => $template, - ) - ); - PhutilEventEngine::dispatchEvent($event); + $to_handles = array_select_keys($handles, $to_phids); + $cc_handles = array_select_keys($handles, $cc_phids); - $template = $event->getValue('mail'); + $this->prepareBody(); - $mails = $reply_handler->multiplexMail( - $template, - array_select_keys($handles, $to_phids), - array_select_keys($handles, $cc_phids)); + $mails = $reply_handler->multiplexMail($template, $to_handles, $cc_handles); - foreach ($mails as $mail) { - $mail->saveAndSend(); + $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); } protected function getMailTags() { @@ -205,6 +237,17 @@ abstract class DifferentialMail { 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() { $body = $this->renderBody(); @@ -369,6 +412,8 @@ EOTEXT; $body = array(); foreach ($aux_fields as $field) { $field->setRevision($this->getRevision()); + // TODO: Introduce and use getRequiredHandlePHIDsForMail() and load all + // handles in prepareBody(). $text = $field->renderValueForMail($phase); if ($text !== null) { $body[] = $text; diff --git a/src/applications/differential/mail/DifferentialReviewRequestMail.php b/src/applications/differential/mail/DifferentialReviewRequestMail.php index 94cf686cbf..446579468f 100644 --- a/src/applications/differential/mail/DifferentialReviewRequestMail.php +++ b/src/applications/differential/mail/DifferentialReviewRequestMail.php @@ -20,6 +20,8 @@ abstract class DifferentialReviewRequestMail extends DifferentialMail { protected $comments; + private $patch; + public function setComments($comments) { $this->comments = $comments; return $this; @@ -40,6 +42,19 @@ abstract class DifferentialReviewRequestMail extends DifferentialMail { $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(); @@ -65,14 +80,9 @@ abstract class DifferentialReviewRequestMail extends DifferentialMail { $body[] = null; } - $inline_key = 'metamta.differential.inline-patches'; - $inline_max_length = PhabricatorEnv::getEnvConfig($inline_key); - if ($inline_max_length) { - $patch = $this->buildPatch(); - if (count(explode("\n", $patch)) <= $inline_max_length) { - $body[] = 'CHANGE DETAILS'; - $body[] = $patch; - } + if ($this->patch) { + $body[] = 'CHANGE DETAILS'; + $body[] = $this->patch; } return implode("\n", $body); @@ -110,14 +120,9 @@ abstract class DifferentialReviewRequestMail extends DifferentialMail { } private function buildPatch() { - $revision = $this->getRevision(); - $revision_id = $revision->getID(); + $diff = new DifferentialDiff(); - $diffs = $revision->loadDiffs(); - $diff_number = count($diffs); - $diff = array_pop($diffs); - - $diff->attachChangesets($diff->loadChangesets()); + $diff->attachChangesets($this->getChangesets()); // TODO: We could batch this to improve performance. foreach ($diff->getChangesets() as $changeset) { $changeset->attachHunks($changeset->loadHunks()); diff --git a/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php b/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php index 689b37afd8..00bfea095e 100644 --- a/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php +++ b/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php @@ -71,6 +71,22 @@ abstract class PhabricatorMailReplyHandler { 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( PhabricatorMetaMTAMail $mail_template, array $to_handles, @@ -115,13 +131,12 @@ abstract class PhabricatorMailReplyHandler { $body = $mail_template->getBody(); $body .= "\n"; if ($to_handles) { - $body .= "To: ".implode(', ', mpull($to_handles, 'getName'))."\n"; $add_headers['X-Phabricator-To'] = $this->formatPHIDList($to_handles); } if ($cc_handles) { - $body .= "Cc: ".implode(', ', mpull($cc_handles, 'getName'))."\n"; $add_headers['X-Phabricator-Cc'] = $this->formatPHIDList($cc_handles); } + $body .= $this->getRecipientsSummary($to_handles, $cc_handles); foreach ($recipients as $recipient) { $mail = clone $mail_template; diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php index f6ccdbe25d..e189296885 100644 --- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php @@ -119,6 +119,28 @@ final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO { 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) { $this->parameters['headers'][$name] = $value; return $this; diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 56482894a6..8786196b89 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -96,6 +96,19 @@ final class PhabricatorUser extends PhabricatorUserDAO implements PhutilPerson { 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() { return !($this->getPHID() === null); }