diff --git a/src/applications/differential/mail/DifferentialCommentMail.php b/src/applications/differential/mail/DifferentialCommentMail.php index ead0bbf583..5ee13d3278 100644 --- a/src/applications/differential/mail/DifferentialCommentMail.php +++ b/src/applications/differential/mail/DifferentialCommentMail.php @@ -1,198 +1,213 @@ changedByCommit = $changed_by_commit; return $this; } public function getChangedByCommit() { return $this->changedByCommit; } public function __construct( DifferentialRevision $revision, PhabricatorObjectHandle $actor, DifferentialComment $comment, array $changesets, array $inline_comments) { assert_instances_of($changesets, 'DifferentialChangeset'); assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface'); $this->setRevision($revision); $this->setActorHandle($actor); $this->setComment($comment); $this->setChangesets($changesets); $this->setInlineComments($inline_comments); } protected function getMailTags() { + $tags = array(); $comment = $this->getComment(); - $action = $comment->getAction(); + $action = $comment->getAction(); - $tags = array(); switch ($action) { case DifferentialAction::ACTION_ADDCCS: $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_CC; break; case DifferentialAction::ACTION_CLOSE: $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_CLOSED; break; + case DifferentialAction::ACTION_ADDREVIEWERS: + $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEWERS; + break; + case DifferentialAction::ACTION_UPDATE: + $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_UPDATED; + break; + case DifferentialAction::ACTION_REQUEST: + $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEW_REQUEST; + break; + case DifferentialAction::ACTION_COMMENT: + // this is a comment which we will check separately below for content + break; + default: + $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_OTHER; + break; } if (strlen(trim($comment->getContent()))) { switch ($action) { case DifferentialAction::ACTION_CLOSE: // Commit comments are auto-generated and not especially interesting, // so don't tag them as having a comment. break; default: $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_COMMENT; break; } } return $tags; } protected function renderVaryPrefix() { $verb = ucwords($this->getVerb()); return "[{$verb}]"; } protected function getVerb() { $comment = $this->getComment(); $action = $comment->getAction(); $verb = DifferentialAction::getActionPastTenseVerb($action); return $verb; } protected function prepareBody() { parent::prepareBody(); // If the commented added reviewers or CCs, list them explicitly. $meta = $this->getComment()->getMetadata(); $m_reviewers = idx( $meta, DifferentialComment::METADATA_ADDED_REVIEWERS, array()); $m_cc = idx( $meta, DifferentialComment::METADATA_ADDED_CCS, array()); $load = array_merge($m_reviewers, $m_cc); if ($load) { $handles = id(new PhabricatorObjectHandleData($load))->loadHandles(); if ($m_reviewers) { $this->addedReviewers = $this->renderHandleList($handles, $m_reviewers); } if ($m_cc) { $this->addedCCs = $this->renderHandleList($handles, $m_cc); } } } protected function renderBody() { $comment = $this->getComment(); $actor = $this->getActorName(); $name = $this->getRevision()->getTitle(); $verb = $this->getVerb(); $body = array(); $body[] = "{$actor} has {$verb} the revision \"{$name}\"."; if ($this->addedReviewers) { $body[] = 'Added Reviewers: '.$this->addedReviewers; } if ($this->addedCCs) { $body[] = 'Added CCs: '.$this->addedCCs; } $body[] = null; $content = $comment->getContent(); if (strlen($content)) { $body[] = $this->formatText($content); $body[] = null; } if ($this->getChangedByCommit()) { $body[] = 'CHANGED PRIOR TO COMMIT'; $body[] = ' '.$this->getChangedByCommit(); $body[] = null; } $inlines = $this->getInlineComments(); if ($inlines) { $body[] = 'INLINE COMMENTS'; $changesets = $this->getChangesets(); if (PhabricatorEnv::getEnvConfig( 'metamta.differential.unified-comment-context', false)) { foreach ($changesets as $changeset) { $changeset->attachHunks($changeset->loadHunks()); } } foreach ($inlines as $inline) { $changeset = $changesets[$inline->getChangesetID()]; if (!$changeset) { throw new Exception('Changeset missing!'); } $file = $changeset->getFilename(); $start = $inline->getLineNumber(); $len = $inline->getLineLength(); if ($len) { $range = $start.'-'.($start + $len); } else { $range = $start; } $inline_content = $inline->getContent(); if (!PhabricatorEnv::getEnvConfig( 'metamta.differential.unified-comment-context', false)) { $body[] = $this->formatText("{$file}:{$range} {$inline_content}"); } else { $body[] = "================"; $body[] = "Comment at: " . $file . ":" . $range; $body[] = $changeset->makeContextDiff($inline, 1); $body[] = "----------------"; $body[] = $inline_content; $body[] = null; } } $body[] = null; } $body[] = $this->renderAuxFields(DifferentialMailPhase::COMMENT); return implode("\n", $body); } } diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index 72ad7f1b89..c307afdf33 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -1,458 +1,468 @@ auxiliaryFields = $fields; return $this; } public function setParentMessageID($parent_message_id) { $this->parentMessageID = $parent_message_id; return $this; } public function setExcludePHIDs(array $exclude) { $this->excludePHIDs = $exclude; return $this; } public function getExcludePHIDs() { return $this->excludePHIDs; } public function applyTransactions(ManiphestTask $task, array $transactions) { assert_instances_of($transactions, 'ManiphestTransaction'); $email_cc = $task->getCCPHIDs(); $email_to = array(); $email_to[] = $task->getOwnerPHID(); $pri_changed = $this->isCreate($transactions); foreach ($transactions as $key => $transaction) { $type = $transaction->getTransactionType(); $new = $transaction->getNewValue(); $email_to[] = $transaction->getAuthorPHID(); $value_is_phid_set = false; switch ($type) { case ManiphestTransactionType::TYPE_NONE: $old = null; break; case ManiphestTransactionType::TYPE_STATUS: $old = $task->getStatus(); break; case ManiphestTransactionType::TYPE_OWNER: $old = $task->getOwnerPHID(); break; case ManiphestTransactionType::TYPE_CCS: $old = $task->getCCPHIDs(); $value_is_phid_set = true; break; case ManiphestTransactionType::TYPE_PRIORITY: $old = $task->getPriority(); break; case ManiphestTransactionType::TYPE_EDGE: $old = $transaction->getOldValue(); break; case ManiphestTransactionType::TYPE_ATTACH: $old = $task->getAttached(); break; case ManiphestTransactionType::TYPE_TITLE: $old = $task->getTitle(); break; case ManiphestTransactionType::TYPE_DESCRIPTION: $old = $task->getDescription(); break; case ManiphestTransactionType::TYPE_PROJECTS: $old = $task->getProjectPHIDs(); $value_is_phid_set = true; break; 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; default: throw new Exception('Unknown action type.'); } $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)) { 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); } } else { switch ($type) { case ManiphestTransactionType::TYPE_NONE: break; case ManiphestTransactionType::TYPE_STATUS: $task->setStatus($new); break; case ManiphestTransactionType::TYPE_OWNER: if ($new) { $handles = id(new PhabricatorObjectHandleData(array($new))) ->loadHandles(); $task->setOwnerOrdering($handles[$new]->getName()); } else { $task->setOwnerOrdering(null); } $task->setOwnerPHID($new); break; case ManiphestTransactionType::TYPE_CCS: $task->setCCPHIDs($new); break; case ManiphestTransactionType::TYPE_PRIORITY: $task->setPriority($new); $pri_changed = true; break; case ManiphestTransactionType::TYPE_ATTACH: $task->setAttached($new); break; case ManiphestTransactionType::TYPE_TITLE: $task->setTitle($new); break; case ManiphestTransactionType::TYPE_DESCRIPTION: $task->setDescription($new); break; case ManiphestTransactionType::TYPE_PROJECTS: $task->setProjectPHIDs($new); break; case ManiphestTransactionType::TYPE_AUXILIARY: $aux_key = $transaction->getMetadataValue('aux:key'); $task->setAuxiliaryAttribute($aux_key, $new); break; case ManiphestTransactionType::TYPE_EDGE: // Edge edits are accomplished through PhabricatorEdgeEditor, which // has authority. break; default: throw new Exception('Unknown action type.'); } $transaction->setOldValue($old); $transaction->setNewValue($new); } } if ($pri_changed) { $subpriority = ManiphestTransactionEditor::getNextSubpriority( $task->getPriority(), null); $task->setSubpriority($subpriority); } $task->save(); foreach ($transactions as $transaction) { $transaction->setTaskID($task->getID()); $transaction->save(); } $email_to[] = $task->getOwnerPHID(); $email_cc = array_merge( $email_cc, $task->getCCPHIDs()); $mail = $this->sendEmail($task, $transactions, $email_to, $email_cc); $this->publishFeedStory( $task, $transactions, $mail->buildRecipientList()); // TODO: Do this offline via workers PhabricatorSearchManiphestIndexer::indexTask($task); } protected function getSubjectPrefix() { return PhabricatorEnv::getEnvConfig('metamta.maniphest.subject-prefix'); } private function sendEmail($task, $transactions, $email_to, $email_cc) { $exclude = $this->getExcludePHIDs(); $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; } } foreach ($email_to as $phid) { $phids[$phid] = true; } foreach ($email_cc as $phid) { $phids[$phid] = true; } $phids = array_keys($phids); $handles = id(new PhabricatorObjectHandleData($phids)) ->loadHandles(); $view = new ManiphestTransactionDetailView(); $view->setTransactionGroup($transactions); $view->setHandles($handles); $view->setAuxiliaryFields($this->auxiliaryFields); list($action, $main_body) = $view->renderForEmail($with_date = false); $is_create = $this->isCreate($transactions); $task_uri = PhabricatorEnv::getURI('/T'.$task->getID()); $reply_handler = $this->buildReplyHandler($task); $body = new PhabricatorMetaMTAMailBody(); $body->addRawSection($main_body); if ($is_create) { $body->addTextSection(pht('TASK DESCRIPTION'), $task->getDescription()); } $body->addTextSection(pht('TASK DETAIL'), $task_uri); $body->addReplySection($reply_handler->getReplyHandlerInstructions()); $thread_id = 'maniphest-task-'.$task->getPHID(); $task_id = $task->getID(); $title = $task->getTitle(); $mailtags = $this->getMailTags($transactions); $template = id(new PhabricatorMetaMTAMail()) ->setSubject("T{$task_id}: {$title}") ->setSubjectPrefix($this->getSubjectPrefix()) ->setVarySubjectPrefix("[{$action}]") ->setFrom($transaction->getAuthorPHID()) ->setParentMessageID($this->parentMessageID) ->addHeader('Thread-Topic', "T{$task_id}: ".$task->getOriginalTitle()) ->setThreadID($thread_id, $is_create) ->setRelatedPHID($task->getPHID()) ->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs()) ->setIsBulk(true) ->setMailTags($mailtags) ->setBody($body->render()); $mails = $reply_handler->multiplexMail( $template, array_select_keys($handles, $email_to), array_select_keys($handles, $email_cc)); foreach ($mails as $mail) { $mail->saveAndSend(); } $template->addTos($email_to); $template->addCCs($email_cc); return $template; } public function buildReplyHandler(ManiphestTask $task) { $handler_object = PhabricatorEnv::newObjectFromConfig( 'metamta.maniphest.reply-handler'); $handler_object->setMailReceiver($task); return $handler_object; } private function publishFeedStory( ManiphestTask $task, array $transactions, array $mailed_phids) { assert_instances_of($transactions, 'ManiphestTransaction'); $actions = array(ManiphestAction::ACTION_UPDATE); $comments = null; foreach ($transactions as $transaction) { if ($transaction->hasComments()) { $comments = $transaction->getComments(); } $type = $transaction->getTransactionType(); switch ($type) { 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; } else { $actions[] = ManiphestAction::ACTION_REOPEN; } break; default: $actions[] = $type; break; } } $action_type = ManiphestAction::selectStrongestAction($actions); $owner_phid = $task->getOwnerPHID(); $actor_phid = head($transactions)->getAuthorPHID(); $author_phid = $task->getAuthorPHID(); id(new PhabricatorFeedStoryPublisher()) ->setStoryType('PhabricatorFeedStoryManiphest') ->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())) ->setPrimaryObjectPHID($task->getPHID()) ->setSubscribedPHIDs( array_merge( array_filter( array( $author_phid, $owner_phid, $actor_phid)), $task->getCCPHIDs())) ->setMailRecipientPHIDs($mailed_phids) ->publish(); } private function isCreate(array $transactions) { assert_instances_of($transactions, 'ManiphestTransaction'); $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; } private function getMailTags(array $transactions) { assert_instances_of($transactions, 'ManiphestTransaction'); $tags = array(); foreach ($transactions as $xaction) { switch ($xaction->getTransactionType()) { + case ManiphestTransactionType::TYPE_STATUS: + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_STATUS; + break; + case ManiphestTransactionType::TYPE_OWNER: + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OWNER; + break; 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; + case ManiphestTransactionType::TYPE_NONE: + // this is a comment which we will check separately below for + // content + break; default: $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OTHER; break; } if ($xaction->hasComments()) { $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_COMMENT; } } return array_unique($tags); } 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); } } diff --git a/src/applications/metamta/constants/MetaMTANotificationType.php b/src/applications/metamta/constants/MetaMTANotificationType.php index c73ee3d9f6..c9af0d600c 100644 --- a/src/applications/metamta/constants/MetaMTANotificationType.php +++ b/src/applications/metamta/constants/MetaMTANotificationType.php @@ -1,32 +1,38 @@ getUser(); $preferences = $user->loadPreferences(); $pref_re_prefix = PhabricatorUserPreferences::PREFERENCE_RE_PREFIX; $pref_vary = PhabricatorUserPreferences::PREFERENCE_VARY_SUBJECT; $pref_no_self_mail = PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL; $errors = array(); if ($request->isFormPost()) { if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) { if ($request->getStr($pref_re_prefix) == 'default') { $preferences->unsetPreference($pref_re_prefix); } else { $preferences->setPreference( $pref_re_prefix, $request->getBool($pref_re_prefix)); } if ($request->getStr($pref_vary) == 'default') { $preferences->unsetPreference($pref_vary); } else { $preferences->setPreference( $pref_vary, $request->getBool($pref_vary)); } } $preferences->setPreference( $pref_no_self_mail, $request->getStr($pref_no_self_mail)); $new_tags = $request->getArr('mailtags'); $mailtags = $preferences->getPreference('mailtags', array()); foreach ($this->getMailTags() as $key => $label) { $mailtags[$key] = (bool)idx($new_tags, $key, false); } $preferences->setPreference('mailtags', $mailtags); $preferences->save(); return id(new AphrontRedirectResponse()) ->setURI($this->getPanelURI('?saved=true')); } $notice = null; if (!$errors) { if ($request->getStr('saved')) { $notice = new AphrontErrorView(); $notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE); $notice->setTitle('Changes Saved'); $notice->appendChild('

Your changes have been saved.

'); } } else { $notice = new AphrontErrorView(); $notice->setTitle('Form Errors'); $notice->setErrors($errors); } $re_prefix_default = PhabricatorEnv::getEnvConfig('metamta.re-prefix') ? 'Enabled' : 'Disabled'; $vary_default = PhabricatorEnv::getEnvConfig('metamta.vary-subjects') ? 'Vary' : 'Do Not Vary'; $re_prefix_value = $preferences->getPreference($pref_re_prefix); if ($re_prefix_value === null) { $re_prefix_value = 'default'; } else { $re_prefix_value = $re_prefix_value ? 'true' : 'false'; } $vary_value = $preferences->getPreference($pref_vary); if ($vary_value === null) { $vary_value = 'default'; } else { $vary_value = $vary_value ? 'true' : 'false'; } $form = new AphrontFormView(); $form ->setUser($user) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Self Actions') ->setName($pref_no_self_mail) ->setOptions( array( '0' => 'Send me an email when I take an action', '1' => 'Do not send me an email when I take an action', )) ->setCaption('You can disable email about your own actions.') ->setValue($preferences->getPreference($pref_no_self_mail, 0))); if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) { $re_control = id(new AphrontFormSelectControl()) ->setName($pref_re_prefix) ->setOptions( array( 'default' => 'Use Server Default ('.$re_prefix_default.')', 'true' => 'Enable "Re:" prefix', 'false' => 'Disable "Re:" prefix', )) ->setValue($re_prefix_value); $vary_control = id(new AphrontFormSelectControl()) ->setName($pref_vary) ->setOptions( array( 'default' => 'Use Server Default ('.$vary_default.')', 'true' => 'Vary Subjects', 'false' => 'Do Not Vary Subjects', )) ->setValue($vary_value); } else { $re_control = id(new AphrontFormStaticControl()) ->setValue('Server Default ('.$re_prefix_default.')'); $vary_control = id(new AphrontFormStaticControl()) ->setValue('Server Default ('.$vary_default.')'); } $form ->appendChild( $re_control ->setLabel('Add "Re:" Prefix') ->setCaption( 'Enable this option to fix threading in Mail.app on OS X Lion, '. 'or if you like "Re:" in your email subjects.')) ->appendChild( $vary_control ->setLabel('Vary Subjects') ->setCaption( 'This option adds more information to email subjects, but may '. 'break threading in some clients.')); $form ->appendChild( '
'. '

'. 'You can customize what mail you receive from Phabricator here.'. '

'. '

'. 'NOTE: If an update makes several changes (like '. 'adding CCs to a task, closing it, and adding a comment) you will '. 'still receive an email as long as at least one of the changes '. 'is set to notify you.'. '

' ); $mailtags = $preferences->getPreference('mailtags', array()); $form ->appendChild( $this->buildMailTagCheckboxes( $this->getDifferentialMailTags(), $mailtags) ->setLabel('Differential')) ->appendChild( $this->buildMailTagCheckboxes( $this->getManiphestMailTags(), $mailtags) ->setLabel('Maniphest')); $form ->appendChild( id(new AphrontFormSubmitControl()) ->setValue('Save Preferences')); $panel = new AphrontPanelView(); $panel->setHeader('Email Preferences'); $panel->setWidth(AphrontPanelView::WIDTH_FORM); $panel->appendChild($form); return id(new AphrontNullView()) ->appendChild( array( $notice, $panel, )); } private function getMailTags() { return array( - MetaMTANotificationType::TYPE_DIFFERENTIAL_CC => - "Send me email when a revision's CCs change.", + MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEWERS => + pht("Send me email when a revision's reviewers change."), MetaMTANotificationType::TYPE_DIFFERENTIAL_CLOSED => - "Send me email when a revision is closed.", - MetaMTANotificationType::TYPE_MANIPHEST_PROJECTS => - "Send me email when a task's associated projects change.", + pht("Send me email when a revision is closed."), + MetaMTANotificationType::TYPE_DIFFERENTIAL_CC => + pht("Send me email when a revision's CCs change."), + MetaMTANotificationType::TYPE_DIFFERENTIAL_COMMENT => + pht("Send me email when a revision is commented on."), + MetaMTANotificationType::TYPE_DIFFERENTIAL_UPDATED => + pht("Send me email when a revision is updated."), + MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEW_REQUEST => + pht("Send me email when I am requested to review a revision."), + MetaMTANotificationType::TYPE_DIFFERENTIAL_OTHER => + pht("Send me email for any other activity not listed above."), + + MetaMTANotificationType::TYPE_MANIPHEST_STATUS => + pht("Send me email when a task's status changes."), + MetaMTANotificationType::TYPE_MANIPHEST_OWNER => + pht("Send me email when a task's owner changes."), MetaMTANotificationType::TYPE_MANIPHEST_PRIORITY => - "Send me email when a task's priority changes.", + pht("Send me email when a task's priority changes."), MetaMTANotificationType::TYPE_MANIPHEST_CC => - "Send me email when a task's CCs change.", + pht("Send me email when a task's CCs change."), + MetaMTANotificationType::TYPE_MANIPHEST_PROJECTS => + pht("Send me email when a task's associated projects change."), + MetaMTANotificationType::TYPE_MANIPHEST_COMMENT => + pht("Send me email when a task is commented on."), + MetaMTANotificationType::TYPE_MANIPHEST_OTHER => + pht("Send me email for any other activity not listed above."), + ); } private function getManiphestMailTags() { return array_select_keys( $this->getMailTags(), array( - MetaMTANotificationType::TYPE_MANIPHEST_PROJECTS, + MetaMTANotificationType::TYPE_MANIPHEST_STATUS, + MetaMTANotificationType::TYPE_MANIPHEST_OWNER, MetaMTANotificationType::TYPE_MANIPHEST_PRIORITY, MetaMTANotificationType::TYPE_MANIPHEST_CC, + MetaMTANotificationType::TYPE_MANIPHEST_PROJECTS, + MetaMTANotificationType::TYPE_MANIPHEST_COMMENT, + MetaMTANotificationType::TYPE_MANIPHEST_OTHER, )); } private function getDifferentialMailTags() { return array_select_keys( $this->getMailTags(), array( - MetaMTANotificationType::TYPE_DIFFERENTIAL_CC, + MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEWERS, MetaMTANotificationType::TYPE_DIFFERENTIAL_CLOSED, + MetaMTANotificationType::TYPE_DIFFERENTIAL_CC, + MetaMTANotificationType::TYPE_DIFFERENTIAL_COMMENT, + MetaMTANotificationType::TYPE_DIFFERENTIAL_UPDATED, + MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEW_REQUEST, + MetaMTANotificationType::TYPE_DIFFERENTIAL_OTHER, )); } private function buildMailTagCheckboxes( array $tags, array $prefs) { $control = new AphrontFormCheckboxControl(); foreach ($tags as $key => $label) { $control->addCheckbox( 'mailtags['.$key.']', 1, $label, idx($prefs, $key, 1)); } return $control; } }