2011-02-08 19:53:59 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
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
|
|
|
* Copyright 2012 Facebook, Inc.
|
2011-02-08 19:53:59 +01:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2011-07-04 22:04:22 +02:00
|
|
|
/**
|
|
|
|
* @group maniphest
|
|
|
|
*/
|
2012-03-14 00:21:04 +01:00
|
|
|
final class ManiphestTransactionEditor {
|
2011-02-08 19:53:59 +01:00
|
|
|
|
2011-06-22 21:41:19 +02:00
|
|
|
private $parentMessageID;
|
2012-03-07 00:10:17 +01:00
|
|
|
private $auxiliaryFields = array();
|
|
|
|
|
|
|
|
public function setAuxiliaryFields(array $fields) {
|
2012-04-03 21:10:45 +02:00
|
|
|
assert_instances_of($fields, 'ManiphestAuxiliaryFieldSpecification');
|
2012-03-07 00:10:17 +01:00
|
|
|
$this->auxiliaryFields = $fields;
|
|
|
|
return $this;
|
|
|
|
}
|
2011-06-22 21:41:19 +02:00
|
|
|
|
|
|
|
public function setParentMessageID($parent_message_id) {
|
|
|
|
$this->parentMessageID = $parent_message_id;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-04-03 21:10:45 +02:00
|
|
|
public function applyTransactions(ManiphestTask $task, array $transactions) {
|
|
|
|
assert_instances_of($transactions, 'ManiphestTransaction');
|
2011-02-08 19:53:59 +01:00
|
|
|
|
|
|
|
$email_cc = $task->getCCPHIDs();
|
|
|
|
|
|
|
|
$email_to = array();
|
|
|
|
$email_to[] = $task->getOwnerPHID();
|
|
|
|
|
2012-04-02 21:12:04 +02:00
|
|
|
$pri_changed = $this->isCreate($transactions);
|
|
|
|
|
2011-02-09 21:57:38 +01:00
|
|
|
foreach ($transactions as $key => $transaction) {
|
2011-02-09 21:47:24 +01:00
|
|
|
$type = $transaction->getTransactionType();
|
|
|
|
$new = $transaction->getNewValue();
|
|
|
|
$email_to[] = $transaction->getAuthorPHID();
|
|
|
|
|
2011-07-10 01:17:36 +02:00
|
|
|
$value_is_phid_set = false;
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
switch ($type) {
|
|
|
|
case ManiphestTransactionType::TYPE_NONE:
|
2011-02-09 21:47:24 +01:00
|
|
|
$old = null;
|
2011-02-08 19:53:59 +01:00
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_STATUS:
|
2011-02-09 21:47:24 +01:00
|
|
|
$old = $task->getStatus();
|
2011-02-08 19:53:59 +01:00
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_OWNER:
|
2011-02-09 21:47:24 +01:00
|
|
|
$old = $task->getOwnerPHID();
|
2011-02-08 19:53:59 +01:00
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_CCS:
|
2011-02-09 21:47:24 +01:00
|
|
|
$old = $task->getCCPHIDs();
|
2011-07-10 01:17:36 +02:00
|
|
|
$value_is_phid_set = true;
|
2011-02-08 19:53:59 +01:00
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_PRIORITY:
|
2011-02-09 21:47:24 +01:00
|
|
|
$old = $task->getPriority();
|
2011-02-08 19:53:59 +01:00
|
|
|
break;
|
2011-02-17 23:32:01 +01:00
|
|
|
case ManiphestTransactionType::TYPE_ATTACH:
|
|
|
|
$old = $task->getAttached();
|
|
|
|
break;
|
2011-02-20 23:15:53 +01:00
|
|
|
case ManiphestTransactionType::TYPE_TITLE:
|
|
|
|
$old = $task->getTitle();
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_DESCRIPTION:
|
|
|
|
$old = $task->getDescription();
|
|
|
|
break;
|
2011-02-21 05:08:16 +01:00
|
|
|
case ManiphestTransactionType::TYPE_PROJECTS:
|
|
|
|
$old = $task->getProjectPHIDs();
|
2011-07-10 01:17:36 +02:00
|
|
|
$value_is_phid_set = true;
|
2011-02-21 05:08:16 +01:00
|
|
|
break;
|
2011-12-24 04:03:28 +01:00
|
|
|
case ManiphestTransactionType::TYPE_AUXILIARY:
|
|
|
|
$aux_key = $transaction->getMetadataValue('aux:key');
|
|
|
|
if (!$aux_key) {
|
|
|
|
throw new Exception(
|
|
|
|
"Expected 'aux:key' metadata on TYPE_AUXILIARY transaction.");
|
|
|
|
}
|
|
|
|
$old = $task->getAuxiliaryAttribute($aux_key);
|
|
|
|
break;
|
2011-02-08 19:53:59 +01:00
|
|
|
default:
|
|
|
|
throw new Exception('Unknown action type.');
|
|
|
|
}
|
|
|
|
|
2011-07-10 01:17:36 +02:00
|
|
|
$old_cmp = $old;
|
|
|
|
$new_cmp = $new;
|
|
|
|
if ($value_is_phid_set) {
|
|
|
|
|
|
|
|
// Normalize the old and new values if they are PHID sets so we don't
|
|
|
|
// get any no-op transactions where the values differ only by keys,
|
|
|
|
// order, duplicates, etc.
|
|
|
|
|
|
|
|
if (is_array($old)) {
|
|
|
|
$old = array_filter($old);
|
|
|
|
$old = array_unique($old);
|
|
|
|
sort($old);
|
|
|
|
$old = array_values($old);
|
|
|
|
$old_cmp = $old;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_array($new)) {
|
|
|
|
$new = array_filter($new);
|
|
|
|
$new = array_unique($new);
|
|
|
|
$transaction->setNewValue($new);
|
|
|
|
|
|
|
|
$new_cmp = $new;
|
|
|
|
sort($new_cmp);
|
|
|
|
$new_cmp = array_values($new_cmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (($old !== null) && ($old_cmp == $new_cmp)) {
|
2011-02-09 21:57:38 +01:00
|
|
|
if (count($transactions) > 1 && !$transaction->hasComments()) {
|
|
|
|
// If we have at least one other transaction and this one isn't
|
|
|
|
// doing anything and doesn't have any comments, just throw it
|
|
|
|
// away.
|
|
|
|
unset($transactions[$key]);
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
$transaction->setOldValue(null);
|
|
|
|
$transaction->setNewValue(null);
|
|
|
|
$transaction->setTransactionType(ManiphestTransactionType::TYPE_NONE);
|
|
|
|
}
|
2011-02-09 21:47:24 +01:00
|
|
|
} else {
|
|
|
|
switch ($type) {
|
|
|
|
case ManiphestTransactionType::TYPE_NONE:
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_STATUS:
|
|
|
|
$task->setStatus($new);
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_OWNER:
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-27 03:50:17 +02:00
|
|
|
if ($new) {
|
|
|
|
$handles = id(new PhabricatorObjectHandleData(array($new)))
|
|
|
|
->loadHandles();
|
|
|
|
$task->setOwnerOrdering($handles[$new]->getName());
|
|
|
|
} else {
|
|
|
|
$task->setOwnerOrdering(null);
|
|
|
|
}
|
2011-02-09 21:47:24 +01:00
|
|
|
$task->setOwnerPHID($new);
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_CCS:
|
|
|
|
$task->setCCPHIDs($new);
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_PRIORITY:
|
|
|
|
$task->setPriority($new);
|
2012-04-02 21:12:04 +02:00
|
|
|
$pri_changed = true;
|
2011-02-09 21:47:24 +01:00
|
|
|
break;
|
2011-02-17 23:32:01 +01:00
|
|
|
case ManiphestTransactionType::TYPE_ATTACH:
|
|
|
|
$task->setAttached($new);
|
|
|
|
break;
|
2011-02-20 23:15:53 +01:00
|
|
|
case ManiphestTransactionType::TYPE_TITLE:
|
|
|
|
$task->setTitle($new);
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_DESCRIPTION:
|
|
|
|
$task->setDescription($new);
|
|
|
|
break;
|
2011-02-21 05:08:16 +01:00
|
|
|
case ManiphestTransactionType::TYPE_PROJECTS:
|
|
|
|
$task->setProjectPHIDs($new);
|
|
|
|
break;
|
2011-12-24 04:03:28 +01:00
|
|
|
case ManiphestTransactionType::TYPE_AUXILIARY:
|
|
|
|
$aux_key = $transaction->getMetadataValue('aux:key');
|
|
|
|
$task->setAuxiliaryAttribute($aux_key, $new);
|
|
|
|
break;
|
2011-02-09 21:47:24 +01:00
|
|
|
default:
|
|
|
|
throw new Exception('Unknown action type.');
|
|
|
|
}
|
|
|
|
|
|
|
|
$transaction->setOldValue($old);
|
|
|
|
$transaction->setNewValue($new);
|
|
|
|
}
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
}
|
|
|
|
|
2012-04-02 21:12:04 +02:00
|
|
|
if ($pri_changed) {
|
|
|
|
$subpriority = ManiphestTransactionEditor::getNextSubpriority(
|
|
|
|
$task->getPriority(),
|
|
|
|
null);
|
|
|
|
$task->setSubpriority($subpriority);
|
|
|
|
}
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
$task->save();
|
2011-02-09 21:47:24 +01:00
|
|
|
foreach ($transactions as $transaction) {
|
|
|
|
$transaction->setTaskID($task->getID());
|
|
|
|
$transaction->save();
|
|
|
|
}
|
2011-02-08 19:53:59 +01:00
|
|
|
|
|
|
|
$email_to[] = $task->getOwnerPHID();
|
2011-02-09 21:47:24 +01:00
|
|
|
$email_cc = array_merge(
|
|
|
|
$email_cc,
|
|
|
|
$task->getCCPHIDs());
|
2011-02-08 19:53:59 +01:00
|
|
|
|
2011-12-17 22:27:11 +01:00
|
|
|
$this->publishFeedStory($task, $transactions);
|
|
|
|
|
2011-02-15 00:34:20 +01:00
|
|
|
// TODO: Do this offline via timeline
|
|
|
|
PhabricatorSearchManiphestIndexer::indexTask($task);
|
|
|
|
|
2011-02-09 21:47:24 +01:00
|
|
|
$this->sendEmail($task, $transactions, $email_to, $email_cc);
|
2011-02-08 19:53:59 +01:00
|
|
|
}
|
|
|
|
|
2011-05-17 00:54:41 +02:00
|
|
|
protected function getSubjectPrefix() {
|
|
|
|
return PhabricatorEnv::getEnvConfig('metamta.maniphest.subject-prefix');
|
|
|
|
}
|
|
|
|
|
2011-02-09 21:47:24 +01:00
|
|
|
private function sendEmail($task, $transactions, $email_to, $email_cc) {
|
2011-02-08 19:53:59 +01:00
|
|
|
$email_to = array_filter(array_unique($email_to));
|
|
|
|
$email_cc = array_filter(array_unique($email_cc));
|
|
|
|
|
|
|
|
$phids = array();
|
|
|
|
foreach ($transactions as $transaction) {
|
|
|
|
foreach ($transaction->extractPHIDs() as $phid) {
|
|
|
|
$phids[$phid] = true;
|
|
|
|
}
|
|
|
|
}
|
2011-05-10 01:31:26 +02:00
|
|
|
foreach ($email_to as $phid) {
|
|
|
|
$phids[$phid] = true;
|
|
|
|
}
|
|
|
|
foreach ($email_cc as $phid) {
|
|
|
|
$phids[$phid] = true;
|
|
|
|
}
|
2011-02-08 19:53:59 +01:00
|
|
|
$phids = array_keys($phids);
|
|
|
|
|
|
|
|
$handles = id(new PhabricatorObjectHandleData($phids))
|
|
|
|
->loadHandles();
|
|
|
|
|
|
|
|
$view = new ManiphestTransactionDetailView();
|
2011-02-09 21:47:24 +01:00
|
|
|
$view->setTransactionGroup($transactions);
|
2011-02-08 19:53:59 +01:00
|
|
|
$view->setHandles($handles);
|
2012-03-07 00:10:17 +01:00
|
|
|
$view->setAuxiliaryFields($this->auxiliaryFields);
|
2011-02-08 19:53:59 +01:00
|
|
|
list($action, $body) = $view->renderForEmail($with_date = false);
|
|
|
|
|
2011-12-17 22:27:11 +01:00
|
|
|
$is_create = $this->isCreate($transactions);
|
2011-02-09 21:47:24 +01:00
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
$task_uri = PhabricatorEnv::getURI('/T'.$task->getID());
|
|
|
|
|
2011-05-10 01:31:26 +02:00
|
|
|
$reply_handler = $this->buildReplyHandler($task);
|
|
|
|
|
2011-02-09 21:47:24 +01:00
|
|
|
if ($is_create) {
|
|
|
|
$body .=
|
|
|
|
"\n\n".
|
|
|
|
"TASK DESCRIPTION\n".
|
|
|
|
" ".$task->getDescription();
|
|
|
|
}
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
$body .=
|
|
|
|
"\n\n".
|
|
|
|
"TASK DETAIL\n".
|
|
|
|
" ".$task_uri."\n";
|
|
|
|
|
2011-05-10 01:31:26 +02:00
|
|
|
$reply_instructions = $reply_handler->getReplyHandlerInstructions();
|
|
|
|
if ($reply_instructions) {
|
|
|
|
$body .=
|
|
|
|
"\n".
|
|
|
|
"REPLY HANDLER ACTIONS\n".
|
|
|
|
" ".$reply_instructions."\n";
|
|
|
|
}
|
|
|
|
|
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
|
|
|
$thread_id = 'maniphest-task-'.$task->getPHID();
|
2011-05-08 09:35:29 +02:00
|
|
|
$task_id = $task->getID();
|
|
|
|
$title = $task->getTitle();
|
2011-02-10 01:13:04 +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
|
|
|
$mailtags = $this->getMailTags($transactions);
|
|
|
|
|
2011-05-10 01:31:26 +02:00
|
|
|
$template = id(new PhabricatorMetaMTAMail())
|
2012-06-11 21:21:37 +02:00
|
|
|
->setSubject("T{$task_id}: {$title}")
|
|
|
|
->setSubjectPrefix($this->getSubjectPrefix())
|
|
|
|
->setVarySubjectPrefix("[{$action}]")
|
2011-02-08 19:53:59 +01:00
|
|
|
->setFrom($transaction->getAuthorPHID())
|
2011-06-22 21:41:19 +02:00
|
|
|
->setParentMessageID($this->parentMessageID)
|
2012-06-14 05:52:10 +02:00
|
|
|
->addHeader('Thread-Topic', "T{$task_id}: ".$task->getOriginalTitle())
|
2011-05-01 07:51:25 +02:00
|
|
|
->setThreadID($thread_id, $is_create)
|
2011-02-10 01:13:04 +01:00
|
|
|
->setRelatedPHID($task->getPHID())
|
2011-10-23 21:07:37 +02:00
|
|
|
->setIsBulk(true)
|
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
|
|
|
->setMailTags($mailtags)
|
2011-05-10 01:31:26 +02:00
|
|
|
->setBody($body);
|
|
|
|
|
|
|
|
$mails = $reply_handler->multiplexMail(
|
|
|
|
$template,
|
|
|
|
array_select_keys($handles, $email_to),
|
|
|
|
array_select_keys($handles, $email_cc));
|
|
|
|
|
|
|
|
foreach ($mails as $mail) {
|
|
|
|
$mail->saveAndSend();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-16 21:31:18 +02:00
|
|
|
public function buildReplyHandler(ManiphestTask $task) {
|
2012-03-21 22:48:58 +01:00
|
|
|
$handler_object = PhabricatorEnv::newObjectFromConfig(
|
2011-05-10 01:31:26 +02:00
|
|
|
'metamta.maniphest.reply-handler');
|
|
|
|
$handler_object->setMailReceiver($task);
|
|
|
|
|
|
|
|
return $handler_object;
|
2011-02-08 19:53:59 +01:00
|
|
|
}
|
2011-12-17 22:27:11 +01:00
|
|
|
|
|
|
|
private function publishFeedStory(ManiphestTask $task, array $transactions) {
|
2012-04-03 21:10:45 +02:00
|
|
|
assert_instances_of($transactions, 'ManiphestTransaction');
|
|
|
|
|
2011-12-17 22:27:11 +01:00
|
|
|
$actions = array(ManiphestAction::ACTION_UPDATE);
|
|
|
|
$comments = null;
|
|
|
|
foreach ($transactions as $transaction) {
|
|
|
|
if ($transaction->hasComments()) {
|
|
|
|
$comments = $transaction->getComments();
|
|
|
|
}
|
2012-06-08 15:31:30 +02:00
|
|
|
$type = $transaction->getTransactionType();
|
|
|
|
switch ($type) {
|
2011-12-17 22:27:11 +01:00
|
|
|
case ManiphestTransactionType::TYPE_OWNER:
|
|
|
|
$actions[] = ManiphestAction::ACTION_ASSIGN;
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_STATUS:
|
|
|
|
if ($task->getStatus() != ManiphestTaskStatus::STATUS_OPEN) {
|
|
|
|
$actions[] = ManiphestAction::ACTION_CLOSE;
|
|
|
|
} else if ($this->isCreate($transactions)) {
|
|
|
|
$actions[] = ManiphestAction::ACTION_CREATE;
|
2012-06-08 15:31:30 +02:00
|
|
|
} else {
|
|
|
|
$actions[] = ManiphestAction::ACTION_REOPEN;
|
2011-12-17 22:27:11 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2012-06-08 20:28:15 +02:00
|
|
|
$actions[] = $type;
|
2011-12-17 22:27:11 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$action_type = ManiphestAction::selectStrongestAction($actions);
|
|
|
|
$owner_phid = $task->getOwnerPHID();
|
|
|
|
$actor_phid = head($transactions)->getAuthorPHID();
|
|
|
|
$author_phid = $task->getAuthorPHID();
|
|
|
|
|
2012-06-08 15:31:30 +02:00
|
|
|
|
2011-12-17 22:27:11 +01:00
|
|
|
id(new PhabricatorFeedStoryPublisher())
|
|
|
|
->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_MANIPHEST)
|
|
|
|
->setStoryData(array(
|
|
|
|
'taskPHID' => $task->getPHID(),
|
|
|
|
'transactionIDs' => mpull($transactions, 'getID'),
|
|
|
|
'ownerPHID' => $owner_phid,
|
|
|
|
'action' => $action_type,
|
|
|
|
'comments' => $comments,
|
|
|
|
'description' => $task->getDescription(),
|
|
|
|
))
|
|
|
|
->setStoryTime(time())
|
|
|
|
->setStoryAuthorPHID($actor_phid)
|
|
|
|
->setRelatedPHIDs(
|
|
|
|
array_merge(
|
|
|
|
array_filter(
|
|
|
|
array(
|
|
|
|
$task->getPHID(),
|
|
|
|
$author_phid,
|
|
|
|
$actor_phid,
|
|
|
|
$owner_phid,
|
|
|
|
)),
|
|
|
|
$task->getProjectPHIDs()))
|
2012-06-08 15:31:30 +02:00
|
|
|
->setPrimaryObjectPHID($task->getPHID())
|
|
|
|
->setSubscribedPHIDs(
|
|
|
|
array_merge(
|
|
|
|
array_filter(
|
|
|
|
array(
|
|
|
|
$author_phid,
|
|
|
|
$owner_phid,
|
2012-06-09 04:11:32 +02:00
|
|
|
$actor_phid)),
|
2012-06-08 15:31:30 +02:00
|
|
|
$task->getCCPHIDs()))
|
2011-12-17 22:27:11 +01:00
|
|
|
->publish();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function isCreate(array $transactions) {
|
2012-04-03 21:10:45 +02:00
|
|
|
assert_instances_of($transactions, 'ManiphestTransaction');
|
2011-12-17 22:27:11 +01:00
|
|
|
$is_create = false;
|
|
|
|
foreach ($transactions as $transaction) {
|
|
|
|
$type = $transaction->getTransactionType();
|
|
|
|
if (($type == ManiphestTransactionType::TYPE_STATUS) &&
|
|
|
|
($transaction->getOldValue() === null) &&
|
|
|
|
($transaction->getNewValue() == ManiphestTaskStatus::STATUS_OPEN)) {
|
|
|
|
$is_create = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $is_create;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
private function getMailTags(array $transactions) {
|
2012-04-03 21:10:45 +02:00
|
|
|
assert_instances_of($transactions, 'ManiphestTransaction');
|
|
|
|
|
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
|
|
|
$tags = array();
|
|
|
|
foreach ($transactions as $xaction) {
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ManiphestTransactionType::TYPE_CCS:
|
|
|
|
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_CC;
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_PROJECTS:
|
|
|
|
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_PROJECTS;
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_PRIORITY:
|
|
|
|
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_PRIORITY;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OTHER;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($xaction->hasComments()) {
|
|
|
|
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_COMMENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array_unique($tags);
|
|
|
|
}
|
|
|
|
|
2012-04-02 21:12:04 +02:00
|
|
|
public static function getNextSubpriority($pri, $sub) {
|
|
|
|
|
|
|
|
if ($sub === null) {
|
|
|
|
$next = id(new ManiphestTask())->loadOneWhere(
|
|
|
|
'priority = %d ORDER BY subpriority ASC LIMIT 1',
|
|
|
|
$pri);
|
|
|
|
if ($next) {
|
|
|
|
return $next->getSubpriority() - ((double)(2 << 16));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$next = id(new ManiphestTask())->loadOneWhere(
|
|
|
|
'priority = %d AND subpriority > %s ORDER BY subpriority ASC LIMIT 1',
|
|
|
|
$pri,
|
|
|
|
$sub);
|
|
|
|
if ($next) {
|
|
|
|
return ($sub + $next->getSubpriority()) / 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (double)(2 << 32);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
}
|