Page MenuHomePhabricator

D13131.id31732.diff
No OneTemporary

D13131.id31732.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2024,6 +2024,7 @@
'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php',
'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php',
'PhabricatorMailSetupCheck' => 'applications/config/check/PhabricatorMailSetupCheck.php',
+ 'PhabricatorMailTarget' => 'applications/metamta/replyhandler/PhabricatorMailTarget.php',
'PhabricatorMailgunConfigOptions' => 'applications/config/option/PhabricatorMailgunConfigOptions.php',
'PhabricatorMailingListDatasource' => 'applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php',
'PhabricatorMailingListListPHIDType' => 'applications/mailinglists/phid/PhabricatorMailingListListPHIDType.php',
@@ -5444,6 +5445,7 @@
'PhabricatorMailManagementWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase',
'PhabricatorMailSetupCheck' => 'PhabricatorSetupCheck',
+ 'PhabricatorMailTarget' => 'Phobject',
'PhabricatorMailgunConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorMailingListDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorMailingListListPHIDType' => 'PhabricatorPHIDType',
diff --git a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php
--- a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php
+++ b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php
@@ -54,6 +54,8 @@
of each approach are:
- One mail to everyone:
+ - This violates policy controls. The body of the mail is generated without
+ respect for object policies.
- Recipients can see To/Cc at a glance.
- If you use mailing lists, you won't get duplicate mail if you're
a normal recipient and also Cc'd on a mailing list.
@@ -65,6 +67,7 @@
- Not supported with a private reply-to address.
- Mails are sent in the server default translation.
- One mail to each user:
+ - Policy controls work correctly and are enforced per-user.
- Recipients need to look in the mail body to see To/Cc.
- If you use mailing lists, recipients may sometimes get duplicate
mail.
@@ -74,8 +77,6 @@
- Required if private reply-to addresses are configured.
- Mails are sent in the language of user preference.
-In the code, splitting one outbound email into one-per-recipient is sometimes
-referred to as "multiplexing".
EODOC
));
@@ -222,6 +223,7 @@
'metamta.one-mail-per-recipient',
'bool',
true)
+ ->setLocked(true)
->setBoolOptions(
array(
pht('Send Mail To Each Recipient'),
diff --git a/src/applications/conpherence/mail/ConpherenceReplyHandler.php b/src/applications/conpherence/mail/ConpherenceReplyHandler.php
--- a/src/applications/conpherence/mail/ConpherenceReplyHandler.php
+++ b/src/applications/conpherence/mail/ConpherenceReplyHandler.php
@@ -21,9 +21,8 @@
}
}
- public function getPrivateReplyHandlerEmailAddress(
- PhabricatorObjectHandle $handle) {
- return $this->getDefaultPrivateReplyHandlerEmailAddress($handle, 'Z');
+ public function getPrivateReplyHandlerEmailAddress(PhabricatorUser $user) {
+ return $this->getDefaultPrivateReplyHandlerEmailAddress($user, 'Z');
}
public function getPublicReplyHandlerEmailAddress() {
diff --git a/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php b/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php
--- a/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php
+++ b/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php
@@ -47,7 +47,7 @@
abstract public function validateMailReceiver($mail_receiver);
abstract public function getPrivateReplyHandlerEmailAddress(
- PhabricatorObjectHandle $handle);
+ PhabricatorUser $user);
public function getReplyHandlerDomain() {
return PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain');
@@ -117,151 +117,6 @@
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 (PhabricatorEnv::getEnvConfig('metamta.recipients.show-hints')) {
- 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 getRecipientsSummaryHTML(
- array $to_handles,
- array $cc_handles) {
- assert_instances_of($to_handles, 'PhabricatorObjectHandle');
- assert_instances_of($cc_handles, 'PhabricatorObjectHandle');
-
- if (PhabricatorEnv::getEnvConfig('metamta.recipients.show-hints')) {
- $body = array();
- if ($to_handles) {
- $body[] = phutil_tag('strong', array(), 'To: ');
- $body[] = phutil_implode_html(', ', mpull($to_handles, 'getName'));
- $body[] = phutil_tag('br');
- }
- if ($cc_handles) {
- $body[] = phutil_tag('strong', array(), 'Cc: ');
- $body[] = phutil_implode_html(', ', mpull($cc_handles, 'getName'));
- $body[] = phutil_tag('br');
- }
- return phutil_tag('div', array(), $body);
- } else {
- return '';
- }
-
- }
-
- final public function multiplexMail(
- PhabricatorMetaMTAMail $mail_template,
- array $to_handles,
- array $cc_handles) {
- assert_instances_of($to_handles, 'PhabricatorObjectHandle');
- assert_instances_of($cc_handles, 'PhabricatorObjectHandle');
-
- $result = array();
-
- // If MetaMTA is configured to always multiplex, skip the single-email
- // case.
- if (!PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
- // If private replies are not supported, simply send one email to all
- // recipients and CCs. This covers cases where we have no reply handler,
- // or we have a public reply handler.
- if (!$this->supportsPrivateReplies()) {
- $mail = clone $mail_template;
- $mail->addTos(mpull($to_handles, 'getPHID'));
- $mail->addCCs(mpull($cc_handles, 'getPHID'));
-
- if ($this->supportsPublicReplies()) {
- $reply_to = $this->getPublicReplyHandlerEmailAddress();
- $mail->setReplyTo($reply_to);
- }
-
- $result[] = $mail;
-
- return $result;
- }
- }
-
- // TODO: This is pretty messy. We should really be doing all of this
- // multiplexing in the task queue, but that requires significant rewriting
- // in the general case. ApplicationTransactions can do it fairly easily,
- // but other mail sites currently can not, so we need to support this
- // junky version until they catch up and we can swap things over.
-
- $to_handles = $this->expandRecipientHandles($to_handles);
- $cc_handles = $this->expandRecipientHandles($cc_handles);
-
- $tos = mpull($to_handles, null, 'getPHID');
- $ccs = mpull($cc_handles, null, 'getPHID');
-
- // Merge all the recipients together. TODO: We could keep the CCs as real
- // CCs and send to a "noreply@domain.com" type address, but keep it simple
- // for now.
- $recipients = $tos + $ccs;
-
- // When multiplexing mail, explicitly include To/Cc information in the
- // message body and headers.
-
- $mail_template = clone $mail_template;
-
- $mail_template->addPHIDHeaders('X-Phabricator-To', array_keys($tos));
- $mail_template->addPHIDHeaders('X-Phabricator-Cc', array_keys($ccs));
-
- $body = $mail_template->getBody();
- $body .= "\n";
- $body .= $this->getRecipientsSummary($to_handles, $cc_handles);
-
- $html_body = $mail_template->getHTMLBody();
- if (strlen($html_body)) {
- $html_body .= hsprintf('%s',
- $this->getRecipientsSummaryHTML($to_handles, $cc_handles));
- }
-
- foreach ($recipients as $phid => $recipient) {
-
- $mail = clone $mail_template;
- if (isset($to_handles[$phid])) {
- $mail->addTos(array($phid));
- } else if (isset($cc_handles[$phid])) {
- $mail->addCCs(array($phid));
- } else {
- // not good - they should be a to or a cc
- continue;
- }
-
- $mail->setBody($body);
- $mail->setHTMLBody($html_body);
-
- $reply_to = null;
- if (!$reply_to && $this->supportsPrivateReplies()) {
- $reply_to = $this->getPrivateReplyHandlerEmailAddress($recipient);
- }
-
- if (!$reply_to && $this->supportsPublicReplies()) {
- $reply_to = $this->getPublicReplyHandlerEmailAddress();
- }
-
- if ($reply_to) {
- $mail->setReplyTo($reply_to);
- }
-
- $result[] = $mail;
- }
-
- return $result;
- }
-
protected function getDefaultPublicReplyHandlerEmailAddress($prefix) {
$receiver = $this->getMailReceiver();
@@ -288,31 +143,15 @@
}
protected function getDefaultPrivateReplyHandlerEmailAddress(
- PhabricatorObjectHandle $handle,
+ PhabricatorUser $user,
$prefix) {
- if ($handle->getType() != PhabricatorPeopleUserPHIDType::TYPECONST) {
- // You must be a real user to get a private reply handler address.
- return null;
- }
-
- $user = id(new PhabricatorPeopleQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withPHIDs(array($handle->getPHID()))
- ->executeOne();
-
- if (!$user) {
- // This may happen if a user was subscribed to something, and was then
- // deleted.
- return null;
- }
-
$receiver = $this->getMailReceiver();
$receiver_id = $receiver->getID();
$user_id = $user->getID();
$hash = PhabricatorObjectMailReceiver::computeMailHash(
$receiver->getMailKey(),
- $handle->getPHID());
+ $user->getPHID());
$domain = $this->getReplyHandlerDomain();
$address = "{$prefix}{$receiver_id}+{$user_id}+{$hash}@{$domain}";
@@ -368,21 +207,188 @@
return rtrim($output);
}
- private function expandRecipientHandles(array $handles) {
- if (!$handles) {
+
+ /**
+ * Produce a list of mail targets for a given to/cc list.
+ *
+ * Each target should be sent a separate email, and contains the information
+ * required to generate it with appropriate permissions and configuration.
+ *
+ * @param list<phid> List of "To" PHIDs.
+ * @param list<phid> List of "CC" PHIDs.
+ * @return list<PhabricatorMailTarget> List of targets.
+ */
+ final public function getMailTargets(array $raw_to, array $raw_cc) {
+ list($to, $cc) = $this->expandRecipientPHIDs($raw_to, $raw_cc);
+ list($to, $cc) = $this->loadRecipientUsers($to, $cc);
+ list($to, $cc) = $this->filterRecipientUsers($to, $cc);
+
+ if (!$to && !$cc) {
return array();
}
- $phids = mpull($handles, 'getPHID');
- $results = id(new PhabricatorMetaMTAMemberQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withPHIDs($phids)
- ->executeExpansion();
+ $template = id(new PhabricatorMailTarget())
+ ->setRawToPHIDs($raw_to)
+ ->setRawCCPHIDs($raw_cc);
- return id(new PhabricatorHandleQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withPHIDs($results)
- ->execute();
+ // Set the public reply address as the default, if one exists. We
+ // might replace this with a private address later.
+ if ($this->supportsPublicReplies()) {
+ $reply_to = $this->getPublicReplyHandlerEmailAddress();
+ if ($reply_to) {
+ $template->setReplyTo($reply_to);
+ }
+ }
+
+ $supports_private_replies = $this->supportsPrivateReplies();
+ $mail_all = !PhabricatorEnv::getEnvConfig('metamta.one-mail-per-recipient');
+ $targets = array();
+ if ($mail_all) {
+ $target = id(clone $template)
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->setToMap($to)
+ ->setCCMap($cc);
+
+ $targets[] = $target;
+ } else {
+ $map = $to + $cc;
+
+ foreach ($map as $phid => $user) {
+ $target = id(clone $template)
+ ->setViewer($user)
+ ->setToMap(array($phid => $user))
+ ->setCCMap(array());
+
+ if ($supports_private_replies) {
+ $reply_to = $this->getPrivateReplyHandlerEmailAddress($user);
+ if ($reply_to) {
+ $target->setReplyTo($reply_to);
+ }
+ }
+
+ $targets[] = $target;
+ }
+ }
+
+ return $targets;
+ }
+
+
+ /**
+ * Expand lists of recipient PHIDs.
+ *
+ * This takes any compound recipients (like projects) and looks up all their
+ * members.
+ *
+ * @param list<phid> List of To PHIDs.
+ * @param list<phid> List of CC PHIDs.
+ * @return pair<list<phid>, list<phid>> Expanded PHID lists.
+ */
+ private function expandRecipientPHIDs(array $to, array $cc) {
+ $to_result = array();
+ $cc_result = array();
+
+ $all_phids = array_merge($to, $cc);
+ if ($all_phids) {
+ $map = id(new PhabricatorMetaMTAMemberQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withPHIDs($all_phids)
+ ->execute();
+ foreach ($to as $phid) {
+ foreach ($map[$phid] as $expanded) {
+ $to_result[$expanded] = $expanded;
+ }
+ }
+ foreach ($cc as $phid) {
+ foreach ($map[$phid] as $expanded) {
+ $cc_result[$expanded] = $expanded;
+ }
+ }
+ }
+
+ // Remove recipients from "CC" if they're also present in "To".
+ $cc_result = array_diff_key($cc_result, $to_result);
+
+ return array(array_values($to_result), array_values($cc_result));
+ }
+
+
+ /**
+ * Load @{class:PhabricatorUser} objects for each recipient.
+ *
+ * Invalid recipients are dropped from the results.
+ *
+ * @param list<phid> List of To PHIDs.
+ * @param list<phid> List of CC PHIDs.
+ * @return pair<wild, wild> Maps from PHIDs to users.
+ */
+ private function loadRecipientUsers(array $to, array $cc) {
+ $to_result = array();
+ $cc_result = array();
+
+ $all_phids = array_merge($to, $cc);
+ if ($all_phids) {
+ $users = id(new PhabricatorPeopleQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withPHIDs($all_phids)
+ ->execute();
+ $users = mpull($users, null, 'getPHID');
+
+ foreach ($to as $phid) {
+ if (isset($users[$phid])) {
+ $to_result[$phid] = $users[$phid];
+ }
+ }
+ foreach ($cc as $phid) {
+ if (isset($users[$phid])) {
+ $cc_result[$phid] = $users[$phid];
+ }
+ }
+ }
+
+ return array($to_result, $cc_result);
+ }
+
+
+ /**
+ * Remove recipients who do not have permission to view the mail receiver.
+ *
+ * @param map<string, PhabricatorUser> Map of "To" users.
+ * @param map<string, PhabricatorUser> Map of "CC" users.
+ * @return pair<wild, wild> Filtered user maps.
+ */
+ private function filterRecipientUsers(array $to, array $cc) {
+ $to_result = array();
+ $cc_result = array();
+
+ $all_users = $to + $cc;
+ if ($all_users) {
+ $can_see = array();
+ $object = $this->getMailReceiver();
+ foreach ($all_users as $phid => $user) {
+ $visible = PhabricatorPolicyFilter::hasCapability(
+ $user,
+ $object,
+ PhabricatorPolicyCapability::CAN_VIEW);
+ if ($visible) {
+ $can_see[$phid] = true;
+ }
+ }
+
+ foreach ($to as $phid => $user) {
+ if (!empty($can_see[$phid])) {
+ $to_result[$phid] = $all_users[$phid];
+ }
+ }
+
+ foreach ($cc as $phid => $user) {
+ if (!empty($can_see[$phid])) {
+ $cc_result[$phid] = $all_users[$phid];
+ }
+ }
+ }
+
+ return array($to_result, $cc_result);
}
}
diff --git a/src/applications/metamta/replyhandler/PhabricatorMailTarget.php b/src/applications/metamta/replyhandler/PhabricatorMailTarget.php
new file mode 100644
--- /dev/null
+++ b/src/applications/metamta/replyhandler/PhabricatorMailTarget.php
@@ -0,0 +1,146 @@
+<?php
+
+final class PhabricatorMailTarget extends Phobject {
+
+ private $viewer;
+ private $replyTo;
+ private $toMap = array();
+ private $ccMap = array();
+ private $rawToPHIDs;
+ private $rawCCPHIDs;
+
+ public function setRawToPHIDs(array $to_phids) {
+ $this->rawToPHIDs = $to_phids;
+ return $this;
+ }
+
+ public function setRawCCPHIDs(array $cc_phids) {
+ $this->rawCCPHIDs = $cc_phids;
+ return $this;
+ }
+
+ public function setCCMap(array $cc_map) {
+ $this->ccMap = $cc_map;
+ return $this;
+ }
+
+ public function getCCMap() {
+ return $this->ccMap;
+ }
+
+ public function setToMap(array $to_map) {
+ $this->toMap = $to_map;
+ return $this;
+ }
+
+ public function getToMap() {
+ return $this->toMap;
+ }
+
+ public function setReplyTo($reply_to) {
+ $this->replyTo = $reply_to;
+ return $this;
+ }
+
+ public function getReplyTo() {
+ return $this->replyTo;
+ }
+
+ public function setViewer($viewer) {
+ $this->viewer = $viewer;
+ return $this;
+ }
+
+ public function getViewer() {
+ return $this->viewer;
+ }
+
+ public function sendMail(PhabricatorMetaMTAMail $mail) {
+ $viewer = $this->getViewer();
+
+ $mail->addPHIDHeaders('X-Phabricator-To', $this->rawToPHIDs);
+ $mail->addPHIDHeaders('X-Phabricator-Cc', $this->rawCCPHIDs);
+
+ $to_handles = $viewer->loadHandles($this->rawToPHIDs);
+ $cc_handles = $viewer->loadHandles($this->rawCCPHIDs);
+
+ $body = $mail->getBody();
+ $body .= "\n";
+ $body .= $this->getRecipientsSummary($to_handles, $cc_handles);
+ $mail->setBody($body);
+
+ $html_body = $mail->getHTMLBody();
+ if (strlen($html_body)) {
+ $html_body .= hsprintf(
+ '%s',
+ $this->getRecipientsSummaryHTML($to_handles, $cc_handles));
+ }
+ $mail->setHTMLBody($html_body);
+
+ $reply_to = $this->getReplyTo();
+ if ($reply_to) {
+ $mail->setReplyTo($reply_to);
+ }
+
+ $to = array_keys($this->getToMap());
+ if ($to) {
+ $mail->addTos($to);
+ }
+
+ $cc = array_keys($this->getCCMap());
+ if ($cc) {
+ $mail->addCCs($cc);
+ }
+
+ return $mail->save();
+ }
+
+ private function getRecipientsSummary(
+ PhabricatorHandleList $to_handles,
+ PhabricatorHandleList $cc_handles) {
+
+ if (!PhabricatorEnv::getEnvConfig('metamta.recipients.show-hints')) {
+ return '';
+ }
+
+ $to_handles = iterator_to_array($to_handles);
+ $cc_handles = iterator_to_array($cc_handles);
+
+ $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;
+ }
+
+ private function getRecipientsSummaryHTML(
+ PhabricatorHandleList $to_handles,
+ PhabricatorHandleList $cc_handles) {
+
+ if (!PhabricatorEnv::getEnvConfig('metamta.recipients.show-hints')) {
+ return '';
+ }
+
+ $to_handles = iterator_to_array($to_handles);
+ $cc_handles = iterator_to_array($cc_handles);
+
+ $body = array();
+ if ($to_handles) {
+ $body[] = phutil_tag('strong', array(), 'To: ');
+ $body[] = phutil_implode_html(', ', mpull($to_handles, 'getName'));
+ $body[] = phutil_tag('br');
+ }
+ if ($cc_handles) {
+ $body[] = phutil_tag('strong', array(), 'Cc: ');
+ $body[] = phutil_implode_html(', ', mpull($cc_handles, 'getName'));
+ $body[] = phutil_tag('br');
+ }
+ return phutil_tag('div', array(), $body);
+ }
+
+
+}
diff --git a/src/applications/owners/mail/OwnersPackageReplyHandler.php b/src/applications/owners/mail/OwnersPackageReplyHandler.php
--- a/src/applications/owners/mail/OwnersPackageReplyHandler.php
+++ b/src/applications/owners/mail/OwnersPackageReplyHandler.php
@@ -11,7 +11,7 @@
}
public function getPrivateReplyHandlerEmailAddress(
- PhabricatorObjectHandle $handle) {
+ PhabricatorUser $user) {
return null;
}
diff --git a/src/applications/repository/mail/PhabricatorRepositoryPushReplyHandler.php b/src/applications/repository/mail/PhabricatorRepositoryPushReplyHandler.php
--- a/src/applications/repository/mail/PhabricatorRepositoryPushReplyHandler.php
+++ b/src/applications/repository/mail/PhabricatorRepositoryPushReplyHandler.php
@@ -8,7 +8,7 @@
}
public function getPrivateReplyHandlerEmailAddress(
- PhabricatorObjectHandle $handle) {
+ PhabricatorUser $user) {
return null;
}
diff --git a/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php b/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php
--- a/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php
+++ b/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php
@@ -27,6 +27,23 @@
return;
}
+ $targets = id(new PhabricatorRepositoryPushReplyHandler())
+ ->setMailReceiver($repository)
+ ->getMailTargets($email_phids, array());
+ foreach ($targets as $target) {
+ $this->sendMail($target, $repository, $event);
+ }
+ }
+
+ private function sendMail(
+ PhabricatorMailTarget $target,
+ PhabricatorRepository $repository,
+ PhabricatorRepositoryPushEvent $event) {
+
+ $task_data = $this->getTaskData();
+ $viewer = $target->getViewer();
+ // TODO: Swap locale to viewer locale.
+
$logs = $event->getLogs();
list($ref_lines, $ref_list) = $this->renderRefs($logs);
@@ -103,20 +120,7 @@
->addHeader('Thread-Topic', $subject)
->setIsBulk(true);
- $to_handles = id(new PhabricatorHandleQuery())
- ->setViewer($viewer)
- ->withPHIDs($email_phids)
- ->execute();
-
- $reply_handler = new PhabricatorRepositoryPushReplyHandler();
- $mails = $reply_handler->multiplexMail(
- $mail,
- $to_handles,
- array());
-
- foreach ($mails as $mail) {
- $mail->saveAndSend();
- }
+ $target->sendMail($mail);
}
public function renderForDisplay(PhabricatorUser $viewer) {
diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
--- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
+++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
@@ -992,12 +992,10 @@
// Hook for other edges that may need (re-)loading
$object = $this->willPublish($object, $xactions);
- $this->loadHandles($xactions);
-
- $mail = null;
+ $mailed = array();
if (!$this->getDisableEmail()) {
if ($this->shouldSendMail($object, $xactions)) {
- $mail = $this->sendMail($object, $xactions);
+ $mailed = $this->sendMail($object, $xactions);
}
}
@@ -1009,10 +1007,6 @@
}
if ($this->shouldPublishFeedStory($object, $xactions)) {
- $mailed = array();
- if ($mail) {
- $mailed = $mail->buildRecipientList();
- }
$this->publishFeedStory(
$object,
$xactions,
@@ -2128,30 +2122,60 @@
}
if (!$any_visible) {
- return;
+ return array();
}
- $email_force = array();
$email_to = $this->mailToPHIDs;
$email_cc = $this->mailCCPHIDs;
-
$email_cc = array_merge($email_cc, $this->heraldEmailPHIDs);
- $email_force = $this->heraldForcedEmailPHIDs;
- $phids = array_merge($email_to, $email_cc);
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireActor())
- ->withPHIDs($phids)
- ->execute();
+ $targets = $this->buildReplyHandler($object)
+ ->getMailTargets($email_to, $email_cc);
+
+ // Set this explicitly before we start swapping out the effective actor.
+ $this->setActingAsPHID($this->getActingAsPHID());
+
+
+ $mailed = array();
+ foreach ($targets as $target) {
+ $original_actor = $this->getActor();
+ $this->setActor($target->getViewer());
+ // TODO: Swap locale to viewer locale.
+
+ $caught = null;
+ try {
+ // Reload handles for the new viewer.
+ $this->loadHandles($xactions);
+
+ $mail = $this->sendMailToTarget($object, $xactions, $target);
+ } catch (Exception $ex) {
+ $caught = $ex;
+ }
- $template = $this->buildMailTemplate($object);
+ $this->setActor($original_actor);
+ if ($caught) {
+ throw $ex;
+ }
+
+ foreach ($mail->buildRecipientList() as $phid) {
+ $mailed[$phid] = true;
+ }
+ }
+
+ return array_keys($mailed);
+ }
+
+ private function sendMailToTarget(
+ PhabricatorLiskDAO $object,
+ array $xactions,
+ PhabricatorMailTarget $target) {
+
+ $mail = $this->buildMailTemplate($object);
$body = $this->buildMailBody($object, $xactions);
$mail_tags = $this->getMailTags($object, $xactions);
$action = $this->getMailAction($object, $xactions);
- $reply_handler = $this->buildReplyHandler($object);
-
if (PhabricatorEnv::getEnvConfig('metamta.email-preferences')) {
$this->addEmailPreferenceSectionToMailBody(
$body,
@@ -2159,48 +2183,36 @@
$xactions);
}
- $template
+ $mail
->setFrom($this->getActingAsPHID())
->setSubjectPrefix($this->getMailSubjectPrefix())
->setVarySubjectPrefix('['.$action.']')
->setThreadID($this->getMailThreadID($object), $this->getIsNewObject())
->setRelatedPHID($object->getPHID())
->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs())
- ->setForceHeraldMailRecipientPHIDs($email_force)
+ ->setForceHeraldMailRecipientPHIDs($this->heraldForcedEmailPHIDs)
->setMailTags($mail_tags)
->setIsBulk(true)
->setBody($body->render())
->setHTMLBody($body->renderHTML());
foreach ($body->getAttachments() as $attachment) {
- $template->addAttachment($attachment);
+ $mail->addAttachment($attachment);
}
if ($this->heraldHeader) {
- $template->addHeader('X-Herald-Rules', $this->heraldHeader);
+ $mail->addHeader('X-Herald-Rules', $this->heraldHeader);
}
if ($object instanceof PhabricatorProjectInterface) {
- $this->addMailProjectMetadata($object, $template);
+ $this->addMailProjectMetadata($object, $mail);
}
if ($this->getParentMessageID()) {
- $template->setParentMessageID($this->getParentMessageID());
+ $mail->setParentMessageID($this->getParentMessageID());
}
- $mails = $reply_handler->multiplexMail(
- $template,
- array_select_keys($handles, $email_to),
- array_select_keys($handles, $email_cc));
-
- foreach ($mails as $mail) {
- $mail->saveAndSend();
- }
-
- $template->addTos($email_to);
- $template->addCCs($email_cc);
-
- return $template;
+ return $target->sendMail($mail);
}
private function addMailProjectMetadata(
diff --git a/src/applications/transactions/replyhandler/PhabricatorApplicationTransactionReplyHandler.php b/src/applications/transactions/replyhandler/PhabricatorApplicationTransactionReplyHandler.php
--- a/src/applications/transactions/replyhandler/PhabricatorApplicationTransactionReplyHandler.php
+++ b/src/applications/transactions/replyhandler/PhabricatorApplicationTransactionReplyHandler.php
@@ -6,9 +6,9 @@
abstract public function getObjectPrefix();
public function getPrivateReplyHandlerEmailAddress(
- PhabricatorObjectHandle $handle) {
+ PhabricatorUser $user) {
return $this->getDefaultPrivateReplyHandlerEmailAddress(
- $handle,
+ $user,
$this->getObjectPrefix());
}

File Metadata

Mime Type
text/plain
Expires
Sat, Mar 8, 4:40 AM (1 d, 22 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7371423
Default Alt Text
D13131.id31732.diff (27 KB)

Event Timeline