@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.

Fully modularize mail commands

Summary: Ref T7199. Everyone can have a mail command! You can have a mail command! You can have a mail command! Mail commands for everyone!

Test Plan: Used `bin/mail receive-test` to issue commands against files and pastes.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7199

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

+175 -40
+4
src/__phutil_library_map__.php
··· 1069 1069 'ManiphestUpdateConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php', 1070 1070 'ManiphestView' => 'applications/maniphest/view/ManiphestView.php', 1071 1071 'MetaMTAConstants' => 'applications/metamta/constants/MetaMTAConstants.php', 1072 + 'MetaMTAEmailTransactionCommand' => 'applications/metamta/command/MetaMTAEmailTransactionCommand.php', 1072 1073 'MetaMTAMailReceivedGarbageCollector' => 'applications/metamta/garbagecollector/MetaMTAMailReceivedGarbageCollector.php', 1073 1074 'MetaMTAMailSentGarbageCollector' => 'applications/metamta/garbagecollector/MetaMTAMailSentGarbageCollector.php', 1074 1075 'MetaMTANotificationType' => 'applications/metamta/constants/MetaMTANotificationType.php', ··· 2544 2545 'PhabricatorSubscriptionsListController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsListController.php', 2545 2546 'PhabricatorSubscriptionsTransactionController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsTransactionController.php', 2546 2547 'PhabricatorSubscriptionsUIEventListener' => 'applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php', 2548 + 'PhabricatorSubscriptionsUnsubscribeEmailCommand' => 'applications/subscriptions/command/PhabricatorSubscriptionsUnsubscribeEmailCommand.php', 2547 2549 'PhabricatorSupportApplication' => 'applications/support/application/PhabricatorSupportApplication.php', 2548 2550 'PhabricatorSyntaxHighlighter' => 'infrastructure/markup/PhabricatorSyntaxHighlighter.php', 2549 2551 'PhabricatorSyntaxHighlightingConfigOptions' => 'applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php', ··· 4320 4322 'ManiphestTransactionSaveController' => 'ManiphestController', 4321 4323 'ManiphestUpdateConduitAPIMethod' => 'ManiphestConduitAPIMethod', 4322 4324 'ManiphestView' => 'AphrontView', 4325 + 'MetaMTAEmailTransactionCommand' => 'Phobject', 4323 4326 'MetaMTAMailReceivedGarbageCollector' => 'PhabricatorGarbageCollector', 4324 4327 'MetaMTAMailSentGarbageCollector' => 'PhabricatorGarbageCollector', 4325 4328 'MetaMTANotificationType' => 'MetaMTAConstants', ··· 5921 5924 'PhabricatorSubscriptionsListController' => 'PhabricatorController', 5922 5925 'PhabricatorSubscriptionsTransactionController' => 'PhabricatorController', 5923 5926 'PhabricatorSubscriptionsUIEventListener' => 'PhabricatorEventListener', 5927 + 'PhabricatorSubscriptionsUnsubscribeEmailCommand' => 'MetaMTAEmailTransactionCommand', 5924 5928 'PhabricatorSupportApplication' => 'PhabricatorApplication', 5925 5929 'PhabricatorSyntaxHighlightingConfigOptions' => 'PhabricatorApplicationConfigOptions', 5926 5930 'PhabricatorSystemActionEngine' => 'Phobject',
-18
src/applications/files/mail/FileReplyHandler.php
··· 13 13 return 'F'; 14 14 } 15 15 16 - protected function processMailCommands(array $commands) { 17 - $actor = $this->getActor(); 18 - 19 - $xactions = array(); 20 - foreach ($commands as $command) { 21 - switch (head($command)) { 22 - case 'unsubscribe': 23 - $xaction = id(new PhabricatorFileTransaction()) 24 - ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) 25 - ->setNewValue(array('-' => array($actor->getPHID()))); 26 - $xactions[] = $xaction; 27 - break; 28 - } 29 - } 30 - 31 - return $xactions; 32 - } 33 - 34 16 }
+81
src/applications/metamta/command/MetaMTAEmailTransactionCommand.php
··· 1 + <?php 2 + 3 + abstract class MetaMTAEmailTransactionCommand extends Phobject { 4 + 5 + abstract public function getCommand(); 6 + abstract public function isCommandSupportedForObject( 7 + PhabricatorApplicationTransactionInterface $object); 8 + abstract public function buildTransactions( 9 + PhabricatorUser $viewer, 10 + PhabricatorApplicationTransactionInterface $object, 11 + PhabricatorMetaMTAReceivedMail $mail, 12 + $command, 13 + array $argv); 14 + 15 + public function getCommandAliases() { 16 + return array(); 17 + } 18 + 19 + public function getCommandObjects() { 20 + return array($this); 21 + } 22 + 23 + public static function getAllCommands() { 24 + static $commands; 25 + 26 + if ($commands === null) { 27 + $kinds = id(new PhutilSymbolLoader()) 28 + ->setAncestorClass(__CLASS__) 29 + ->loadObjects(); 30 + $commands = array(); 31 + foreach ($kinds as $kind) { 32 + foreach ($kind->getCommandObjects() as $command) { 33 + $commands[] = $command; 34 + } 35 + } 36 + } 37 + 38 + return $commands; 39 + } 40 + 41 + public static function getAllCommandsForObject( 42 + PhabricatorApplicationTransactionInterface $object) { 43 + 44 + $commands = self::getAllCommands(); 45 + foreach ($commands as $key => $command) { 46 + if (!$command->isCommandSupportedForObject($object)) { 47 + unset($commands[$key]); 48 + } 49 + } 50 + 51 + return $commands; 52 + } 53 + 54 + public static function getCommandMap(array $commands) { 55 + assert_instances_of($commands, 'MetaMTAEmailTransactionCommand'); 56 + 57 + $map = array(); 58 + foreach ($commands as $command) { 59 + $keywords = $command->getCommandAliases(); 60 + $keywords[] = $command->getCommand(); 61 + 62 + foreach ($keywords as $keyword) { 63 + $keyword = phutil_utf8_strtolower($keyword); 64 + if (empty($map[$keyword])) { 65 + $map[$keyword] = $command; 66 + } else { 67 + throw new Exception( 68 + pht( 69 + 'Mail commands "%s" and "%s" both respond to keyword "%s". '. 70 + 'Keywords must be uniquely associated with commands.', 71 + get_class($command), 72 + get_class($map[$keyword]), 73 + $keyword)); 74 + } 75 + } 76 + } 77 + 78 + return $map; 79 + } 80 + 81 + }
-18
src/applications/paste/mail/PasteReplyHandler.php
··· 13 13 return 'P'; 14 14 } 15 15 16 - protected function processMailCommands(array $commands) { 17 - $actor = $this->getActor(); 18 - 19 - $xactions = array(); 20 - foreach ($commands as $command) { 21 - switch (head($command)) { 22 - case 'unsubscribe': 23 - $xaction = id(new PhabricatorPasteTransaction()) 24 - ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) 25 - ->setNewValue(array('-' => array($actor->getPHID()))); 26 - $xactions[] = $xaction; 27 - break; 28 - } 29 - } 30 - 31 - return $xactions; 32 - } 33 - 34 16 }
+33
src/applications/subscriptions/command/PhabricatorSubscriptionsUnsubscribeEmailCommand.php
··· 1 + <?php 2 + 3 + final class PhabricatorSubscriptionsUnsubscribeEmailCommand 4 + extends MetaMTAEmailTransactionCommand { 5 + 6 + public function getCommand() { 7 + return 'unsubscribe'; 8 + } 9 + 10 + public function isCommandSupportedForObject( 11 + PhabricatorApplicationTransactionInterface $object) { 12 + return ($object instanceof PhabricatorSubscribableInterface); 13 + } 14 + 15 + public function buildTransactions( 16 + PhabricatorUser $viewer, 17 + PhabricatorApplicationTransactionInterface $object, 18 + PhabricatorMetaMTAReceivedMail $mail, 19 + $command, 20 + array $argv) { 21 + $xactions = array(); 22 + 23 + $xactions[] = $object->getApplicationTransactionTemplate() 24 + ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) 25 + ->setNewValue( 26 + array( 27 + '-' => array($viewer->getPHID()), 28 + )); 29 + 30 + return $xactions; 31 + } 32 + 33 + }
+57 -4
src/applications/transactions/replyhandler/PhabricatorApplicationTransactionReplyHandler.php
··· 49 49 50 50 $body_data = $mail->parseBody(); 51 51 52 - $xactions = $this->processMailCommands($body_data['commands']); 52 + $xactions = $this->processMailCommands( 53 + $mail, 54 + $body_data['commands']); 53 55 54 56 // If this object is subscribable, subscribe all the users who were 55 57 // CC'd on the message. ··· 84 86 ->applyTransactions($target, $xactions); 85 87 } 86 88 87 - protected function processMailCommands(array $commands) { 88 - // TODO: Modularize this. 89 - return array(); 89 + private function processMailCommands( 90 + PhabricatorMetaMTAReceivedMail $mail, 91 + array $command_list) { 92 + 93 + $viewer = $this->getActor(); 94 + $object = $this->getMailReceiver(); 95 + 96 + $list = MetaMTAEmailTransactionCommand::getAllCommandsForObject($object); 97 + $map = MetaMTAEmailTransactionCommand::getCommandMap($list); 98 + 99 + $xactions = array(); 100 + foreach ($command_list as $command_argv) { 101 + $command = head($command_argv); 102 + $argv = array_slice($command_argv, 1); 103 + 104 + $handler = idx($map, phutil_utf8_strtolower($command)); 105 + if ($handler) { 106 + $results = $handler->buildTransactions( 107 + $viewer, 108 + $object, 109 + $mail, 110 + $command, 111 + $argv); 112 + foreach ($results as $result) { 113 + $xactions[] = $result; 114 + } 115 + } else { 116 + $valid_commands = array(); 117 + foreach ($list as $valid_command) { 118 + $aliases = $valid_command->getCommandAliases(); 119 + if ($aliases) { 120 + foreach ($aliases as $key => $alias) { 121 + $aliases[$key] = '!'.$alias; 122 + } 123 + $aliases = implode(', ', $aliases); 124 + $valid_commands[] = pht( 125 + '!%s (or %s)', 126 + $valid_command->getCommand(), 127 + $aliases); 128 + } else { 129 + $valid_commands[] = '!'.$valid_command->getCommand(); 130 + } 131 + } 132 + 133 + throw new Exception( 134 + pht( 135 + 'The command "!%s" is not a supported mail command. Valid '. 136 + 'commands for this object are: %s.', 137 + $command, 138 + implode(', ', $valid_commands))); 139 + } 140 + } 141 + 142 + return $xactions; 90 143 } 91 144 92 145 }