diff --git a/src/applications/maniphest/controller/ManiphestSubscribeController.php b/src/applications/maniphest/controller/ManiphestSubscribeController.php index 46e7aef934..83e46ababc 100644 --- a/src/applications/maniphest/controller/ManiphestSubscribeController.php +++ b/src/applications/maniphest/controller/ManiphestSubscribeController.php @@ -1,40 +1,48 @@ id = $data['id']; $this->action = $data['action']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $task = id(new ManiphestTask())->load($this->id); if (!$task) { return new Aphront404Response(); } + $ccs = $task->getCCPHIDs(); switch ($this->action) { case 'add': - ManiphestTransactionEditor::addCC( - $task, - $user); + $ccs[] = $user->getPHID(); break; case 'rem': - ManiphestTransactionEditor::removeCC( - $task, - $user); + $ccs = array_diff($ccs, array($user->getPHID())); break; default: return new Aphront400Response(); } + $xaction = id(new ManiphestTransactionPro()) + ->setTransactionType(ManiphestTransactionPro::TYPE_CCS) + ->setNewValue($ccs); + + $editor = id(new ManiphestTransactionEditorPro()) + ->setActor($user) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($task, array($xaction)); + return id(new AphrontRedirectResponse())->setURI('/T'.$task->getID()); } } diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index f3ae5876fa..0051aaaf68 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -1,510 +1,38 @@ auxiliaryFields = $fields; - return $this; - } - - public function setParentMessageID($parent_message_id) { - $this->parentMessageID = $parent_message_id; - return $this; - } - - 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); - $aux_writes = array(); - - foreach ($transactions as $key => $transaction) { - $type = $transaction->getTransactionType(); - $new = $transaction->getNewValue(); - $email_to[] = $transaction->getAuthorPHID(); - - $value_is_phid_set = false; - - switch ($type) { - case PhabricatorTransactions::TYPE_COMMENT: - $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 PhabricatorTransactions::TYPE_CUSTOMFIELD: - $aux_key = $transaction->getMetadataValue('customfield:key'); - if (!$aux_key) { - throw new Exception( - "Expected 'customfield:key' metadata on TYPE_CUSTOMFIELD ". - "transaction."); - } - // This has already been populated. - $old = $transaction->getOldValue(); - 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( - PhabricatorTransactions::TYPE_COMMENT); - } - } else { - switch ($type) { - case PhabricatorTransactions::TYPE_COMMENT: - break; - case ManiphestTransactionType::TYPE_STATUS: - $task->setStatus($new); - break; - case ManiphestTransactionType::TYPE_OWNER: - if ($new) { - $handle = id(new PhabricatorHandleQuery()) - ->setViewer($this->getActor()) - ->withPHIDs(array($new)) - ->executeOne(); - $task->setOwnerOrdering($handle->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 PhabricatorTransactions::TYPE_CUSTOMFIELD: - $aux_key = $transaction->getMetadataValue('customfield:key'); - $aux_writes[$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(); - - if ($aux_writes) { - ManiphestAuxiliaryFieldSpecification::writeLegacyAuxiliaryUpdates( - $task, - $aux_writes); - } - - foreach ($transactions as $transaction) { - $transaction->setTransactionTask($task); - $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()); - - id(new PhabricatorSearchIndexer()) - ->indexDocumentByPHID($task->getPHID()); - - $fields = PhabricatorCustomField::getObjectFields( - $task, - PhabricatorCustomField::ROLE_APPLICATIONSEARCH); - $fields->readFieldsFromStorage($task); - $fields->rebuildIndexes($task); - - } - - protected function getSubjectPrefix() { - return PhabricatorEnv::getEnvConfig('metamta.maniphest.subject-prefix'); - } - - private function sendEmail($task, $transactions, $email_to, $email_cc) { - $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 PhabricatorHandleQuery()) - ->setViewer($this->getActor()) - ->withPHIDs($phids) - ->execute(); - - $main_body = array(); - foreach ($transactions as $transaction) { - $main_body[] = id(clone $transaction->getModernTransaction()) - ->attachViewer($this->getActor()) - ->setHandles($handles) - ->setRenderingTarget('text') - ->getTitle(); - } - - foreach ($transactions as $transaction) { - if ($transaction->getComments()) { - $main_body[] = null; - $main_body[] = $transaction->getComments(); - } - } - - $main_body = implode("\n", $main_body); - - $action = head($transactions)->getModernTransaction()->getActionName(); - - $is_create = $this->isCreate($transactions); - - $task_uri = PhabricatorEnv::getProductionURI('/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 PhabricatorTransactions::TYPE_COMMENT: - // 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); } - 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->setTransactionTask($task); - $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)); - } - - 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->setTransactionTask($task); - $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)); - } } diff --git a/src/applications/maniphest/storage/ManiphestTransactionPro.php b/src/applications/maniphest/storage/ManiphestTransactionPro.php index 2c2b1ded30..bda8e7bab2 100644 --- a/src/applications/maniphest/storage/ManiphestTransactionPro.php +++ b/src/applications/maniphest/storage/ManiphestTransactionPro.php @@ -1,419 +1,447 @@ getNewValue(); $old = $this->getOldValue(); switch ($this->getTransactionType()) { case self::TYPE_OWNER: if ($new) { $phids[] = $new; } if ($old) { $phids[] = $old; } break; case self::TYPE_CCS: case self::TYPE_PROJECTS: $phids = array_mergev( array( $phids, nonempty($old, array()), nonempty($new, array()), )); break; case self::TYPE_EDGE: $phids = array_mergev( array( $phids, array_keys(nonempty($old, array())), array_keys(nonempty($new, array())), )); break; case self::TYPE_ATTACH: $old = nonempty($old, array()); $new = nonempty($new, array()); $phids = array_mergev( array( $phids, array_keys(idx($new, 'FILE', array())), array_keys(idx($old, 'FILE', array())), )); break; } return $phids; } public function getColor() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_STATUS: if ($new == ManiphestTaskStatus::STATUS_OPEN) { if ($old) { return 'violet'; } else { return 'green'; } } else { return 'black'; } case self::TYPE_PRIORITY: if ($old == ManiphestTaskPriority::getDefaultPriority()) { return 'green'; } else if ($old > $new) { return 'grey'; } else { return 'yellow'; } } return parent::getColor(); } public function getActionName() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_TITLE: return pht('Retitled'); case self::TYPE_STATUS: if ($new == ManiphestTaskStatus::STATUS_OPEN) { if ($old) { return pht('Reopened'); } else { return pht('Created'); } } else { switch ($new) { case ManiphestTaskStatus::STATUS_CLOSED_SPITE: return pht('Spited'); case ManiphestTaskStatus::STATUS_CLOSED_DUPLICATE: return pht('Merged'); default: return pht('Closed'); } } case self::TYPE_DESCRIPTION: return pht('Edited'); case self::TYPE_OWNER: if ($this->getAuthorPHID() == $new) { return pht('Claimed'); } else if (!$new) { return pht('Up For Grabs'); } else if (!$old) { return pht('Assigned'); } else { return pht('Reassigned'); } case self::TYPE_CCS: return pht('Changed CC'); case self::TYPE_PROJECTS: return pht('Changed Projects'); case self::TYPE_PRIORITY: if ($old == ManiphestTaskPriority::getDefaultPriority()) { return pht('Triaged'); } else if ($old > $new) { return pht('Lowered Priority'); } else { return pht('Raised Priority'); } case self::TYPE_EDGE: case self::TYPE_ATTACH: return pht('Attached'); } return parent::getActionName(); } public function getIcon() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_TITLE: return 'edit'; case self::TYPE_STATUS: if ($new == ManiphestTaskStatus::STATUS_OPEN) { return 'create'; } else { switch ($new) { case ManiphestTaskStatus::STATUS_CLOSED_SPITE: return 'dislike'; case ManiphestTaskStatus::STATUS_CLOSED_DUPLICATE: return 'delete'; default: return 'check'; } } case self::TYPE_DESCRIPTION: return 'edit'; case self::TYPE_PROJECTS: return 'tag'; case self::TYPE_PRIORITY: if ($old == ManiphestTaskPriority::getDefaultPriority()) { return 'start-sandcastle'; return pht('Triaged'); } else if ($old > $new) { return 'download-alt'; } else { return 'upload'; } case self::TYPE_EDGE: case self::TYPE_ATTACH: return 'attach'; } return parent::getIcon(); } public function getTitle() { $author_phid = $this->getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_TITLE: return pht( '%s changed the title from "%s" to "%s".', $this->renderHandleLink($author_phid), $old, $new); case self::TYPE_DESCRIPTION: return pht( '%s edited the task description.', $this->renderHandleLink($author_phid)); case self::TYPE_STATUS: if ($new == ManiphestTaskStatus::STATUS_OPEN) { if ($old) { return pht( '%s reopened this task.', $this->renderHandleLink($author_phid)); } else { return pht( '%s created this task.', $this->renderHandleLink($author_phid)); } } else { switch ($new) { case ManiphestTaskStatus::STATUS_CLOSED_SPITE: return pht( '%s closed this task out of spite.', $this->renderHandleLink($author_phid)); case ManiphestTaskStatus::STATUS_CLOSED_DUPLICATE: return pht( '%s closed this task as a duplicate.', $this->renderHandleLink($author_phid)); default: $status_name = idx( ManiphestTaskStatus::getTaskStatusMap(), $new, '???'); return pht( '%s closed this task as "%s".', $this->renderHandleLink($author_phid), $status_name); } } case self::TYPE_OWNER: if ($author_phid == $new) { return pht( '%s claimed this task.', $this->renderHandleLink($author_phid)); } else if (!$new) { return pht( '%s placed this task up for grabs.', $this->renderHandleLink($author_phid)); } else if (!$old) { return pht( '%s assigned this task to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($new)); } else { return pht( '%s reassigned this task from %s to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($old), $this->renderHandleLink($new)); } case self::TYPE_PROJECTS: $added = array_diff($new, $old); $removed = array_diff($old, $new); if ($added && !$removed) { return pht( '%s added %d project(s): %s', $this->renderHandleLink($author_phid), count($added), $this->renderHandleList($added)); } else if ($removed && !$added) { return pht( '%s removed %d project(s): %s', $this->renderHandleLink($author_phid), count($removed), $this->renderHandleList($removed)); } else if ($removed && $added) { return pht( '%s changed projects, added %d: %s; removed %d: %s', $this->renderHandleLink($author_phid), count($added), $this->renderHandleList($added), count($removed), $this->renderHandleList($removed)); } else { // This is hit when rendering previews. return pht( '%s changed projects...', $this->renderHandleLink($author_phid)); } case self::TYPE_PRIORITY: $old_name = ManiphestTaskPriority::getTaskPriorityName($old); $new_name = ManiphestTaskPriority::getTaskPriorityName($new); if ($old == ManiphestTaskPriority::getDefaultPriority()) { return pht( '%s triaged this task as "%s" priority.', $this->renderHandleLink($author_phid), $new_name); } else if ($old > $new) { return pht( '%s lowered the priority of this task from "%s" to "%s".', $this->renderHandleLink($author_phid), $old_name, $new_name); } else { return pht( '%s raised the priority of this task from "%s" to "%s".', $this->renderHandleLink($author_phid), $old_name, $new_name); } case self::TYPE_CCS: // TODO: Remove this when we switch to subscribers. Just reuse the // code in the parent. $clone = clone $this; $clone->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS); return $clone->getTitle(); case self::TYPE_EDGE: // TODO: Remove this when we switch to real edges. Just reuse the // code in the parent; $clone = clone $this; $clone->setTransactionType(PhabricatorTransactions::TYPE_EDGE); return $clone->getTitle(); case self::TYPE_ATTACH: $old = nonempty($old, array()); $new = nonempty($new, array()); $new = array_keys(idx($new, 'FILE', array())); $old = array_keys(idx($old, 'FILE', array())); $added = array_diff($new, $old); $removed = array_diff($old, $new); if ($added && !$removed) { return pht( '%s attached %d file(s): %s', $this->renderHandleLink($author_phid), count($added), $this->renderHandleList($added)); } else if ($removed && !$added) { return pht( '%s detached %d file(s): %s', $this->renderHandleLink($author_phid), count($removed), $this->renderHandleList($removed)); } else { return pht( '%s changed projects, attached %d: %s; detached %d: %s', $this->renderHandleLink($author_phid), count($added), $this->renderHandleList($added), count($removed), $this->renderHandleList($removed)); } } return parent::getTitle(); } public function hasChangeDetails() { switch ($this->getTransactionType()) { case self::TYPE_DESCRIPTION: return true; } return parent::hasChangeDetails(); } public function renderChangeDetails(PhabricatorUser $viewer) { $old = $this->getOldValue(); $new = $this->getNewValue(); $view = id(new PhabricatorApplicationTransactionTextDiffDetailView()) ->setUser($viewer) ->setOldText($old) ->setNewText($new); return $view->render(); } + public function getMailTags() { + $tags = array(); + switch ($this->getTransactionType()) { + case self::TYPE_STATUS: + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_STATUS; + break; + case self::TYPE_OWNER: + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OWNER; + break; + case self::TYPE_CCS: + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_CC; + break; + case self::TYPE_PROJECTS: + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_PROJECTS; + break; + case self::TYPE_PRIORITY: + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_PRIORITY; + break; + case self::TYPE_COMMENT: + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_COMMENT; + break; + default: + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OTHER; + break; + } + return $tags; + } + }