2011-01-26 02:40:21 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* See #394445 for an explanation of why this thing even exists.
|
2012-10-23 01:25:43 +02:00
|
|
|
*
|
|
|
|
* @task recipients Managing Recipients
|
2011-01-26 02:40:21 +01:00
|
|
|
*/
|
2012-03-14 00:21:04 +01:00
|
|
|
final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
|
2011-01-26 02:40:21 +01:00
|
|
|
|
|
|
|
const STATUS_QUEUE = 'queued';
|
|
|
|
const STATUS_SENT = 'sent';
|
|
|
|
const STATUS_FAIL = 'fail';
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
const STATUS_VOID = 'void';
|
2011-01-26 02:40:21 +01:00
|
|
|
|
|
|
|
const MAX_RETRIES = 250;
|
|
|
|
const RETRY_DELAY = 5;
|
|
|
|
|
|
|
|
protected $parameters;
|
|
|
|
protected $status;
|
|
|
|
protected $message;
|
|
|
|
protected $retryCount;
|
|
|
|
protected $nextRetry;
|
|
|
|
protected $relatedPHID;
|
|
|
|
|
2012-10-10 19:18:23 +02:00
|
|
|
private $excludePHIDs = array();
|
|
|
|
|
2011-01-26 02:40:21 +01:00
|
|
|
public function __construct() {
|
|
|
|
|
|
|
|
$this->status = self::STATUS_QUEUE;
|
|
|
|
$this->retryCount = 0;
|
|
|
|
$this->nextRetry = time();
|
|
|
|
$this->parameters = array();
|
|
|
|
|
|
|
|
parent::__construct();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getConfiguration() {
|
|
|
|
return array(
|
|
|
|
self::CONFIG_SERIALIZATION => array(
|
|
|
|
'parameters' => self::SERIALIZATION_JSON,
|
|
|
|
),
|
|
|
|
) + parent::getConfiguration();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function setParam($param, $value) {
|
|
|
|
$this->parameters[$param] = $value;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-10-23 01:25:43 +02:00
|
|
|
protected function getParam($param, $default = null) {
|
|
|
|
return idx($this->parameters, $param, $default);
|
2011-01-26 02:40:21 +01:00
|
|
|
}
|
|
|
|
|
Add email preferences to receive fewer less-important notifications
Summary:
A few similar requests have come in across several tools and use cases that I
think this does a reasonable job of resolving.
We currently send one email for each update an object receives, but these aren't
always appreciated:
- Asana does post-commit review via Differential, so the "committed" mails are
useless.
- Quora wants to make project category edits to bugs without spamming people
attached to them.
- Some users in general are very sensitive to email volumes, and this gives us
a good way to reduce the volumes without incurring the complexity of
delayed-send-batching.
The technical mechanism is basically:
- Mail may optionally have "mail tags", which indicate content in the mail
(e.g., "maniphest-priority, maniphest-cc, maniphest-comment" for a mail which
contains a priority change, a CC change, and a comment).
- If a mail has tags, remove any recipients who have opted out of all the
tags.
- Some tags can't be opted out of via the UI, so this ensures that important
email is still delivered (e.g., cc + assign + comment is always delivered
because you can't opt out of "assign" or "comment").
Test Plan:
- Disabled all mail tags in the web UI.
- Used test console to send myself mail with an opt-outable tag, it was
immediately dropped.
- Used test console to send myself mail with an opt-outable tag and a custom
tag, it was delivered.
- Made Differential updates affecting CCs with and without comments, got
appropriate delivery.
- Made Maniphest updates affecting project, priority and CCs with and without
comments, got appropriate delivery.
- Verified mail headers in all cases.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley, moskov
Maniphest Tasks: T616, T855
Differential Revision: https://secure.phabricator.com/D1635
2012-02-18 07:57:07 +01:00
|
|
|
/**
|
|
|
|
* Set tags (@{class:MetaMTANotificationType} constants) which identify the
|
|
|
|
* content of this mail in a general way. These tags are used to allow users
|
|
|
|
* to opt out of receiving certain types of mail, like updates when a task's
|
|
|
|
* projects change.
|
|
|
|
*
|
|
|
|
* @param list<const> List of @{class:MetaMTANotificationType} constants.
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
public function setMailTags(array $tags) {
|
|
|
|
$this->setParam('mailtags', $tags);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-06-22 21:41:19 +02:00
|
|
|
/**
|
|
|
|
* In Gmail, conversations will be broken if you reply to a thread and the
|
|
|
|
* server sends back a response without referencing your Message-ID, even if
|
|
|
|
* it references a Message-ID earlier in the thread. To avoid this, use the
|
|
|
|
* parent email's message ID explicitly if it's available. This overwrites the
|
|
|
|
* "In-Reply-To" and "References" headers we would otherwise generate. This
|
|
|
|
* needs to be set whenever an action is triggered by an email message. See
|
|
|
|
* T251 for more details.
|
|
|
|
*
|
|
|
|
* @param string The "Message-ID" of the email which precedes this one.
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
public function setParentMessageID($id) {
|
|
|
|
$this->setParam('parent-message-id', $id);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getParentMessageID() {
|
|
|
|
return $this->getParam('parent-message-id');
|
|
|
|
}
|
|
|
|
|
2011-01-26 02:40:21 +01:00
|
|
|
public function getSubject() {
|
|
|
|
return $this->getParam('subject');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function addTos(array $phids) {
|
Better enforce uniqueness for email delivery
Summary:
@skrul reports receiving multiple copies of notification emails since
@hunterbridges configured some bizarre dystopian email replication factory on
their outbound route. Two fixes:
- Ensure "To" and "Cc" are unique. Email shouldn't be replicated for "To:
x@y.com, x@y.com" but it's silly that we do this.
- Remove "To" addresses from "Cc". Email shouldn't be replicated here either,
but we don't really lose anything by accommodating this.
Test Plan:
Sent a mail to the same to/cc, verified I was to'd only and not cc'd when the
mail was delivered.
@hunterbridges, can you apply this patch locally and verify it fixes the issue?
You can test by going to MetaMTA -> Send New Message and sending a message to
yourself as both To and CC.
Reviewed By: skrul
Reviewers: skrul, hunterbridges, jungejason, tuomaspelkonen, aran
CC: aran, skrul, epriestley
Differential Revision: 751
2011-07-31 20:54:50 +02:00
|
|
|
$phids = array_unique($phids);
|
2011-01-26 02:40:21 +01:00
|
|
|
$this->setParam('to', $phids);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-05-07 19:29:33 +02:00
|
|
|
public function addRawTos(array $raw_email) {
|
|
|
|
$this->setParam('raw-to', $raw_email);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-01-26 02:40:21 +01:00
|
|
|
public function addCCs(array $phids) {
|
Better enforce uniqueness for email delivery
Summary:
@skrul reports receiving multiple copies of notification emails since
@hunterbridges configured some bizarre dystopian email replication factory on
their outbound route. Two fixes:
- Ensure "To" and "Cc" are unique. Email shouldn't be replicated for "To:
x@y.com, x@y.com" but it's silly that we do this.
- Remove "To" addresses from "Cc". Email shouldn't be replicated here either,
but we don't really lose anything by accommodating this.
Test Plan:
Sent a mail to the same to/cc, verified I was to'd only and not cc'd when the
mail was delivered.
@hunterbridges, can you apply this patch locally and verify it fixes the issue?
You can test by going to MetaMTA -> Send New Message and sending a message to
yourself as both To and CC.
Reviewed By: skrul
Reviewers: skrul, hunterbridges, jungejason, tuomaspelkonen, aran
CC: aran, skrul, epriestley
Differential Revision: 751
2011-07-31 20:54:50 +02:00
|
|
|
$phids = array_unique($phids);
|
2011-01-26 02:40:21 +01:00
|
|
|
$this->setParam('cc', $phids);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-10-10 19:18:23 +02:00
|
|
|
public function setExcludeMailRecipientPHIDs($exclude) {
|
|
|
|
$this->excludePHIDs = $exclude;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
private function getExcludeMailRecipientPHIDs() {
|
|
|
|
return $this->excludePHIDs;
|
|
|
|
}
|
|
|
|
|
2012-06-16 08:21:25 +02:00
|
|
|
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;
|
|
|
|
}
|
2012-07-02 16:17:56 +02:00
|
|
|
|
|
|
|
if (!$return) {
|
|
|
|
$return = $default_translation;
|
|
|
|
}
|
|
|
|
|
2012-06-16 08:21:25 +02:00
|
|
|
return $return;
|
|
|
|
}
|
|
|
|
|
2012-06-22 03:39:35 +02:00
|
|
|
public function addPHIDHeaders($name, array $phids) {
|
|
|
|
foreach ($phids as $phid) {
|
|
|
|
$this->addHeader($name, '<'.$phid.'>');
|
|
|
|
}
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-01-26 02:40:21 +01:00
|
|
|
public function addHeader($name, $value) {
|
2012-06-22 03:39:35 +02:00
|
|
|
$this->parameters['headers'][] = array($name, $value);
|
2011-01-26 02:40:21 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-11-09 00:15:44 +01:00
|
|
|
public function addAttachment(PhabricatorMetaMTAAttachment $attachment) {
|
|
|
|
$this->parameters['attachments'][] = $attachment;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getAttachments() {
|
|
|
|
return $this->getParam('attachments');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setAttachments(array $attachments) {
|
2012-04-03 21:10:45 +02:00
|
|
|
assert_instances_of($attachments, 'PhabricatorMetaMTAAttachment');
|
2011-11-09 00:15:44 +01:00
|
|
|
$this->setParam('attachments', $attachments);
|
2011-10-14 21:07:29 +02:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-01-26 02:40:21 +01:00
|
|
|
public function setFrom($from) {
|
|
|
|
$this->setParam('from', $from);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-01-26 18:08:26 +01:00
|
|
|
public function setReplyTo($reply_to) {
|
|
|
|
$this->setParam('reply-to', $reply_to);
|
2011-01-26 02:40:21 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setSubject($subject) {
|
|
|
|
$this->setParam('subject', $subject);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-06-11 21:21:37 +02:00
|
|
|
public function setSubjectPrefix($prefix) {
|
|
|
|
$this->setParam('subject-prefix', $prefix);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setVarySubjectPrefix($prefix) {
|
|
|
|
$this->setParam('vary-subject-prefix', $prefix);
|
Fix various threading issues, particularly in Gmail
Summary:
- Add an explicit multiplexing option, and enable it by default. This is necessary for Mail.app to coexist with other clients ("Re:" breaks outlook at the very least, and generally sucks in the common case), and allows users with flexible clients to enable subject variance.
- Add an option for subject line variance. Default to not varying the subject, so mail no longer says [Committed], [Closed], etc. This is so the defaults thread correctly in Gmail (not entirely sure this actually works).
- Add a preference to enable subject line variance.
- Unless all mail is multiplexed, don't enable or respect the "Re" or "vary subject" preferences. These are currently shown and respected in non-multiplex cases, which creates inconsistent results.
NOTE: @jungejason @nh @vrana This changes the default behavior (from non-multiplexing to multiplexing), and might break Facebook's integration. You should be able to keep the same behavior by setting the options appropriately, although if you can get the new defaults working they're probably better.
Test Plan:
Send mail from Maniphest, Differential and Audit. Updated preferences. Enabled/disabled multiplexing. Things seem OK?
NOTE: I haven't actually been able to repro the Gmail threading issue so I'm not totally sure what's going on there, maybe it started respecting "Re:" (or always has), but @cpiro and @20after4 both reported it independently. This fixes a bunch of bugs in any case and gives us more conservative set of defaults.
I'll see if I can buff out the Gmail story a bit but every client is basically a giant black box of mystery. :/
Reviewers: btrahan, vrana, jungejason, nh
Reviewed By: btrahan
CC: cpiro, 20after4, aran
Maniphest Tasks: T1097, T847
Differential Revision: https://secure.phabricator.com/D2206
2012-04-12 18:31:03 +02:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-01-26 02:40:21 +01:00
|
|
|
public function setBody($body) {
|
|
|
|
$this->setParam('body', $body);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-05-12 05:32:30 +02:00
|
|
|
public function getBody() {
|
|
|
|
return $this->getParam('body');
|
|
|
|
}
|
|
|
|
|
2011-01-26 02:40:21 +01:00
|
|
|
public function setIsHTML($html) {
|
|
|
|
$this->setParam('is-html', $html);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getSimulatedFailureCount() {
|
|
|
|
return nonempty($this->getParam('simulated-failures'), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setSimulatedFailureCount($count) {
|
|
|
|
$this->setParam('simulated-failures', $count);
|
|
|
|
return $this;
|
|
|
|
}
|
2011-02-02 00:52:04 +01:00
|
|
|
|
2012-02-28 02:11:25 +01:00
|
|
|
public function getWorkerTaskID() {
|
|
|
|
return $this->getParam('worker-task');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setWorkerTaskID($id) {
|
|
|
|
$this->setParam('worker-task', $id);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-10-23 21:07:37 +02:00
|
|
|
/**
|
|
|
|
* Flag that this is an auto-generated bulk message and should have bulk
|
|
|
|
* headers added to it if appropriate. Broadly, this means some flavor of
|
|
|
|
* "Precedence: bulk" or similar, but is implementation and configuration
|
|
|
|
* dependent.
|
|
|
|
*
|
|
|
|
* @param bool True if the mail is automated bulk mail.
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
public function setIsBulk($is_bulk) {
|
|
|
|
$this->setParam('is-bulk', $is_bulk);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
Fix a threading issue with Amazon SES
Summary:
Amazon SES does not allow us to set a Message-ID header, which means
that threads are incorrect in Mail.app (and presumably other applications
which respect In-Reply-To and References) because the initial email does not
have anything which attaches it to the rest of the thread. To fix this, never
rely on Message-ID if the mailer doesn't support Message-ID.
(In the Amazon SES case, Amazon generates its own Message-ID which we can't
know ahead of time).
I additionally used all the Lisk isolation from the other tests to make this
testable and wrote tests for it.
I also moved the idea of a thread ID lower in the stack and out of
DifferentialMail, which should not be responsible for implementation details.
NOTE: If you push this, it will cause a one-time break of threading for
everyone using Outlook since I've changed the seed for generating Thread-Index.
I feel like this is okay to avoid introducing more complexity here.
Test Plan:
Created and then updated a revision, messages delivered over Amazon
SES threaded correctly in Mail.app. Verified headers. Unit tests.
Reviewed By: rm
Reviewers: aran, tuomaspelkonen, jungejason, rm
Commenters: aran
CC: aran, rm, epriestley
Differential Revision: 195
2011-04-30 20:47:00 +02:00
|
|
|
/**
|
|
|
|
* Use this method to set an ID used for message threading. MetaMTA will
|
|
|
|
* set appropriate headers (Message-ID, In-Reply-To, References and
|
|
|
|
* Thread-Index) based on the capabilities of the underlying mailer.
|
|
|
|
*
|
|
|
|
* @param string Unique identifier, appropriate for use in a Message-ID,
|
|
|
|
* In-Reply-To or References headers.
|
|
|
|
* @param bool If true, indicates this is the first message in the thread.
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
public function setThreadID($thread_id, $is_first_message = false) {
|
|
|
|
$this->setParam('thread-id', $thread_id);
|
|
|
|
$this->setParam('is-first-message', $is_first_message);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-05-01 07:51:25 +02:00
|
|
|
/**
|
|
|
|
* Save a newly created mail to the database and attempt to send it
|
|
|
|
* immediately if the server is configured for immediate sends. When
|
|
|
|
* applications generate new mail they should generally use this method to
|
|
|
|
* deliver it. If the server doesn't use immediate sends, this has the same
|
|
|
|
* effect as calling save(): the mail will eventually be delivered by the
|
|
|
|
* MetaMTA daemon.
|
|
|
|
*
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
public function saveAndSend() {
|
2011-10-19 21:54:49 +02:00
|
|
|
$ret = null;
|
2011-02-02 00:52:04 +01:00
|
|
|
|
2011-05-01 07:51:25 +02:00
|
|
|
if (PhabricatorEnv::getEnvConfig('metamta.send-immediately')) {
|
2011-10-19 21:54:49 +02:00
|
|
|
$ret = $this->sendNow();
|
|
|
|
} else {
|
|
|
|
$ret = $this->save();
|
2011-02-01 06:13:38 +01:00
|
|
|
}
|
2011-02-02 00:52:04 +01:00
|
|
|
|
2011-02-01 06:13:38 +01:00
|
|
|
return $ret;
|
|
|
|
}
|
2011-02-02 00:52:04 +01:00
|
|
|
|
2012-02-28 02:11:25 +01:00
|
|
|
protected function didWriteData() {
|
|
|
|
parent::didWriteData();
|
|
|
|
|
|
|
|
if (!$this->getWorkerTaskID()) {
|
2012-10-31 23:22:16 +01:00
|
|
|
$mailer_task = PhabricatorWorker::scheduleTask(
|
|
|
|
'PhabricatorMetaMTAWorker',
|
|
|
|
$this->getID());
|
|
|
|
|
2012-02-28 02:11:25 +01:00
|
|
|
$this->setWorkerTaskID($mailer_task->getID());
|
|
|
|
$this->save();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-01 07:51:25 +02:00
|
|
|
|
Fix a threading issue with Amazon SES
Summary:
Amazon SES does not allow us to set a Message-ID header, which means
that threads are incorrect in Mail.app (and presumably other applications
which respect In-Reply-To and References) because the initial email does not
have anything which attaches it to the rest of the thread. To fix this, never
rely on Message-ID if the mailer doesn't support Message-ID.
(In the Amazon SES case, Amazon generates its own Message-ID which we can't
know ahead of time).
I additionally used all the Lisk isolation from the other tests to make this
testable and wrote tests for it.
I also moved the idea of a thread ID lower in the stack and out of
DifferentialMail, which should not be responsible for implementation details.
NOTE: If you push this, it will cause a one-time break of threading for
everyone using Outlook since I've changed the seed for generating Thread-Index.
I feel like this is okay to avoid introducing more complexity here.
Test Plan:
Created and then updated a revision, messages delivered over Amazon
SES threaded correctly in Mail.app. Verified headers. Unit tests.
Reviewed By: rm
Reviewers: aran, tuomaspelkonen, jungejason, rm
Commenters: aran
CC: aran, rm, epriestley
Differential Revision: 195
2011-04-30 20:47:00 +02:00
|
|
|
public function buildDefaultMailer() {
|
2012-03-21 22:48:58 +01:00
|
|
|
return PhabricatorEnv::newObjectFromConfig('metamta.mail-adapter');
|
2011-02-10 02:39:55 +01:00
|
|
|
}
|
2011-01-26 02:40:21 +01:00
|
|
|
|
2012-10-23 01:25:43 +02:00
|
|
|
|
Fix a threading issue with Amazon SES
Summary:
Amazon SES does not allow us to set a Message-ID header, which means
that threads are incorrect in Mail.app (and presumably other applications
which respect In-Reply-To and References) because the initial email does not
have anything which attaches it to the rest of the thread. To fix this, never
rely on Message-ID if the mailer doesn't support Message-ID.
(In the Amazon SES case, Amazon generates its own Message-ID which we can't
know ahead of time).
I additionally used all the Lisk isolation from the other tests to make this
testable and wrote tests for it.
I also moved the idea of a thread ID lower in the stack and out of
DifferentialMail, which should not be responsible for implementation details.
NOTE: If you push this, it will cause a one-time break of threading for
everyone using Outlook since I've changed the seed for generating Thread-Index.
I feel like this is okay to avoid introducing more complexity here.
Test Plan:
Created and then updated a revision, messages delivered over Amazon
SES threaded correctly in Mail.app. Verified headers. Unit tests.
Reviewed By: rm
Reviewers: aran, tuomaspelkonen, jungejason, rm
Commenters: aran
CC: aran, rm, epriestley
Differential Revision: 195
2011-04-30 20:47:00 +02:00
|
|
|
/**
|
|
|
|
* Attempt to deliver an email immediately, in this process.
|
|
|
|
*
|
|
|
|
* @param bool Try to deliver this email even if it has already been
|
|
|
|
* delivered or is in backoff after a failed delivery attempt.
|
|
|
|
* @param PhabricatorMailImplementationAdapter Use a specific mail adapter,
|
|
|
|
* instead of the default.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
2011-01-26 18:08:26 +01:00
|
|
|
public function sendNow(
|
|
|
|
$force_send = false,
|
2011-02-10 02:39:55 +01:00
|
|
|
PhabricatorMailImplementationAdapter $mailer = null) {
|
|
|
|
|
|
|
|
if ($mailer === null) {
|
|
|
|
$mailer = $this->buildDefaultMailer();
|
|
|
|
}
|
2011-01-26 02:40:21 +01:00
|
|
|
|
|
|
|
if (!$force_send) {
|
|
|
|
if ($this->getStatus() != self::STATUS_QUEUE) {
|
|
|
|
throw new Exception("Trying to send an already-sent mail!");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (time() < $this->getNextRetry()) {
|
|
|
|
throw new Exception("Trying to send an email before next retry!");
|
|
|
|
}
|
|
|
|
}
|
2011-02-02 00:52:04 +01:00
|
|
|
|
2011-01-26 02:40:21 +01:00
|
|
|
try {
|
2012-10-23 19:48:03 +02:00
|
|
|
$params = $this->parameters;
|
2011-01-26 18:33:31 +01:00
|
|
|
$phids = array();
|
2012-10-23 01:25:43 +02:00
|
|
|
|
2012-10-23 19:48:03 +02:00
|
|
|
foreach ($params as $key => $value) {
|
2012-10-23 01:25:43 +02:00
|
|
|
switch ($key) {
|
|
|
|
case 'to':
|
2012-10-23 19:48:03 +02:00
|
|
|
$params[$key] = $this->buildToList();
|
2012-10-23 01:25:43 +02:00
|
|
|
break;
|
|
|
|
case 'cc':
|
2012-10-23 19:48:03 +02:00
|
|
|
$params[$key] = $this->buildCCList();
|
2012-10-23 01:25:43 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-23 19:48:03 +02:00
|
|
|
foreach ($params as $key => $value) {
|
2011-01-26 18:33:31 +01:00
|
|
|
switch ($key) {
|
|
|
|
case 'from':
|
2012-10-23 01:25:43 +02:00
|
|
|
$value = array($value);
|
|
|
|
/* fallthrough */
|
2011-01-26 18:33:31 +01:00
|
|
|
case 'to':
|
|
|
|
case 'cc':
|
2012-10-23 01:25:43 +02:00
|
|
|
foreach ($value as $phid) {
|
2012-05-18 06:46:45 +02:00
|
|
|
$type = phid_get_type($phid);
|
|
|
|
$phids[$phid] = $type;
|
2011-01-26 18:33:31 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-02-02 00:52:04 +01:00
|
|
|
|
2012-05-18 06:46:45 +02:00
|
|
|
$this->loadEmailAndNameDataFromPHIDs($phids);
|
2011-02-10 02:39:55 +01:00
|
|
|
|
2011-02-09 20:11:24 +01:00
|
|
|
$default = PhabricatorEnv::getEnvConfig('metamta.default-address');
|
|
|
|
if (empty($params['from'])) {
|
|
|
|
$mailer->setFrom($default);
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
} else {
|
2011-02-09 20:11:24 +01:00
|
|
|
$from = $params['from'];
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
|
|
|
|
if (!PhabricatorEnv::getEnvConfig('metamta.can-send-as-user')) {
|
|
|
|
if (empty($params['reply-to'])) {
|
2012-05-18 06:46:45 +02:00
|
|
|
$params['reply-to'] = $phids[$from]['email'];
|
|
|
|
$params['reply-to-name'] = $phids[$from]['name'];
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
}
|
|
|
|
$mailer->setFrom(
|
|
|
|
$default,
|
2012-05-18 06:46:45 +02:00
|
|
|
$phids[$from]['name']);
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
unset($params['from']);
|
2011-02-09 20:11:24 +01:00
|
|
|
}
|
|
|
|
}
|
2011-02-02 00:52:04 +01:00
|
|
|
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
$is_first = idx($params, 'is-first-message');
|
Fix a threading issue with Amazon SES
Summary:
Amazon SES does not allow us to set a Message-ID header, which means
that threads are incorrect in Mail.app (and presumably other applications
which respect In-Reply-To and References) because the initial email does not
have anything which attaches it to the rest of the thread. To fix this, never
rely on Message-ID if the mailer doesn't support Message-ID.
(In the Amazon SES case, Amazon generates its own Message-ID which we can't
know ahead of time).
I additionally used all the Lisk isolation from the other tests to make this
testable and wrote tests for it.
I also moved the idea of a thread ID lower in the stack and out of
DifferentialMail, which should not be responsible for implementation details.
NOTE: If you push this, it will cause a one-time break of threading for
everyone using Outlook since I've changed the seed for generating Thread-Index.
I feel like this is okay to avoid introducing more complexity here.
Test Plan:
Created and then updated a revision, messages delivered over Amazon
SES threaded correctly in Mail.app. Verified headers. Unit tests.
Reviewed By: rm
Reviewers: aran, tuomaspelkonen, jungejason, rm
Commenters: aran
CC: aran, rm, epriestley
Differential Revision: 195
2011-04-30 20:47:00 +02:00
|
|
|
unset($params['is-first-message']);
|
|
|
|
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
$is_threaded = (bool)idx($params, 'thread-id');
|
|
|
|
|
2011-05-07 01:35:59 +02:00
|
|
|
$reply_to_name = idx($params, 'reply-to-name', '');
|
|
|
|
unset($params['reply-to-name']);
|
|
|
|
|
Add email preferences to receive fewer less-important notifications
Summary:
A few similar requests have come in across several tools and use cases that I
think this does a reasonable job of resolving.
We currently send one email for each update an object receives, but these aren't
always appreciated:
- Asana does post-commit review via Differential, so the "committed" mails are
useless.
- Quora wants to make project category edits to bugs without spamming people
attached to them.
- Some users in general are very sensitive to email volumes, and this gives us
a good way to reduce the volumes without incurring the complexity of
delayed-send-batching.
The technical mechanism is basically:
- Mail may optionally have "mail tags", which indicate content in the mail
(e.g., "maniphest-priority, maniphest-cc, maniphest-comment" for a mail which
contains a priority change, a CC change, and a comment).
- If a mail has tags, remove any recipients who have opted out of all the
tags.
- Some tags can't be opted out of via the UI, so this ensures that important
email is still delivered (e.g., cc + assign + comment is always delivered
because you can't opt out of "assign" or "comment").
Test Plan:
- Disabled all mail tags in the web UI.
- Used test console to send myself mail with an opt-outable tag, it was
immediately dropped.
- Used test console to send myself mail with an opt-outable tag and a custom
tag, it was delivered.
- Made Differential updates affecting CCs with and without comments, got
appropriate delivery.
- Made Maniphest updates affecting project, priority and CCs with and without
comments, got appropriate delivery.
- Verified mail headers in all cases.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley, moskov
Maniphest Tasks: T616, T855
Differential Revision: https://secure.phabricator.com/D1635
2012-02-18 07:57:07 +01:00
|
|
|
$add_cc = array();
|
|
|
|
$add_to = array();
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
|
2011-02-09 20:11:24 +01:00
|
|
|
foreach ($params as $key => $value) {
|
2011-01-26 02:40:21 +01:00
|
|
|
switch ($key) {
|
|
|
|
case 'from':
|
2012-05-18 06:46:45 +02:00
|
|
|
$mailer->setFrom($phids[$from]['email']);
|
2011-01-26 02:40:21 +01:00
|
|
|
break;
|
|
|
|
case 'reply-to':
|
2011-05-07 01:35:59 +02:00
|
|
|
$mailer->addReplyTo($value, $reply_to_name);
|
2011-01-26 02:40:21 +01:00
|
|
|
break;
|
|
|
|
case 'to':
|
2012-10-23 01:25:43 +02:00
|
|
|
$to_emails = $this->filterSendable($value, $phids);
|
2012-05-18 06:46:45 +02:00
|
|
|
if ($to_emails) {
|
|
|
|
$add_to = array_merge($add_to, $to_emails);
|
2011-01-26 18:33:31 +01:00
|
|
|
}
|
2011-01-26 02:40:21 +01:00
|
|
|
break;
|
2012-05-07 19:29:33 +02:00
|
|
|
case 'raw-to':
|
|
|
|
$add_to = array_merge($add_to, $value);
|
|
|
|
break;
|
2011-01-26 02:40:21 +01:00
|
|
|
case 'cc':
|
2012-10-23 01:25:43 +02:00
|
|
|
$cc_emails = $this->filterSendable($value, $phids);
|
2012-05-18 06:46:45 +02:00
|
|
|
if ($cc_emails) {
|
|
|
|
$add_cc = $cc_emails;
|
2011-01-26 18:33:31 +01:00
|
|
|
}
|
2011-01-26 02:40:21 +01:00
|
|
|
break;
|
|
|
|
case 'headers':
|
2012-06-22 03:39:35 +02:00
|
|
|
foreach ($value as $pair) {
|
|
|
|
list($header_key, $header_value) = $pair;
|
|
|
|
|
2012-01-08 19:25:01 +01:00
|
|
|
// NOTE: If we have \n in a header, SES rejects the email.
|
|
|
|
$header_value = str_replace("\n", " ", $header_value);
|
|
|
|
|
2011-01-26 02:40:21 +01:00
|
|
|
$mailer->addHeader($header_key, $header_value);
|
|
|
|
}
|
|
|
|
break;
|
2011-10-14 21:07:29 +02:00
|
|
|
case 'attachments':
|
|
|
|
foreach ($value as $attachment) {
|
|
|
|
$mailer->addAttachment(
|
2011-11-09 00:15:44 +01:00
|
|
|
$attachment->getData(),
|
2012-01-17 08:05:44 +01:00
|
|
|
$attachment->getFilename(),
|
2011-11-09 00:15:44 +01:00
|
|
|
$attachment->getMimeType()
|
2011-10-14 21:07:29 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
break;
|
2011-01-26 02:40:21 +01:00
|
|
|
case 'body':
|
|
|
|
$mailer->setBody($value);
|
|
|
|
break;
|
|
|
|
case 'subject':
|
Fix various threading issues, particularly in Gmail
Summary:
- Add an explicit multiplexing option, and enable it by default. This is necessary for Mail.app to coexist with other clients ("Re:" breaks outlook at the very least, and generally sucks in the common case), and allows users with flexible clients to enable subject variance.
- Add an option for subject line variance. Default to not varying the subject, so mail no longer says [Committed], [Closed], etc. This is so the defaults thread correctly in Gmail (not entirely sure this actually works).
- Add a preference to enable subject line variance.
- Unless all mail is multiplexed, don't enable or respect the "Re" or "vary subject" preferences. These are currently shown and respected in non-multiplex cases, which creates inconsistent results.
NOTE: @jungejason @nh @vrana This changes the default behavior (from non-multiplexing to multiplexing), and might break Facebook's integration. You should be able to keep the same behavior by setting the options appropriately, although if you can get the new defaults working they're probably better.
Test Plan:
Send mail from Maniphest, Differential and Audit. Updated preferences. Enabled/disabled multiplexing. Things seem OK?
NOTE: I haven't actually been able to repro the Gmail threading issue so I'm not totally sure what's going on there, maybe it started respecting "Re:" (or always has), but @cpiro and @20after4 both reported it independently. This fixes a bunch of bugs in any case and gives us more conservative set of defaults.
I'll see if I can buff out the Gmail story a bit but every client is basically a giant black box of mystery. :/
Reviewers: btrahan, vrana, jungejason, nh
Reviewed By: btrahan
CC: cpiro, 20after4, aran
Maniphest Tasks: T1097, T847
Differential Revision: https://secure.phabricator.com/D2206
2012-04-12 18:31:03 +02:00
|
|
|
// Only try to use preferences if everything is multiplexed, so we
|
|
|
|
// get consistent behavior.
|
|
|
|
$use_prefs = self::shouldMultiplexAllMail();
|
|
|
|
|
|
|
|
$prefs = null;
|
|
|
|
if ($use_prefs) {
|
2012-07-23 18:23:54 +02:00
|
|
|
|
|
|
|
// If multiplexing is enabled, some recipients will be in "Cc"
|
|
|
|
// rather than "To". We'll move them to "To" later (or supply a
|
|
|
|
// dummy "To") but need to look for the recipient in either the
|
|
|
|
// "To" or "Cc" fields here.
|
|
|
|
$target_phid = head(idx($params, 'to', array()));
|
|
|
|
if (!$target_phid) {
|
|
|
|
$target_phid = head(idx($params, 'cc', array()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($target_phid) {
|
|
|
|
$user = id(new PhabricatorUser())->loadOneWhere(
|
|
|
|
'phid = %s',
|
|
|
|
$target_phid);
|
|
|
|
if ($user) {
|
|
|
|
$prefs = $user->loadPreferences();
|
|
|
|
}
|
Fix various threading issues, particularly in Gmail
Summary:
- Add an explicit multiplexing option, and enable it by default. This is necessary for Mail.app to coexist with other clients ("Re:" breaks outlook at the very least, and generally sucks in the common case), and allows users with flexible clients to enable subject variance.
- Add an option for subject line variance. Default to not varying the subject, so mail no longer says [Committed], [Closed], etc. This is so the defaults thread correctly in Gmail (not entirely sure this actually works).
- Add a preference to enable subject line variance.
- Unless all mail is multiplexed, don't enable or respect the "Re" or "vary subject" preferences. These are currently shown and respected in non-multiplex cases, which creates inconsistent results.
NOTE: @jungejason @nh @vrana This changes the default behavior (from non-multiplexing to multiplexing), and might break Facebook's integration. You should be able to keep the same behavior by setting the options appropriately, although if you can get the new defaults working they're probably better.
Test Plan:
Send mail from Maniphest, Differential and Audit. Updated preferences. Enabled/disabled multiplexing. Things seem OK?
NOTE: I haven't actually been able to repro the Gmail threading issue so I'm not totally sure what's going on there, maybe it started respecting "Re:" (or always has), but @cpiro and @20after4 both reported it independently. This fixes a bunch of bugs in any case and gives us more conservative set of defaults.
I'll see if I can buff out the Gmail story a bit but every client is basically a giant black box of mystery. :/
Reviewers: btrahan, vrana, jungejason, nh
Reviewed By: btrahan
CC: cpiro, 20after4, aran
Maniphest Tasks: T1097, T847
Differential Revision: https://secure.phabricator.com/D2206
2012-04-12 18:31:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-11 21:21:37 +02:00
|
|
|
$subject = array();
|
Fix various threading issues, particularly in Gmail
Summary:
- Add an explicit multiplexing option, and enable it by default. This is necessary for Mail.app to coexist with other clients ("Re:" breaks outlook at the very least, and generally sucks in the common case), and allows users with flexible clients to enable subject variance.
- Add an option for subject line variance. Default to not varying the subject, so mail no longer says [Committed], [Closed], etc. This is so the defaults thread correctly in Gmail (not entirely sure this actually works).
- Add a preference to enable subject line variance.
- Unless all mail is multiplexed, don't enable or respect the "Re" or "vary subject" preferences. These are currently shown and respected in non-multiplex cases, which creates inconsistent results.
NOTE: @jungejason @nh @vrana This changes the default behavior (from non-multiplexing to multiplexing), and might break Facebook's integration. You should be able to keep the same behavior by setting the options appropriately, although if you can get the new defaults working they're probably better.
Test Plan:
Send mail from Maniphest, Differential and Audit. Updated preferences. Enabled/disabled multiplexing. Things seem OK?
NOTE: I haven't actually been able to repro the Gmail threading issue so I'm not totally sure what's going on there, maybe it started respecting "Re:" (or always has), but @cpiro and @20after4 both reported it independently. This fixes a bunch of bugs in any case and gives us more conservative set of defaults.
I'll see if I can buff out the Gmail story a bit but every client is basically a giant black box of mystery. :/
Reviewers: btrahan, vrana, jungejason, nh
Reviewed By: btrahan
CC: cpiro, 20after4, aran
Maniphest Tasks: T1097, T847
Differential Revision: https://secure.phabricator.com/D2206
2012-04-12 18:31:03 +02:00
|
|
|
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
if ($is_threaded) {
|
|
|
|
$add_re = PhabricatorEnv::getEnvConfig('metamta.re-prefix');
|
|
|
|
|
Fix various threading issues, particularly in Gmail
Summary:
- Add an explicit multiplexing option, and enable it by default. This is necessary for Mail.app to coexist with other clients ("Re:" breaks outlook at the very least, and generally sucks in the common case), and allows users with flexible clients to enable subject variance.
- Add an option for subject line variance. Default to not varying the subject, so mail no longer says [Committed], [Closed], etc. This is so the defaults thread correctly in Gmail (not entirely sure this actually works).
- Add a preference to enable subject line variance.
- Unless all mail is multiplexed, don't enable or respect the "Re" or "vary subject" preferences. These are currently shown and respected in non-multiplex cases, which creates inconsistent results.
NOTE: @jungejason @nh @vrana This changes the default behavior (from non-multiplexing to multiplexing), and might break Facebook's integration. You should be able to keep the same behavior by setting the options appropriately, although if you can get the new defaults working they're probably better.
Test Plan:
Send mail from Maniphest, Differential and Audit. Updated preferences. Enabled/disabled multiplexing. Things seem OK?
NOTE: I haven't actually been able to repro the Gmail threading issue so I'm not totally sure what's going on there, maybe it started respecting "Re:" (or always has), but @cpiro and @20after4 both reported it independently. This fixes a bunch of bugs in any case and gives us more conservative set of defaults.
I'll see if I can buff out the Gmail story a bit but every client is basically a giant black box of mystery. :/
Reviewers: btrahan, vrana, jungejason, nh
Reviewed By: btrahan
CC: cpiro, 20after4, aran
Maniphest Tasks: T1097, T847
Differential Revision: https://secure.phabricator.com/D2206
2012-04-12 18:31:03 +02:00
|
|
|
if ($prefs) {
|
|
|
|
$add_re = $prefs->getPreference(
|
|
|
|
PhabricatorUserPreferences::PREFERENCE_RE_PREFIX,
|
|
|
|
$add_re);
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($add_re) {
|
2012-06-11 21:21:37 +02:00
|
|
|
$subject[] = 'Re:';
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-11 21:21:37 +02:00
|
|
|
$subject[] = trim(idx($params, 'subject-prefix'));
|
|
|
|
|
|
|
|
$vary_prefix = idx($params, 'vary-subject-prefix');
|
|
|
|
if ($vary_prefix != '') {
|
|
|
|
$use_subject = PhabricatorEnv::getEnvConfig(
|
|
|
|
'metamta.vary-subjects');
|
|
|
|
|
|
|
|
if ($prefs) {
|
|
|
|
$use_subject = $prefs->getPreference(
|
|
|
|
PhabricatorUserPreferences::PREFERENCE_VARY_SUBJECT,
|
|
|
|
$use_subject);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($use_subject) {
|
|
|
|
$subject[] = $vary_prefix;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$subject[] = $value;
|
|
|
|
|
|
|
|
$mailer->setSubject(implode(' ', array_filter($subject)));
|
2011-01-26 02:40:21 +01:00
|
|
|
break;
|
|
|
|
case 'is-html':
|
|
|
|
if ($value) {
|
|
|
|
$mailer->setIsHTML(true);
|
|
|
|
}
|
|
|
|
break;
|
2011-10-23 21:07:37 +02:00
|
|
|
case 'is-bulk':
|
|
|
|
if ($value) {
|
|
|
|
if (PhabricatorEnv::getEnvConfig('metamta.precedence-bulk')) {
|
|
|
|
$mailer->addHeader('Precedence', 'bulk');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
Fix a threading issue with Amazon SES
Summary:
Amazon SES does not allow us to set a Message-ID header, which means
that threads are incorrect in Mail.app (and presumably other applications
which respect In-Reply-To and References) because the initial email does not
have anything which attaches it to the rest of the thread. To fix this, never
rely on Message-ID if the mailer doesn't support Message-ID.
(In the Amazon SES case, Amazon generates its own Message-ID which we can't
know ahead of time).
I additionally used all the Lisk isolation from the other tests to make this
testable and wrote tests for it.
I also moved the idea of a thread ID lower in the stack and out of
DifferentialMail, which should not be responsible for implementation details.
NOTE: If you push this, it will cause a one-time break of threading for
everyone using Outlook since I've changed the seed for generating Thread-Index.
I feel like this is okay to avoid introducing more complexity here.
Test Plan:
Created and then updated a revision, messages delivered over Amazon
SES threaded correctly in Mail.app. Verified headers. Unit tests.
Reviewed By: rm
Reviewers: aran, tuomaspelkonen, jungejason, rm
Commenters: aran
CC: aran, rm, epriestley
Differential Revision: 195
2011-04-30 20:47:00 +02:00
|
|
|
case 'thread-id':
|
Fix various threading issues, particularly in Gmail
Summary:
- Add an explicit multiplexing option, and enable it by default. This is necessary for Mail.app to coexist with other clients ("Re:" breaks outlook at the very least, and generally sucks in the common case), and allows users with flexible clients to enable subject variance.
- Add an option for subject line variance. Default to not varying the subject, so mail no longer says [Committed], [Closed], etc. This is so the defaults thread correctly in Gmail (not entirely sure this actually works).
- Add a preference to enable subject line variance.
- Unless all mail is multiplexed, don't enable or respect the "Re" or "vary subject" preferences. These are currently shown and respected in non-multiplex cases, which creates inconsistent results.
NOTE: @jungejason @nh @vrana This changes the default behavior (from non-multiplexing to multiplexing), and might break Facebook's integration. You should be able to keep the same behavior by setting the options appropriately, although if you can get the new defaults working they're probably better.
Test Plan:
Send mail from Maniphest, Differential and Audit. Updated preferences. Enabled/disabled multiplexing. Things seem OK?
NOTE: I haven't actually been able to repro the Gmail threading issue so I'm not totally sure what's going on there, maybe it started respecting "Re:" (or always has), but @cpiro and @20after4 both reported it independently. This fixes a bunch of bugs in any case and gives us more conservative set of defaults.
I'll see if I can buff out the Gmail story a bit but every client is basically a giant black box of mystery. :/
Reviewers: btrahan, vrana, jungejason, nh
Reviewed By: btrahan
CC: cpiro, 20after4, aran
Maniphest Tasks: T1097, T847
Differential Revision: https://secure.phabricator.com/D2206
2012-04-12 18:31:03 +02:00
|
|
|
|
|
|
|
// NOTE: Gmail freaks out about In-Reply-To and References which
|
|
|
|
// aren't in the form "<string@domain.tld>"; this is also required
|
|
|
|
// by RFC 2822, although some clients are more liberal in what they
|
|
|
|
// accept.
|
|
|
|
$domain = PhabricatorEnv::getEnvConfig('metamta.domain');
|
|
|
|
$value = '<'.$value.'@'.$domain.'>';
|
|
|
|
|
Fix a threading issue with Amazon SES
Summary:
Amazon SES does not allow us to set a Message-ID header, which means
that threads are incorrect in Mail.app (and presumably other applications
which respect In-Reply-To and References) because the initial email does not
have anything which attaches it to the rest of the thread. To fix this, never
rely on Message-ID if the mailer doesn't support Message-ID.
(In the Amazon SES case, Amazon generates its own Message-ID which we can't
know ahead of time).
I additionally used all the Lisk isolation from the other tests to make this
testable and wrote tests for it.
I also moved the idea of a thread ID lower in the stack and out of
DifferentialMail, which should not be responsible for implementation details.
NOTE: If you push this, it will cause a one-time break of threading for
everyone using Outlook since I've changed the seed for generating Thread-Index.
I feel like this is okay to avoid introducing more complexity here.
Test Plan:
Created and then updated a revision, messages delivered over Amazon
SES threaded correctly in Mail.app. Verified headers. Unit tests.
Reviewed By: rm
Reviewers: aran, tuomaspelkonen, jungejason, rm
Commenters: aran
CC: aran, rm, epriestley
Differential Revision: 195
2011-04-30 20:47:00 +02:00
|
|
|
if ($is_first && $mailer->supportsMessageIDHeader()) {
|
|
|
|
$mailer->addHeader('Message-ID', $value);
|
|
|
|
} else {
|
2011-06-22 21:41:19 +02:00
|
|
|
$in_reply_to = $value;
|
2011-06-23 00:37:30 +02:00
|
|
|
$references = array($value);
|
2011-06-22 21:41:19 +02:00
|
|
|
$parent_id = $this->getParentMessageID();
|
|
|
|
if ($parent_id) {
|
|
|
|
$in_reply_to = $parent_id;
|
2011-06-23 00:37:30 +02:00
|
|
|
// By RFC 2822, the most immediate parent should appear last
|
|
|
|
// in the "References" header, so this order is intentional.
|
|
|
|
$references[] = $parent_id;
|
2011-06-22 21:41:19 +02:00
|
|
|
}
|
2011-06-23 00:37:30 +02:00
|
|
|
$references = implode(' ', $references);
|
2011-06-22 21:41:19 +02:00
|
|
|
$mailer->addHeader('In-Reply-To', $in_reply_to);
|
2011-06-23 00:37:30 +02:00
|
|
|
$mailer->addHeader('References', $references);
|
Fix a threading issue with Amazon SES
Summary:
Amazon SES does not allow us to set a Message-ID header, which means
that threads are incorrect in Mail.app (and presumably other applications
which respect In-Reply-To and References) because the initial email does not
have anything which attaches it to the rest of the thread. To fix this, never
rely on Message-ID if the mailer doesn't support Message-ID.
(In the Amazon SES case, Amazon generates its own Message-ID which we can't
know ahead of time).
I additionally used all the Lisk isolation from the other tests to make this
testable and wrote tests for it.
I also moved the idea of a thread ID lower in the stack and out of
DifferentialMail, which should not be responsible for implementation details.
NOTE: If you push this, it will cause a one-time break of threading for
everyone using Outlook since I've changed the seed for generating Thread-Index.
I feel like this is okay to avoid introducing more complexity here.
Test Plan:
Created and then updated a revision, messages delivered over Amazon
SES threaded correctly in Mail.app. Verified headers. Unit tests.
Reviewed By: rm
Reviewers: aran, tuomaspelkonen, jungejason, rm
Commenters: aran
CC: aran, rm, epriestley
Differential Revision: 195
2011-04-30 20:47:00 +02:00
|
|
|
}
|
|
|
|
$thread_index = $this->generateThreadIndex($value, $is_first);
|
|
|
|
$mailer->addHeader('Thread-Index', $thread_index);
|
|
|
|
break;
|
Add email preferences to receive fewer less-important notifications
Summary:
A few similar requests have come in across several tools and use cases that I
think this does a reasonable job of resolving.
We currently send one email for each update an object receives, but these aren't
always appreciated:
- Asana does post-commit review via Differential, so the "committed" mails are
useless.
- Quora wants to make project category edits to bugs without spamming people
attached to them.
- Some users in general are very sensitive to email volumes, and this gives us
a good way to reduce the volumes without incurring the complexity of
delayed-send-batching.
The technical mechanism is basically:
- Mail may optionally have "mail tags", which indicate content in the mail
(e.g., "maniphest-priority, maniphest-cc, maniphest-comment" for a mail which
contains a priority change, a CC change, and a comment).
- If a mail has tags, remove any recipients who have opted out of all the
tags.
- Some tags can't be opted out of via the UI, so this ensures that important
email is still delivered (e.g., cc + assign + comment is always delivered
because you can't opt out of "assign" or "comment").
Test Plan:
- Disabled all mail tags in the web UI.
- Used test console to send myself mail with an opt-outable tag, it was
immediately dropped.
- Used test console to send myself mail with an opt-outable tag and a custom
tag, it was delivered.
- Made Differential updates affecting CCs with and without comments, got
appropriate delivery.
- Made Maniphest updates affecting project, priority and CCs with and without
comments, got appropriate delivery.
- Verified mail headers in all cases.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley, moskov
Maniphest Tasks: T616, T855
Differential Revision: https://secure.phabricator.com/D1635
2012-02-18 07:57:07 +01:00
|
|
|
case 'mailtags':
|
|
|
|
// Handled below.
|
|
|
|
break;
|
2012-06-11 21:21:37 +02:00
|
|
|
case 'subject-prefix':
|
|
|
|
case 'vary-subject-prefix':
|
Fix various threading issues, particularly in Gmail
Summary:
- Add an explicit multiplexing option, and enable it by default. This is necessary for Mail.app to coexist with other clients ("Re:" breaks outlook at the very least, and generally sucks in the common case), and allows users with flexible clients to enable subject variance.
- Add an option for subject line variance. Default to not varying the subject, so mail no longer says [Committed], [Closed], etc. This is so the defaults thread correctly in Gmail (not entirely sure this actually works).
- Add a preference to enable subject line variance.
- Unless all mail is multiplexed, don't enable or respect the "Re" or "vary subject" preferences. These are currently shown and respected in non-multiplex cases, which creates inconsistent results.
NOTE: @jungejason @nh @vrana This changes the default behavior (from non-multiplexing to multiplexing), and might break Facebook's integration. You should be able to keep the same behavior by setting the options appropriately, although if you can get the new defaults working they're probably better.
Test Plan:
Send mail from Maniphest, Differential and Audit. Updated preferences. Enabled/disabled multiplexing. Things seem OK?
NOTE: I haven't actually been able to repro the Gmail threading issue so I'm not totally sure what's going on there, maybe it started respecting "Re:" (or always has), but @cpiro and @20after4 both reported it independently. This fixes a bunch of bugs in any case and gives us more conservative set of defaults.
I'll see if I can buff out the Gmail story a bit but every client is basically a giant black box of mystery. :/
Reviewers: btrahan, vrana, jungejason, nh
Reviewed By: btrahan
CC: cpiro, 20after4, aran
Maniphest Tasks: T1097, T847
Differential Revision: https://secure.phabricator.com/D2206
2012-04-12 18:31:03 +02:00
|
|
|
// Handled above.
|
|
|
|
break;
|
2011-01-26 02:40:21 +01:00
|
|
|
default:
|
|
|
|
// Just discard.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-23 01:25:43 +02:00
|
|
|
if (!$add_to && !$add_cc) {
|
|
|
|
$this->setStatus(self::STATUS_VOID);
|
|
|
|
$this->setMessage(
|
|
|
|
"Message has no valid recipients: all To/CC are disabled or ".
|
|
|
|
"configured not to receive this mail.");
|
|
|
|
return $this->save();
|
|
|
|
}
|
|
|
|
|
2012-03-01 00:30:56 +01:00
|
|
|
$mailer->addHeader('X-Phabricator-Sent-This-Message', 'Yes');
|
2011-01-26 02:40:21 +01:00
|
|
|
$mailer->addHeader('X-Mail-Transport-Agent', 'MetaMTA');
|
|
|
|
|
2012-04-17 15:35:28 +02:00
|
|
|
// Some clients respect this to suppress OOF and other auto-responses.
|
|
|
|
$mailer->addHeader('X-Auto-Response-Suppress', 'All');
|
|
|
|
|
Add email preferences to receive fewer less-important notifications
Summary:
A few similar requests have come in across several tools and use cases that I
think this does a reasonable job of resolving.
We currently send one email for each update an object receives, but these aren't
always appreciated:
- Asana does post-commit review via Differential, so the "committed" mails are
useless.
- Quora wants to make project category edits to bugs without spamming people
attached to them.
- Some users in general are very sensitive to email volumes, and this gives us
a good way to reduce the volumes without incurring the complexity of
delayed-send-batching.
The technical mechanism is basically:
- Mail may optionally have "mail tags", which indicate content in the mail
(e.g., "maniphest-priority, maniphest-cc, maniphest-comment" for a mail which
contains a priority change, a CC change, and a comment).
- If a mail has tags, remove any recipients who have opted out of all the
tags.
- Some tags can't be opted out of via the UI, so this ensures that important
email is still delivered (e.g., cc + assign + comment is always delivered
because you can't opt out of "assign" or "comment").
Test Plan:
- Disabled all mail tags in the web UI.
- Used test console to send myself mail with an opt-outable tag, it was
immediately dropped.
- Used test console to send myself mail with an opt-outable tag and a custom
tag, it was delivered.
- Made Differential updates affecting CCs with and without comments, got
appropriate delivery.
- Made Maniphest updates affecting project, priority and CCs with and without
comments, got appropriate delivery.
- Verified mail headers in all cases.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley, moskov
Maniphest Tasks: T616, T855
Differential Revision: https://secure.phabricator.com/D1635
2012-02-18 07:57:07 +01:00
|
|
|
// If the message has mailtags, filter out any recipients who don't want
|
|
|
|
// to receive this type of mail.
|
|
|
|
$mailtags = $this->getParam('mailtags');
|
2012-10-23 01:25:43 +02:00
|
|
|
if ($mailtags) {
|
Add email preferences to receive fewer less-important notifications
Summary:
A few similar requests have come in across several tools and use cases that I
think this does a reasonable job of resolving.
We currently send one email for each update an object receives, but these aren't
always appreciated:
- Asana does post-commit review via Differential, so the "committed" mails are
useless.
- Quora wants to make project category edits to bugs without spamming people
attached to them.
- Some users in general are very sensitive to email volumes, and this gives us
a good way to reduce the volumes without incurring the complexity of
delayed-send-batching.
The technical mechanism is basically:
- Mail may optionally have "mail tags", which indicate content in the mail
(e.g., "maniphest-priority, maniphest-cc, maniphest-comment" for a mail which
contains a priority change, a CC change, and a comment).
- If a mail has tags, remove any recipients who have opted out of all the
tags.
- Some tags can't be opted out of via the UI, so this ensures that important
email is still delivered (e.g., cc + assign + comment is always delivered
because you can't opt out of "assign" or "comment").
Test Plan:
- Disabled all mail tags in the web UI.
- Used test console to send myself mail with an opt-outable tag, it was
immediately dropped.
- Used test console to send myself mail with an opt-outable tag and a custom
tag, it was delivered.
- Made Differential updates affecting CCs with and without comments, got
appropriate delivery.
- Made Maniphest updates affecting project, priority and CCs with and without
comments, got appropriate delivery.
- Verified mail headers in all cases.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley, moskov
Maniphest Tasks: T616, T855
Differential Revision: https://secure.phabricator.com/D1635
2012-02-18 07:57:07 +01:00
|
|
|
$tag_header = array();
|
|
|
|
foreach ($mailtags as $mailtag) {
|
|
|
|
$tag_header[] = '<'.$mailtag.'>';
|
|
|
|
}
|
|
|
|
$tag_header = implode(', ', $tag_header);
|
|
|
|
$mailer->addHeader('X-Phabricator-Mail-Tags', $tag_header);
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
}
|
|
|
|
|
2012-08-20 23:08:45 +02:00
|
|
|
// Some mailers require a valid "To:" in order to deliver mail. If we
|
|
|
|
// don't have any "To:", try to fill it in with a placeholder "To:".
|
|
|
|
// If that also fails, move the "Cc:" line to "To:".
|
|
|
|
if (!$add_to) {
|
|
|
|
$placeholder_key = 'metamta.placeholder-to-recipient';
|
|
|
|
$placeholder = PhabricatorEnv::getEnvConfig($placeholder_key);
|
|
|
|
if ($placeholder !== null) {
|
|
|
|
$add_to = array($placeholder);
|
|
|
|
} else {
|
|
|
|
$add_to = $add_cc;
|
|
|
|
$add_cc = array();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$mailer->addTos($add_to);
|
|
|
|
if ($add_cc) {
|
|
|
|
$mailer->addCCs($add_cc);
|
|
|
|
}
|
2011-01-26 02:40:21 +01:00
|
|
|
} catch (Exception $ex) {
|
|
|
|
$this->setStatus(self::STATUS_FAIL);
|
|
|
|
$this->setMessage($ex->getMessage());
|
2011-10-19 21:54:49 +02:00
|
|
|
return $this->save();
|
2011-01-26 02:40:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->getRetryCount() < $this->getSimulatedFailureCount()) {
|
|
|
|
$ok = false;
|
|
|
|
$error = 'Simulated failure.';
|
|
|
|
} else {
|
2011-01-26 18:33:31 +01:00
|
|
|
try {
|
|
|
|
$ok = $mailer->send();
|
2011-02-08 06:08:32 +01:00
|
|
|
$error = null;
|
2011-01-26 18:33:31 +01:00
|
|
|
} catch (Exception $ex) {
|
|
|
|
$ok = false;
|
2011-02-11 01:19:17 +01:00
|
|
|
$error = $ex->getMessage()."\n".$ex->getTraceAsString();
|
2011-01-26 18:33:31 +01:00
|
|
|
}
|
2011-01-26 02:40:21 +01:00
|
|
|
}
|
2011-01-26 18:08:26 +01:00
|
|
|
|
2011-01-26 02:40:21 +01:00
|
|
|
if (!$ok) {
|
|
|
|
$this->setMessage($error);
|
|
|
|
if ($this->getRetryCount() > self::MAX_RETRIES) {
|
|
|
|
$this->setStatus(self::STATUS_FAIL);
|
|
|
|
} else {
|
|
|
|
$this->setRetryCount($this->getRetryCount() + 1);
|
|
|
|
$next_retry = time() + ($this->getRetryCount() * self::RETRY_DELAY);
|
|
|
|
$this->setNextRetry($next_retry);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$this->setStatus(self::STATUS_SENT);
|
|
|
|
}
|
|
|
|
|
2011-10-19 21:54:49 +02:00
|
|
|
return $this->save();
|
2011-01-26 02:40:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public static function getReadableStatus($status_code) {
|
|
|
|
static $readable = array(
|
|
|
|
self::STATUS_QUEUE => "Queued for Delivery",
|
|
|
|
self::STATUS_FAIL => "Delivery Failed",
|
|
|
|
self::STATUS_SENT => "Sent",
|
Add optional "Re:" prefix to all threaded mail and allow disabling mail about
your own actions
Summary:
- Mail.app on Lion has cumbersome threading rules, see T782. Add an option to
stick "Re: " in front of all threaded mail so it behaves. This is horrible, but
apparently the least-horrible option.
- While I was in there, I added an option for T228.
Test Plan:
- Sent a bunch of threaded and unthreaded mail with varous "Re:" settings,
seemed to get "Re:" in the right places.
- Disabled email about my stuff, created a task with just me, got voided mail,
added a CC, got mail to just the CC.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, mkjones
Maniphest Tasks: T228, T782
Differential Revision: https://secure.phabricator.com/D1448
2012-01-18 05:32:28 +01:00
|
|
|
self::STATUS_VOID => "Void",
|
2011-01-26 02:40:21 +01:00
|
|
|
);
|
|
|
|
$status_code = coalesce($status_code, '?');
|
|
|
|
return idx($readable, $status_code, $status_code);
|
|
|
|
}
|
|
|
|
|
Fix a threading issue with Amazon SES
Summary:
Amazon SES does not allow us to set a Message-ID header, which means
that threads are incorrect in Mail.app (and presumably other applications
which respect In-Reply-To and References) because the initial email does not
have anything which attaches it to the rest of the thread. To fix this, never
rely on Message-ID if the mailer doesn't support Message-ID.
(In the Amazon SES case, Amazon generates its own Message-ID which we can't
know ahead of time).
I additionally used all the Lisk isolation from the other tests to make this
testable and wrote tests for it.
I also moved the idea of a thread ID lower in the stack and out of
DifferentialMail, which should not be responsible for implementation details.
NOTE: If you push this, it will cause a one-time break of threading for
everyone using Outlook since I've changed the seed for generating Thread-Index.
I feel like this is okay to avoid introducing more complexity here.
Test Plan:
Created and then updated a revision, messages delivered over Amazon
SES threaded correctly in Mail.app. Verified headers. Unit tests.
Reviewed By: rm
Reviewers: aran, tuomaspelkonen, jungejason, rm
Commenters: aran
CC: aran, rm, epriestley
Differential Revision: 195
2011-04-30 20:47:00 +02:00
|
|
|
private function generateThreadIndex($seed, $is_first_mail) {
|
|
|
|
// When threading, Outlook ignores the 'References' and 'In-Reply-To'
|
|
|
|
// headers that most clients use. Instead, it uses a custom 'Thread-Index'
|
|
|
|
// header. The format of this header is something like this (from
|
|
|
|
// camel-exchange-folder.c in Evolution Exchange):
|
|
|
|
|
|
|
|
/* A new post to a folder gets a 27-byte-long thread index. (The value
|
|
|
|
* is apparently unique but meaningless.) Each reply to a post gets a
|
|
|
|
* 32-byte-long thread index whose first 27 bytes are the same as the
|
|
|
|
* parent's thread index. Each reply to any of those gets a
|
|
|
|
* 37-byte-long thread index, etc. The Thread-Index header contains a
|
|
|
|
* base64 representation of this value.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// The specific implementation uses a 27-byte header for the first email
|
|
|
|
// a recipient receives, and a random 5-byte suffix (32 bytes total)
|
|
|
|
// thereafter. This means that all the replies are (incorrectly) siblings,
|
|
|
|
// but it would be very difficult to keep track of the entire tree and this
|
|
|
|
// gets us reasonable client behavior.
|
|
|
|
|
|
|
|
$base = substr(md5($seed), 0, 27);
|
|
|
|
if (!$is_first_mail) {
|
|
|
|
// Not totally sure, but it seems like outlook orders replies by
|
|
|
|
// thread-index rather than timestamp, so to get these to show up in the
|
|
|
|
// right order we use the time as the last 4 bytes.
|
|
|
|
$base .= ' '.pack('N', time());
|
|
|
|
}
|
|
|
|
|
|
|
|
return base64_encode($base);
|
|
|
|
}
|
|
|
|
|
2012-05-18 06:46:45 +02:00
|
|
|
private function loadEmailAndNameDataFromPHIDs(array &$phids) {
|
|
|
|
$users = array();
|
|
|
|
$mlsts = array();
|
|
|
|
// first iteration - group by types to do data fetches
|
|
|
|
foreach ($phids as $phid => $type) {
|
|
|
|
switch ($type) {
|
|
|
|
case PhabricatorPHIDConstants::PHID_TYPE_USER:
|
|
|
|
$users[] = $phid;
|
|
|
|
break;
|
|
|
|
case PhabricatorPHIDConstants::PHID_TYPE_MLST:
|
|
|
|
$mlsts[] = $phid;
|
|
|
|
break;
|
2011-11-16 18:49:18 +01:00
|
|
|
}
|
2012-05-18 06:46:45 +02:00
|
|
|
}
|
|
|
|
$user_emails = array();
|
|
|
|
if ($users) {
|
|
|
|
$user_emails = id(new PhabricatorUserEmail())->loadAllWhere(
|
|
|
|
'userPHID IN (%Ls) AND isPrimary = 1', $users);
|
|
|
|
$users = id(new PhabricatorUser())->loadAllWhere(
|
|
|
|
'phid IN (%Ls)', $users);
|
|
|
|
$user_emails = mpull($user_emails, null, 'getUserPHID');
|
|
|
|
$users = mpull($users, null, 'getPHID');
|
|
|
|
}
|
|
|
|
if ($mlsts) {
|
|
|
|
$mlsts = id(new PhabricatorMetaMTAMailingList())->loadAllWhere(
|
|
|
|
'phid IN (%Ls)', $mlsts);
|
|
|
|
$mlsts = mpull($mlsts, null, 'getPHID');
|
|
|
|
}
|
|
|
|
|
|
|
|
// second iteration - create entries for each phid
|
|
|
|
$default = PhabricatorEnv::getEnvConfig('metamta.default-address');
|
|
|
|
foreach ($phids as $phid => &$value) {
|
|
|
|
$name = '';
|
|
|
|
$email = $default;
|
|
|
|
$is_mailable = false;
|
|
|
|
switch ($value) {
|
|
|
|
case PhabricatorPHIDConstants::PHID_TYPE_USER:
|
|
|
|
$user = $users[$phid];
|
|
|
|
if ($user) {
|
2012-10-05 20:27:58 +02:00
|
|
|
$name = $this->getUserName($user);
|
2012-05-18 06:46:45 +02:00
|
|
|
$is_mailable = !$user->getIsDisabled()
|
|
|
|
&& !$user->getIsSystemAgent();
|
|
|
|
}
|
|
|
|
$email = $user_emails[$phid] ?
|
|
|
|
$user_emails[$phid]->getAddress() :
|
|
|
|
$default;
|
|
|
|
break;
|
|
|
|
case PhabricatorPHIDConstants::PHID_TYPE_MLST:
|
|
|
|
$mlst = $mlsts[$phid];
|
|
|
|
if ($mlst) {
|
|
|
|
$name = $mlst->getName();
|
|
|
|
$email = $mlst->getEmail();
|
|
|
|
$is_mailable = true;
|
|
|
|
}
|
|
|
|
break;
|
2011-11-16 18:49:18 +01:00
|
|
|
}
|
2012-05-18 06:46:45 +02:00
|
|
|
$value = array(
|
|
|
|
'name' => $name,
|
|
|
|
'email' => $email,
|
|
|
|
'mailable' => $is_mailable,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-05 20:27:58 +02:00
|
|
|
/**
|
|
|
|
* Small helper function to make sure we format the username properly as
|
|
|
|
* specified by the `metamta.user-address-format` configuration value.
|
|
|
|
*/
|
|
|
|
private function getUserName($user) {
|
|
|
|
$format = PhabricatorEnv::getEnvConfig(
|
|
|
|
'metamta.user-address-format',
|
|
|
|
'full'
|
|
|
|
);
|
|
|
|
|
|
|
|
switch ($format) {
|
|
|
|
case 'short':
|
|
|
|
$name = $user->getUserName();
|
|
|
|
break;
|
|
|
|
case 'real':
|
|
|
|
$name = $user->getRealName();
|
|
|
|
break;
|
|
|
|
case 'full':
|
|
|
|
default:
|
|
|
|
$name = $user->getFullName();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $name;
|
|
|
|
}
|
|
|
|
|
2012-10-23 01:25:43 +02:00
|
|
|
private function filterSendable($value, $phids) {
|
2012-05-18 06:46:45 +02:00
|
|
|
$result = array();
|
|
|
|
foreach ($value as $phid) {
|
|
|
|
if (isset($phids[$phid]) && $phids[$phid]['mailable']) {
|
|
|
|
$result[$phid] = $phids[$phid]['email'];
|
|
|
|
}
|
2011-11-16 18:49:18 +01:00
|
|
|
}
|
2012-05-18 06:46:45 +02:00
|
|
|
return $result;
|
2011-11-16 18:49:18 +01:00
|
|
|
}
|
|
|
|
|
Fix various threading issues, particularly in Gmail
Summary:
- Add an explicit multiplexing option, and enable it by default. This is necessary for Mail.app to coexist with other clients ("Re:" breaks outlook at the very least, and generally sucks in the common case), and allows users with flexible clients to enable subject variance.
- Add an option for subject line variance. Default to not varying the subject, so mail no longer says [Committed], [Closed], etc. This is so the defaults thread correctly in Gmail (not entirely sure this actually works).
- Add a preference to enable subject line variance.
- Unless all mail is multiplexed, don't enable or respect the "Re" or "vary subject" preferences. These are currently shown and respected in non-multiplex cases, which creates inconsistent results.
NOTE: @jungejason @nh @vrana This changes the default behavior (from non-multiplexing to multiplexing), and might break Facebook's integration. You should be able to keep the same behavior by setting the options appropriately, although if you can get the new defaults working they're probably better.
Test Plan:
Send mail from Maniphest, Differential and Audit. Updated preferences. Enabled/disabled multiplexing. Things seem OK?
NOTE: I haven't actually been able to repro the Gmail threading issue so I'm not totally sure what's going on there, maybe it started respecting "Re:" (or always has), but @cpiro and @20after4 both reported it independently. This fixes a bunch of bugs in any case and gives us more conservative set of defaults.
I'll see if I can buff out the Gmail story a bit but every client is basically a giant black box of mystery. :/
Reviewers: btrahan, vrana, jungejason, nh
Reviewed By: btrahan
CC: cpiro, 20after4, aran
Maniphest Tasks: T1097, T847
Differential Revision: https://secure.phabricator.com/D2206
2012-04-12 18:31:03 +02:00
|
|
|
public static function shouldMultiplexAllMail() {
|
|
|
|
return PhabricatorEnv::getEnvConfig('metamta.one-mail-per-recipient');
|
|
|
|
}
|
|
|
|
|
2012-10-23 01:25:43 +02:00
|
|
|
|
|
|
|
/* -( Managing Recipients )------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get all of the recipients for this mail, after preference filters are
|
|
|
|
* applied. This list has all objects to whom delivery will be attempted, but
|
|
|
|
* does not exclude recipeints two whom delivery may be impossible.
|
|
|
|
*
|
|
|
|
* @return list<phid> A list of all recipients to whom delivery will be
|
|
|
|
* attempted.
|
|
|
|
* @task recipients
|
|
|
|
*/
|
|
|
|
public function buildRecipientList() {
|
|
|
|
return $this->resolveRecipients(
|
|
|
|
array_merge(
|
|
|
|
$this->getRawToPHIDs(),
|
|
|
|
$this->getRawCCPHIDs()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filter out duplicate, invalid, or excluded recipients from a PHID list.
|
|
|
|
*
|
|
|
|
* @param list<phid> Unfiltered recipients.
|
|
|
|
* @return list<phid> Filtered recipients.
|
|
|
|
*
|
|
|
|
* @task recipients
|
|
|
|
*/
|
|
|
|
private function resolveRecipients(array $phids) {
|
2013-01-08 19:39:49 +01:00
|
|
|
if (!$phids) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
2012-10-23 01:25:43 +02:00
|
|
|
$phids = array_combine($phids, $phids);
|
|
|
|
|
|
|
|
|
|
|
|
// Exclude PHIDs explicitly marked for exclusion. We use this to prevent
|
|
|
|
// recipients of an accidental "Reply All" from receiving the followup
|
|
|
|
// mail from Phabricator.
|
|
|
|
$exclude = $this->getExcludeMailRecipientPHIDs();
|
|
|
|
$exclude = array_fill_keys($exclude, true);
|
|
|
|
$phids = array_diff_key($phids, $exclude);
|
|
|
|
|
|
|
|
|
|
|
|
// If the actor is a recipient and has configured their preferences not to
|
|
|
|
// send them mail about their own actions, drop them from the recipient
|
|
|
|
// list.
|
|
|
|
$from = $this->getParam('from');
|
|
|
|
if (isset($phids[$from])) {
|
|
|
|
$from_user = id(new PhabricatorUser())->loadOneWhere(
|
|
|
|
'phid = %s',
|
|
|
|
$from);
|
|
|
|
if ($from_user) {
|
|
|
|
$pref_key = PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL;
|
|
|
|
$exclude_self = $from_user
|
|
|
|
->loadPreferences()
|
|
|
|
->getPreference($pref_key);
|
|
|
|
if ($exclude_self) {
|
|
|
|
unset($phids[$from]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Exclude all recipients who have set preferences to not receive this type
|
|
|
|
// of email (for example, a user who says they don't want emails about task
|
|
|
|
// CC changes).
|
|
|
|
$tags = $this->getParam('mailtags');
|
|
|
|
if ($tags && $phids) {
|
|
|
|
$all_prefs = id(new PhabricatorUserPreferences())->loadAllWhere(
|
|
|
|
'userPHID in (%Ls)',
|
|
|
|
$phids);
|
|
|
|
$all_prefs = mpull($all_prefs, null, 'getUserPHID');
|
|
|
|
|
|
|
|
foreach ($phids as $phid) {
|
|
|
|
$prefs = idx($all_prefs, $phid);
|
|
|
|
if (!$prefs) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$user_mailtags = $prefs->getPreference(
|
|
|
|
PhabricatorUserPreferences::PREFERENCE_MAILTAGS,
|
|
|
|
array());
|
|
|
|
|
|
|
|
// The user must have elected to receive mail for at least one
|
|
|
|
// of the mailtags.
|
|
|
|
$send = false;
|
|
|
|
foreach ($tags as $tag) {
|
|
|
|
if (idx($user_mailtags, $tag, true)) {
|
|
|
|
$send = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$send) {
|
|
|
|
unset($phids[$phid]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array_keys($phids);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task recipients
|
|
|
|
*/
|
|
|
|
private function buildToList() {
|
|
|
|
return $this->resolveRecipients($this->getRawToPHIDs());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task recipients
|
|
|
|
*/
|
|
|
|
private function buildCCList() {
|
|
|
|
return $this->resolveRecipients($this->getRawCCPHIDs());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task recipients
|
|
|
|
*/
|
|
|
|
private function getRawToPHIDs() {
|
|
|
|
$to = $this->getParam('to', array());
|
|
|
|
return $this->filterRawPHIDList($to);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task recipients
|
|
|
|
*/
|
|
|
|
private function getRawCCPHIDs() {
|
|
|
|
$cc = $this->getParam('cc', array());
|
|
|
|
return $this->filterRawPHIDList($cc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task recipients
|
|
|
|
*/
|
|
|
|
private function filterRawPHIDList(array $list) {
|
|
|
|
$list = array_filter($list);
|
|
|
|
$list = array_unique($list);
|
|
|
|
return array_values($list);
|
|
|
|
}
|
|
|
|
|
2011-01-26 02:40:21 +01:00
|
|
|
}
|