2011-02-08 10:53:59 -08:00
|
|
|
<?php
|
|
|
|
|
2011-07-04 13:04:22 -07:00
|
|
|
/**
|
|
|
|
* @group maniphest
|
|
|
|
*/
|
2012-10-10 10:18:23 -07:00
|
|
|
final class ManiphestTransactionEditor extends PhabricatorEditor {
|
2011-02-08 10:53:59 -08:00
|
|
|
|
2011-06-22 12:41:19 -07:00
|
|
|
private $parentMessageID;
|
2012-03-06 15:10:17 -08:00
|
|
|
private $auxiliaryFields = array();
|
|
|
|
|
|
|
|
public function setAuxiliaryFields(array $fields) {
|
2012-04-03 12:10:45 -07:00
|
|
|
assert_instances_of($fields, 'ManiphestAuxiliaryFieldSpecification');
|
2012-03-06 15:10:17 -08:00
|
|
|
$this->auxiliaryFields = $fields;
|
|
|
|
return $this;
|
|
|
|
}
|
2011-06-22 12:41:19 -07:00
|
|
|
|
|
|
|
public function setParentMessageID($parent_message_id) {
|
|
|
|
$this->parentMessageID = $parent_message_id;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-04-03 12:10:45 -07:00
|
|
|
public function applyTransactions(ManiphestTask $task, array $transactions) {
|
|
|
|
assert_instances_of($transactions, 'ManiphestTransaction');
|
2011-02-08 10:53:59 -08:00
|
|
|
|
|
|
|
$email_cc = $task->getCCPHIDs();
|
|
|
|
|
|
|
|
$email_to = array();
|
|
|
|
$email_to[] = $task->getOwnerPHID();
|
|
|
|
|
2012-04-02 12:12:04 -07:00
|
|
|
$pri_changed = $this->isCreate($transactions);
|
|
|
|
|
2011-02-09 12:57:38 -08:00
|
|
|
foreach ($transactions as $key => $transaction) {
|
2011-02-09 12:47:24 -08:00
|
|
|
$type = $transaction->getTransactionType();
|
|
|
|
$new = $transaction->getNewValue();
|
|
|
|
$email_to[] = $transaction->getAuthorPHID();
|
|
|
|
|
2011-07-09 16:17:36 -07:00
|
|
|
$value_is_phid_set = false;
|
|
|
|
|
2011-02-08 10:53:59 -08:00
|
|
|
switch ($type) {
|
|
|
|
case ManiphestTransactionType::TYPE_NONE:
|
2011-02-09 12:47:24 -08:00
|
|
|
$old = null;
|
2011-02-08 10:53:59 -08:00
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_STATUS:
|
2011-02-09 12:47:24 -08:00
|
|
|
$old = $task->getStatus();
|
2011-02-08 10:53:59 -08:00
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_OWNER:
|
2011-02-09 12:47:24 -08:00
|
|
|
$old = $task->getOwnerPHID();
|
2011-02-08 10:53:59 -08:00
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_CCS:
|
2011-02-09 12:47:24 -08:00
|
|
|
$old = $task->getCCPHIDs();
|
2011-07-09 16:17:36 -07:00
|
|
|
$value_is_phid_set = true;
|
2011-02-08 10:53:59 -08:00
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_PRIORITY:
|
2011-02-09 12:47:24 -08:00
|
|
|
$old = $task->getPriority();
|
2011-02-08 10:53:59 -08:00
|
|
|
break;
|
2012-07-02 15:42:06 -07:00
|
|
|
case ManiphestTransactionType::TYPE_EDGE:
|
|
|
|
$old = $transaction->getOldValue();
|
|
|
|
break;
|
2011-02-17 14:32:01 -08:00
|
|
|
case ManiphestTransactionType::TYPE_ATTACH:
|
|
|
|
$old = $task->getAttached();
|
|
|
|
break;
|
2011-02-20 14:15:53 -08:00
|
|
|
case ManiphestTransactionType::TYPE_TITLE:
|
|
|
|
$old = $task->getTitle();
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_DESCRIPTION:
|
|
|
|
$old = $task->getDescription();
|
|
|
|
break;
|
2011-02-20 20:08:16 -08:00
|
|
|
case ManiphestTransactionType::TYPE_PROJECTS:
|
|
|
|
$old = $task->getProjectPHIDs();
|
2011-07-09 16:17:36 -07:00
|
|
|
$value_is_phid_set = true;
|
2011-02-20 20:08:16 -08:00
|
|
|
break;
|
2011-12-23 19:03:28 -08: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 10:53:59 -08:00
|
|
|
default:
|
|
|
|
throw new Exception('Unknown action type.');
|
|
|
|
}
|
|
|
|
|
2011-07-09 16:17:36 -07: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 12:57:38 -08: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 12:47:24 -08: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-26 18:50:17 -07:00
|
|
|
if ($new) {
|
|
|
|
$handles = id(new PhabricatorObjectHandleData(array($new)))
|
2013-02-28 17:15:09 -08:00
|
|
|
->setViewer($this->getActor())
|
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-26 18:50:17 -07:00
|
|
|
->loadHandles();
|
|
|
|
$task->setOwnerOrdering($handles[$new]->getName());
|
|
|
|
} else {
|
|
|
|
$task->setOwnerOrdering(null);
|
|
|
|
}
|
2011-02-09 12:47:24 -08:00
|
|
|
$task->setOwnerPHID($new);
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_CCS:
|
|
|
|
$task->setCCPHIDs($new);
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_PRIORITY:
|
|
|
|
$task->setPriority($new);
|
2012-04-02 12:12:04 -07:00
|
|
|
$pri_changed = true;
|
2011-02-09 12:47:24 -08:00
|
|
|
break;
|
2011-02-17 14:32:01 -08:00
|
|
|
case ManiphestTransactionType::TYPE_ATTACH:
|
|
|
|
$task->setAttached($new);
|
|
|
|
break;
|
2011-02-20 14:15:53 -08:00
|
|
|
case ManiphestTransactionType::TYPE_TITLE:
|
|
|
|
$task->setTitle($new);
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_DESCRIPTION:
|
|
|
|
$task->setDescription($new);
|
|
|
|
break;
|
2011-02-20 20:08:16 -08:00
|
|
|
case ManiphestTransactionType::TYPE_PROJECTS:
|
|
|
|
$task->setProjectPHIDs($new);
|
|
|
|
break;
|
2011-12-23 19:03:28 -08:00
|
|
|
case ManiphestTransactionType::TYPE_AUXILIARY:
|
|
|
|
$aux_key = $transaction->getMetadataValue('aux:key');
|
|
|
|
$task->setAuxiliaryAttribute($aux_key, $new);
|
|
|
|
break;
|
2012-07-02 15:42:06 -07:00
|
|
|
case ManiphestTransactionType::TYPE_EDGE:
|
|
|
|
// Edge edits are accomplished through PhabricatorEdgeEditor, which
|
|
|
|
// has authority.
|
|
|
|
break;
|
2011-02-09 12:47:24 -08:00
|
|
|
default:
|
|
|
|
throw new Exception('Unknown action type.');
|
|
|
|
}
|
|
|
|
|
|
|
|
$transaction->setOldValue($old);
|
|
|
|
$transaction->setNewValue($new);
|
|
|
|
}
|
|
|
|
|
2011-02-08 10:53:59 -08:00
|
|
|
}
|
|
|
|
|
2012-04-02 12:12:04 -07:00
|
|
|
if ($pri_changed) {
|
|
|
|
$subpriority = ManiphestTransactionEditor::getNextSubpriority(
|
|
|
|
$task->getPriority(),
|
|
|
|
null);
|
|
|
|
$task->setSubpriority($subpriority);
|
|
|
|
}
|
|
|
|
|
2011-02-08 10:53:59 -08:00
|
|
|
$task->save();
|
2011-02-09 12:47:24 -08:00
|
|
|
foreach ($transactions as $transaction) {
|
|
|
|
$transaction->setTaskID($task->getID());
|
|
|
|
$transaction->save();
|
|
|
|
}
|
2011-02-08 10:53:59 -08:00
|
|
|
|
|
|
|
$email_to[] = $task->getOwnerPHID();
|
2011-02-09 12:47:24 -08:00
|
|
|
$email_cc = array_merge(
|
|
|
|
$email_cc,
|
|
|
|
$task->getCCPHIDs());
|
2011-02-08 10:53:59 -08:00
|
|
|
|
2012-10-23 12:02:40 -07:00
|
|
|
$mail = $this->sendEmail($task, $transactions, $email_to, $email_cc);
|
2011-12-17 13:27:11 -08:00
|
|
|
|
2012-10-23 12:02:40 -07:00
|
|
|
$this->publishFeedStory(
|
|
|
|
$task,
|
|
|
|
$transactions,
|
|
|
|
$mail->buildRecipientList());
|
|
|
|
|
Improve Search architecture
Summary:
The search indexing API has several problems right now:
- Always runs in-process.
- It would be nice to push this into the task queue for performance. However, the API currently passses an object all the way through (and some indexers depend on preloaded object attributes), so it can't be dumped into the task queue at any stage since we can't serialize it.
- Being able to use the task queue will also make rebuilding indexes faster.
- Instead, make the API phid-oriented.
- No uniform indexing API.
- Each "Editor" currently calls SomeCustomIndexer::indexThing(). This won't work with AbstractTransactions. The API is also just weird.
- Instead, provide a uniform API.
- No uniform CLI.
- We have `scripts/search/reindex_everything.php`, but it doesn't actually index everything. Each new document type needs to be separately added to it, leading to stuff like D3839. Third-party applications can't provide indexers.
- Instead, let indexers expose documents for indexing.
- Not application-oriented.
- All the indexers live in search/ right now, which isn't the right organization in an application-orietned view of the world.
- Instead, move indexers to applications and load them with SymbolLoader.
Test Plan:
- `bin/search index`
- Indexed one revision, one task.
- Indexed `--type TASK`, `--type DREV`, etc., for all types.
- Indexed `--all`.
- Added the word "saboteur" to a revision, task, wiki page, and question and then searched for it.
- Creating users is a pain; searched for a user after indexing.
- Creating commits is a pain; searched for a commit after indexing.
- Mocks aren't currently loadable in the result view, so their indexing is moot.
Reviewers: btrahan, vrana
Reviewed By: btrahan
CC: 20after4, aran
Maniphest Tasks: T1991, T2104
Differential Revision: https://secure.phabricator.com/D4261
2012-12-21 14:21:31 -08:00
|
|
|
id(new PhabricatorSearchIndexer())
|
|
|
|
->indexDocumentByPHID($task->getPHID());
|
2011-02-08 10:53:59 -08:00
|
|
|
}
|
|
|
|
|
2011-05-16 15:54:41 -07:00
|
|
|
protected function getSubjectPrefix() {
|
|
|
|
return PhabricatorEnv::getEnvConfig('metamta.maniphest.subject-prefix');
|
|
|
|
}
|
|
|
|
|
2011-02-09 12:47:24 -08:00
|
|
|
private function sendEmail($task, $transactions, $email_to, $email_cc) {
|
2011-02-08 10:53:59 -08: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-09 16:31:26 -07:00
|
|
|
foreach ($email_to as $phid) {
|
|
|
|
$phids[$phid] = true;
|
|
|
|
}
|
|
|
|
foreach ($email_cc as $phid) {
|
|
|
|
$phids[$phid] = true;
|
|
|
|
}
|
2011-02-08 10:53:59 -08:00
|
|
|
$phids = array_keys($phids);
|
|
|
|
|
|
|
|
$handles = id(new PhabricatorObjectHandleData($phids))
|
2013-02-27 11:43:57 -08:00
|
|
|
->setViewer($this->getActor())
|
2011-02-08 10:53:59 -08:00
|
|
|
->loadHandles();
|
|
|
|
|
|
|
|
$view = new ManiphestTransactionDetailView();
|
2011-02-09 12:47:24 -08:00
|
|
|
$view->setTransactionGroup($transactions);
|
2011-02-08 10:53:59 -08:00
|
|
|
$view->setHandles($handles);
|
2012-03-06 15:10:17 -08:00
|
|
|
$view->setAuxiliaryFields($this->auxiliaryFields);
|
2012-07-16 19:01:43 -07:00
|
|
|
list($action, $main_body) = $view->renderForEmail($with_date = false);
|
2011-02-08 10:53:59 -08:00
|
|
|
|
2011-12-17 13:27:11 -08:00
|
|
|
$is_create = $this->isCreate($transactions);
|
2011-02-09 12:47:24 -08:00
|
|
|
|
2012-11-02 13:43:14 -07:00
|
|
|
$task_uri = PhabricatorEnv::getProductionURI('/T'.$task->getID());
|
2011-02-08 10:53:59 -08:00
|
|
|
|
2011-05-09 16:31:26 -07:00
|
|
|
$reply_handler = $this->buildReplyHandler($task);
|
|
|
|
|
2012-07-16 19:01:43 -07:00
|
|
|
$body = new PhabricatorMetaMTAMailBody();
|
|
|
|
$body->addRawSection($main_body);
|
2011-02-09 12:47:24 -08:00
|
|
|
if ($is_create) {
|
2012-07-16 19:01:43 -07:00
|
|
|
$body->addTextSection(pht('TASK DESCRIPTION'), $task->getDescription());
|
2011-05-09 16:31:26 -07:00
|
|
|
}
|
2012-07-16 19:01:43 -07:00
|
|
|
$body->addTextSection(pht('TASK DETAIL'), $task_uri);
|
|
|
|
$body->addReplySection($reply_handler->getReplyHandlerInstructions());
|
2011-05-09 16:31:26 -07: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 09:31:03 -07:00
|
|
|
$thread_id = 'maniphest-task-'.$task->getPHID();
|
2011-05-08 00:35:29 -07:00
|
|
|
$task_id = $task->getID();
|
|
|
|
$title = $task->getTitle();
|
2011-02-09 16:13:04 -08: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-17 22:57:07 -08:00
|
|
|
$mailtags = $this->getMailTags($transactions);
|
|
|
|
|
2011-05-09 16:31:26 -07:00
|
|
|
$template = id(new PhabricatorMetaMTAMail())
|
2012-06-11 12:21:37 -07:00
|
|
|
->setSubject("T{$task_id}: {$title}")
|
|
|
|
->setSubjectPrefix($this->getSubjectPrefix())
|
|
|
|
->setVarySubjectPrefix("[{$action}]")
|
2011-02-08 10:53:59 -08:00
|
|
|
->setFrom($transaction->getAuthorPHID())
|
2011-06-22 12:41:19 -07:00
|
|
|
->setParentMessageID($this->parentMessageID)
|
2012-06-13 20:52:10 -07:00
|
|
|
->addHeader('Thread-Topic', "T{$task_id}: ".$task->getOriginalTitle())
|
2011-04-30 22:51:25 -07:00
|
|
|
->setThreadID($thread_id, $is_create)
|
2011-02-09 16:13:04 -08:00
|
|
|
->setRelatedPHID($task->getPHID())
|
2012-10-10 10:18:23 -07:00
|
|
|
->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs())
|
2011-10-23 12:07:37 -07: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-17 22:57:07 -08:00
|
|
|
->setMailTags($mailtags)
|
2012-07-16 19:01:43 -07:00
|
|
|
->setBody($body->render());
|
2011-05-09 16:31:26 -07:00
|
|
|
|
|
|
|
$mails = $reply_handler->multiplexMail(
|
|
|
|
$template,
|
|
|
|
array_select_keys($handles, $email_to),
|
|
|
|
array_select_keys($handles, $email_cc));
|
|
|
|
|
|
|
|
foreach ($mails as $mail) {
|
|
|
|
$mail->saveAndSend();
|
|
|
|
}
|
2012-10-23 12:02:40 -07:00
|
|
|
|
|
|
|
$template->addTos($email_to);
|
|
|
|
$template->addCCs($email_cc);
|
|
|
|
return $template;
|
2011-05-09 16:31:26 -07:00
|
|
|
}
|
|
|
|
|
2011-05-16 12:31:18 -07:00
|
|
|
public function buildReplyHandler(ManiphestTask $task) {
|
2012-03-21 14:48:58 -07:00
|
|
|
$handler_object = PhabricatorEnv::newObjectFromConfig(
|
2011-05-09 16:31:26 -07:00
|
|
|
'metamta.maniphest.reply-handler');
|
|
|
|
$handler_object->setMailReceiver($task);
|
|
|
|
|
|
|
|
return $handler_object;
|
2011-02-08 10:53:59 -08:00
|
|
|
}
|
2011-12-17 13:27:11 -08:00
|
|
|
|
2012-10-23 12:02:40 -07:00
|
|
|
private function publishFeedStory(
|
|
|
|
ManiphestTask $task,
|
|
|
|
array $transactions,
|
|
|
|
array $mailed_phids) {
|
2012-04-03 12:10:45 -07:00
|
|
|
assert_instances_of($transactions, 'ManiphestTransaction');
|
|
|
|
|
2011-12-17 13:27:11 -08:00
|
|
|
$actions = array(ManiphestAction::ACTION_UPDATE);
|
|
|
|
$comments = null;
|
|
|
|
foreach ($transactions as $transaction) {
|
|
|
|
if ($transaction->hasComments()) {
|
|
|
|
$comments = $transaction->getComments();
|
|
|
|
}
|
2012-06-08 06:31:30 -07:00
|
|
|
$type = $transaction->getTransactionType();
|
|
|
|
switch ($type) {
|
2011-12-17 13:27:11 -08: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 06:31:30 -07:00
|
|
|
} else {
|
|
|
|
$actions[] = ManiphestAction::ACTION_REOPEN;
|
2011-12-17 13:27:11 -08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2012-06-08 11:28:15 -07:00
|
|
|
$actions[] = $type;
|
2011-12-17 13:27:11 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$action_type = ManiphestAction::selectStrongestAction($actions);
|
|
|
|
$owner_phid = $task->getOwnerPHID();
|
|
|
|
$actor_phid = head($transactions)->getAuthorPHID();
|
|
|
|
$author_phid = $task->getAuthorPHID();
|
|
|
|
|
|
|
|
id(new PhabricatorFeedStoryPublisher())
|
2012-10-23 12:02:40 -07:00
|
|
|
->setStoryType('PhabricatorFeedStoryManiphest')
|
2011-12-17 13:27:11 -08:00
|
|
|
->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 06:31:30 -07:00
|
|
|
->setPrimaryObjectPHID($task->getPHID())
|
|
|
|
->setSubscribedPHIDs(
|
|
|
|
array_merge(
|
|
|
|
array_filter(
|
|
|
|
array(
|
|
|
|
$author_phid,
|
|
|
|
$owner_phid,
|
2012-06-08 19:11:32 -07:00
|
|
|
$actor_phid)),
|
2012-06-08 06:31:30 -07:00
|
|
|
$task->getCCPHIDs()))
|
2012-10-23 12:02:40 -07:00
|
|
|
->setMailRecipientPHIDs($mailed_phids)
|
2011-12-17 13:27:11 -08:00
|
|
|
->publish();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function isCreate(array $transactions) {
|
2012-04-03 12:10:45 -07:00
|
|
|
assert_instances_of($transactions, 'ManiphestTransaction');
|
2011-12-17 13:27:11 -08: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-17 22:57:07 -08:00
|
|
|
private function getMailTags(array $transactions) {
|
2012-04-03 12:10:45 -07: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-17 22:57:07 -08:00
|
|
|
$tags = array();
|
|
|
|
foreach ($transactions as $xaction) {
|
|
|
|
switch ($xaction->getTransactionType()) {
|
2012-10-31 17:11:04 -07:00
|
|
|
case ManiphestTransactionType::TYPE_STATUS:
|
|
|
|
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_STATUS;
|
|
|
|
break;
|
|
|
|
case ManiphestTransactionType::TYPE_OWNER:
|
|
|
|
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OWNER;
|
|
|
|
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-17 22:57:07 -08:00
|
|
|
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;
|
2012-10-31 17:11:04 -07:00
|
|
|
case ManiphestTransactionType::TYPE_NONE:
|
|
|
|
// this is a comment which we will check separately below for
|
|
|
|
// content
|
|
|
|
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-17 22:57:07 -08:00
|
|
|
default:
|
|
|
|
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OTHER;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($xaction->hasComments()) {
|
|
|
|
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_COMMENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array_unique($tags);
|
|
|
|
}
|
|
|
|
|
2012-04-02 12:12:04 -07: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);
|
|
|
|
}
|
|
|
|
|
2013-05-03 15:47:39 -07:00
|
|
|
public static function addCC(
|
|
|
|
ManiphestTask $task,
|
|
|
|
PhabricatorUser $user) {
|
|
|
|
$current_ccs = $task->getCCPHIDs();
|
|
|
|
$new_ccs = array_merge($current_ccs, array($user->getPHID()));
|
|
|
|
|
|
|
|
$transaction = new ManiphestTransaction();
|
|
|
|
$transaction->setTaskID($task->getID());
|
|
|
|
$transaction->setAuthorPHID($user->getPHID());
|
|
|
|
$transaction->setTransactionType(ManiphestTransactionType::TYPE_CCS);
|
|
|
|
$transaction->setNewValue(array_unique($new_ccs));
|
|
|
|
$transaction->setOldValue($current_ccs);
|
|
|
|
|
|
|
|
id(new ManiphestTransactionEditor())
|
|
|
|
->setActor($user)
|
|
|
|
->applyTransactions($task, array($transaction));
|
|
|
|
}
|
2012-04-02 12:12:04 -07:00
|
|
|
|
2013-05-03 15:47:39 -07:00
|
|
|
public static function removeCC(
|
|
|
|
ManiphestTask $task,
|
|
|
|
PhabricatorUser $user) {
|
|
|
|
$current_ccs = $task->getCCPHIDs();
|
|
|
|
$new_ccs = array_diff($current_ccs, array($user->getPHID()));
|
|
|
|
|
|
|
|
$transaction = new ManiphestTransaction();
|
|
|
|
$transaction->setTaskID($task->getID());
|
|
|
|
$transaction->setAuthorPHID($user->getPHID());
|
|
|
|
$transaction->setTransactionType(ManiphestTransactionType::TYPE_CCS);
|
|
|
|
$transaction->setNewValue(array_unique($new_ccs));
|
|
|
|
$transaction->setOldValue($current_ccs);
|
|
|
|
|
|
|
|
id(new ManiphestTransactionEditor())
|
|
|
|
->setActor($user)
|
|
|
|
->applyTransactions($task, array($transaction));
|
|
|
|
}
|
2011-02-08 10:53:59 -08:00
|
|
|
}
|