@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Move Maniphest to modular mail commands

Summary:
Ref T7199. This fully modularizes mail command handling in Maniphest.

I had to add a couple of minor not-totally-solid-feeling tricks to deal with the "create" case, but they feel not-too-bad, and a million times better than what came before.

Test Plan: Used all commands with `receive-test`.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7199

Differential Revision: https://secure.phabricator.com/D12240

+155 -142
+9 -1
src/__phutil_library_map__.php
··· 1000 1000 'MacroConduitAPIMethod' => 'applications/macro/conduit/MacroConduitAPIMethod.php', 1001 1001 'MacroCreateMemeConduitAPIMethod' => 'applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php', 1002 1002 'MacroQueryConduitAPIMethod' => 'applications/macro/conduit/MacroQueryConduitAPIMethod.php', 1003 + 'ManiphestAssignEmailCommand' => 'applications/maniphest/command/ManiphestAssignEmailCommand.php', 1003 1004 'ManiphestBatchEditController' => 'applications/maniphest/controller/ManiphestBatchEditController.php', 1004 1005 'ManiphestBulkEditCapability' => 'applications/maniphest/capability/ManiphestBulkEditCapability.php', 1006 + 'ManiphestClaimEmailCommand' => 'applications/maniphest/command/ManiphestClaimEmailCommand.php', 1007 + 'ManiphestCloseEmailCommand' => 'applications/maniphest/command/ManiphestCloseEmailCommand.php', 1005 1008 'ManiphestConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestConduitAPIMethod.php', 1006 1009 'ManiphestConfiguredCustomField' => 'applications/maniphest/field/ManiphestConfiguredCustomField.php', 1007 1010 'ManiphestConstants' => 'applications/maniphest/constants/ManiphestConstants.php', ··· 1022 1025 'ManiphestEditPriorityCapability' => 'applications/maniphest/capability/ManiphestEditPriorityCapability.php', 1023 1026 'ManiphestEditProjectsCapability' => 'applications/maniphest/capability/ManiphestEditProjectsCapability.php', 1024 1027 'ManiphestEditStatusCapability' => 'applications/maniphest/capability/ManiphestEditStatusCapability.php', 1028 + 'ManiphestEmailCommand' => 'applications/maniphest/command/ManiphestEmailCommand.php', 1025 1029 'ManiphestExcelDefaultFormat' => 'applications/maniphest/export/ManiphestExcelDefaultFormat.php', 1026 1030 'ManiphestExcelFormat' => 'applications/maniphest/export/ManiphestExcelFormat.php', 1027 1031 'ManiphestExportController' => 'applications/maniphest/controller/ManiphestExportController.php', ··· 4241 4245 'MacroConduitAPIMethod' => 'ConduitAPIMethod', 4242 4246 'MacroCreateMemeConduitAPIMethod' => 'MacroConduitAPIMethod', 4243 4247 'MacroQueryConduitAPIMethod' => 'MacroConduitAPIMethod', 4248 + 'ManiphestAssignEmailCommand' => 'ManiphestEmailCommand', 4244 4249 'ManiphestBatchEditController' => 'ManiphestController', 4245 4250 'ManiphestBulkEditCapability' => 'PhabricatorPolicyCapability', 4251 + 'ManiphestClaimEmailCommand' => 'ManiphestEmailCommand', 4252 + 'ManiphestCloseEmailCommand' => 'ManiphestEmailCommand', 4246 4253 'ManiphestConduitAPIMethod' => 'ConduitAPIMethod', 4247 4254 'ManiphestConfiguredCustomField' => array( 4248 4255 'ManiphestCustomField', ··· 4265 4272 'ManiphestEditPriorityCapability' => 'PhabricatorPolicyCapability', 4266 4273 'ManiphestEditProjectsCapability' => 'PhabricatorPolicyCapability', 4267 4274 'ManiphestEditStatusCapability' => 'PhabricatorPolicyCapability', 4275 + 'ManiphestEmailCommand' => 'MetaMTAEmailTransactionCommand', 4268 4276 'ManiphestExcelDefaultFormat' => 'ManiphestExcelFormat', 4269 4277 'ManiphestExportController' => 'ManiphestController', 4270 4278 'ManiphestGetTaskTransactionsConduitAPIMethod' => 'ManiphestConduitAPIMethod', ··· 4275 4283 'ManiphestQueryConduitAPIMethod' => 'ManiphestConduitAPIMethod', 4276 4284 'ManiphestQueryStatusesConduitAPIMethod' => 'ManiphestConduitAPIMethod', 4277 4285 'ManiphestRemarkupRule' => 'PhabricatorObjectRemarkupRule', 4278 - 'ManiphestReplyHandler' => 'PhabricatorMailReplyHandler', 4286 + 'ManiphestReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 4279 4287 'ManiphestReportController' => 'ManiphestController', 4280 4288 'ManiphestSchemaSpec' => 'PhabricatorConfigSchemaSpec', 4281 4289 'ManiphestSearchIndexer' => 'PhabricatorSearchDocumentIndexer',
+42
src/applications/maniphest/command/ManiphestAssignEmailCommand.php
··· 1 + <?php 2 + 3 + final class ManiphestAssignEmailCommand 4 + extends ManiphestEmailCommand { 5 + 6 + public function getCommand() { 7 + return 'assign'; 8 + } 9 + 10 + public function buildTransactions( 11 + PhabricatorUser $viewer, 12 + PhabricatorApplicationTransactionInterface $object, 13 + PhabricatorMetaMTAReceivedMail $mail, 14 + $command, 15 + array $argv) { 16 + $xactions = array(); 17 + 18 + 19 + $assign_to = head($argv); 20 + if ($assign_to) { 21 + $assign_user = id(new PhabricatorPeopleQuery()) 22 + ->setViewer($viewer) 23 + ->withUsernames(array($assign_to)) 24 + ->executeOne(); 25 + if ($assign_user) { 26 + $assign_phid = $assign_user->getPHID(); 27 + } 28 + } 29 + 30 + // Treat bad "!assign" like "!claim". 31 + if (!$assign_phid) { 32 + $assign_phid = $viewer->getPHID(); 33 + } 34 + 35 + $xactions[] = $object->getApplicationTransactionTemplate() 36 + ->setTransactionType(ManiphestTransaction::TYPE_OWNER) 37 + ->setNewValue($assign_phid); 38 + 39 + return $xactions; 40 + } 41 + 42 + }
+25
src/applications/maniphest/command/ManiphestClaimEmailCommand.php
··· 1 + <?php 2 + 3 + final class ManiphestClaimEmailCommand 4 + extends ManiphestEmailCommand { 5 + 6 + public function getCommand() { 7 + return 'claim'; 8 + } 9 + 10 + public function buildTransactions( 11 + PhabricatorUser $viewer, 12 + PhabricatorApplicationTransactionInterface $object, 13 + PhabricatorMetaMTAReceivedMail $mail, 14 + $command, 15 + array $argv) { 16 + $xactions = array(); 17 + 18 + $xactions[] = $object->getApplicationTransactionTemplate() 19 + ->setTransactionType(ManiphestTransaction::TYPE_OWNER) 20 + ->setNewValue($viewer->getPHID()); 21 + 22 + return $xactions; 23 + } 24 + 25 + }
+25
src/applications/maniphest/command/ManiphestCloseEmailCommand.php
··· 1 + <?php 2 + 3 + final class ManiphestCloseEmailCommand 4 + extends ManiphestEmailCommand { 5 + 6 + public function getCommand() { 7 + return 'close'; 8 + } 9 + 10 + public function buildTransactions( 11 + PhabricatorUser $viewer, 12 + PhabricatorApplicationTransactionInterface $object, 13 + PhabricatorMetaMTAReceivedMail $mail, 14 + $command, 15 + array $argv) { 16 + $xactions = array(); 17 + 18 + $xactions[] = $object->getApplicationTransactionTemplate() 19 + ->setTransactionType(ManiphestTransaction::TYPE_STATUS) 20 + ->setNewValue(ManiphestTaskStatus::getDefaultClosedStatus()); 21 + 22 + return $xactions; 23 + } 24 + 25 + }
+11
src/applications/maniphest/command/ManiphestEmailCommand.php
··· 1 + <?php 2 + 3 + abstract class ManiphestEmailCommand 4 + extends MetaMTAEmailTransactionCommand { 5 + 6 + public function isCommandSupportedForObject( 7 + PhabricatorApplicationTransactionInterface $object) { 8 + return ($object instanceof ManiphestTask); 9 + } 10 + 11 + }
+15 -129
src/applications/maniphest/mail/ManiphestReplyHandler.php
··· 1 1 <?php 2 2 3 - final class ManiphestReplyHandler extends PhabricatorMailReplyHandler { 3 + final class ManiphestReplyHandler 4 + extends PhabricatorApplicationTransactionReplyHandler { 4 5 5 6 public function validateMailReceiver($mail_receiver) { 6 7 if (!($mail_receiver instanceof ManiphestTask)) { ··· 8 9 } 9 10 } 10 11 11 - public function getPrivateReplyHandlerEmailAddress( 12 - PhabricatorObjectHandle $handle) { 13 - return $this->getDefaultPrivateReplyHandlerEmailAddress($handle, 'T'); 14 - } 15 - 16 - public function getPublicReplyHandlerEmailAddress() { 17 - return $this->getDefaultPublicReplyHandlerEmailAddress('T'); 12 + public function getObjectPrefix() { 13 + return 'T'; 18 14 } 19 15 20 - protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) { 21 - // NOTE: We'll drop in here on both the "reply to a task" and "create a 22 - // new task" workflows! Make sure you test both if you make changes! 23 - $task = $this->getMailReceiver(); 24 - $viewer = $this->getActor(); 25 - 26 - $is_new_task = !$task->getID(); 27 - 28 - $body_data = $mail->parseBody(); 29 - $body = $body_data['body']; 30 - $body = $this->enhanceBodyWithAttachments($body, $mail->getAttachments()); 31 - 32 - $content_source = PhabricatorContentSource::newForSource( 33 - PhabricatorContentSource::SOURCE_EMAIL, 34 - array( 35 - 'id' => $mail->getID(), 36 - )); 16 + protected function didReceiveMail( 17 + PhabricatorMetaMTAReceivedMail $mail, 18 + $body) { 37 19 38 - $template = new ManiphestTransaction(); 20 + $object = $this->getMailReceiver(); 21 + $is_new = !$object->getID(); 39 22 40 23 $xactions = array(); 41 - if ($is_new_task) { 42 - $xactions[] = id(clone $template) 24 + 25 + if ($is_new) { 26 + $xactions[] = $object->getApplicationTransactionTemplate() 43 27 ->setTransactionType(ManiphestTransaction::TYPE_TITLE) 44 28 ->setNewValue(nonempty($mail->getSubject(), pht('Untitled Task'))); 45 29 46 - $xactions[] = id(clone $template) 30 + $xactions[] = $object->getApplicationTransactionTemplate() 47 31 ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION) 48 32 ->setNewValue($body); 49 - } else { 50 - $xactions[] = id(clone $template) 51 - ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) 52 - ->attachComment( 53 - id(new ManiphestTransactionComment()) 54 - ->setContent($body)); 55 33 } 56 34 57 - $commands = $body_data['commands']; 58 - foreach ($commands as $command_argv) { 59 - $command = head($command_argv); 60 - $args = array_slice($command_argv, 1); 61 - switch ($command) { 62 - case 'close': 63 - $xactions[] = id(clone $template) 64 - ->setTransactionType(ManiphestTransaction::TYPE_STATUS) 65 - ->setNewValue(ManiphestTaskStatus::getDefaultClosedStatus()); 66 - break; 67 - case 'claim': 68 - $xactions[] = id(clone $template) 69 - ->setTransactionType(ManiphestTransaction::TYPE_OWNER) 70 - ->setNewValue($viewer->getPHID()); 71 - break; 72 - case 'assign': 73 - $assign_to = head($args); 74 - if ($assign_to) { 75 - $assign_user = id(new PhabricatorPeopleQuery()) 76 - ->setViewer($viewer) 77 - ->withUsernames(array($assign_to)) 78 - ->executeOne(); 79 - if ($assign_user) { 80 - $assign_phid = $assign_user->getPHID(); 81 - } 82 - } 83 - 84 - // Treat bad "!assign" like "!claim". 85 - if (!$assign_phid) { 86 - $assign_phid = $viewer->getPHID(); 87 - } 88 - 89 - $xactions[] = id(clone $template) 90 - ->setTransactionType(ManiphestTransaction::TYPE_OWNER) 91 - ->setNewValue($assign_phid); 92 - break; 93 - case 'unsubscribe': 94 - $xactions[] = id(clone $template) 95 - ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) 96 - ->setNewValue( 97 - array( 98 - '-' => array($viewer->getPHID()), 99 - )); 100 - break; 101 - } 102 - } 103 - 104 - $ccs = $mail->loadCCPHIDs(); 105 - if ($ccs) { 106 - $xactions[] = id(clone $template) 107 - ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) 108 - ->setNewValue( 109 - array( 110 - '+' => array($viewer->getPHID()), 111 - )); 112 - } 113 - 114 - $event = new PhabricatorEvent( 115 - PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK, 116 - array( 117 - 'task' => $task, 118 - 'mail' => $mail, 119 - 'new' => $is_new_task, 120 - 'transactions' => $xactions, 121 - )); 122 - $event->setUser($viewer); 123 - PhutilEventEngine::dispatchEvent($event); 124 - 125 - $task = $event->getValue('task'); 126 - $xactions = $event->getValue('transactions'); 127 - 128 - $editor = id(new ManiphestTransactionEditor()) 129 - ->setActor($viewer) 130 - ->setParentMessageID($mail->getMessageID()) 131 - ->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs()) 132 - ->setContinueOnNoEffect(true) 133 - ->setContinueOnMissingFields(true) 134 - ->setContentSource($content_source); 135 - 136 - if ($this->getApplicationEmail()) { 137 - $editor->setApplicationEmail($this->getApplicationEmail()); 138 - } 139 - 140 - $editor->applyTransactions($task, $xactions); 141 - 142 - $event = new PhabricatorEvent( 143 - PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK, 144 - array( 145 - 'task' => $task, 146 - 'new' => $is_new_task, 147 - 'transactions' => $xactions, 148 - )); 149 - $event->setUser($viewer); 150 - PhutilEventEngine::dispatchEvent($event); 35 + return $xactions; 151 36 } 37 + 152 38 153 39 }
+28 -12
src/applications/transactions/replyhandler/PhabricatorApplicationTransactionReplyHandler.php
··· 43 43 return $this->getMailReceiver()->getApplicationTransactionTemplate(); 44 44 } 45 45 46 + protected function didReceiveMail( 47 + PhabricatorMetaMTAReceivedMail $mail, 48 + $body) { 49 + return array(); 50 + } 51 + 52 + protected function shouldCreateCommentFromMailBody() { 53 + return (bool)$this->getMailReceiver()->getID(); 54 + } 55 + 46 56 final protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) { 47 57 $viewer = $this->getActor(); 48 58 $object = $this->getMailReceiver(); 49 59 50 60 $body_data = $mail->parseBody(); 61 + $body = $body_data['body']; 62 + $body = $this->enhanceBodyWithAttachments($body, $mail->getAttachments()); 51 63 52 - $xactions = $this->processMailCommands( 53 - $mail, 54 - $body_data['commands']); 64 + $xactions = $this->didReceiveMail($mail, $body); 55 65 56 66 // If this object is subscribable, subscribe all the users who were 57 67 // CC'd on the message. ··· 67 77 } 68 78 } 69 79 70 - $body = $body_data['body']; 71 - $body = $this->enhanceBodyWithAttachments($body, $mail->getAttachments()); 80 + $command_xactions = $this->processMailCommands( 81 + $mail, 82 + $body_data['commands']); 83 + foreach ($command_xactions as $xaction) { 84 + $xactions[] = $xaction; 85 + } 72 86 73 - $comment = $this 74 - ->newTransaction() 75 - ->getApplicationTransactionCommentObject() 76 - ->setContent($body); 87 + if ($this->shouldCreateCommentFromMailBody()) { 88 + $comment = $this 89 + ->newTransaction() 90 + ->getApplicationTransactionCommentObject() 91 + ->setContent($body); 77 92 78 - $xactions[] = $this->newTransaction() 79 - ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) 80 - ->attachComment($comment); 93 + $xactions[] = $this->newTransaction() 94 + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) 95 + ->attachComment($comment); 96 + } 81 97 82 98 $target = $object->getApplicationTransactionObject(); 83 99