1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-19 13:22:42 +01:00

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
This commit is contained in:
epriestley 2015-04-01 08:40:00 -07:00
parent 7c5f71b691
commit c32fee0e48
6 changed files with 175 additions and 40 deletions

View file

@ -1069,6 +1069,7 @@ phutil_register_library_map(array(
'ManiphestUpdateConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php',
'ManiphestView' => 'applications/maniphest/view/ManiphestView.php',
'MetaMTAConstants' => 'applications/metamta/constants/MetaMTAConstants.php',
'MetaMTAEmailTransactionCommand' => 'applications/metamta/command/MetaMTAEmailTransactionCommand.php',
'MetaMTAMailReceivedGarbageCollector' => 'applications/metamta/garbagecollector/MetaMTAMailReceivedGarbageCollector.php',
'MetaMTAMailSentGarbageCollector' => 'applications/metamta/garbagecollector/MetaMTAMailSentGarbageCollector.php',
'MetaMTANotificationType' => 'applications/metamta/constants/MetaMTANotificationType.php',
@ -2544,6 +2545,7 @@ phutil_register_library_map(array(
'PhabricatorSubscriptionsListController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsListController.php',
'PhabricatorSubscriptionsTransactionController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsTransactionController.php',
'PhabricatorSubscriptionsUIEventListener' => 'applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php',
'PhabricatorSubscriptionsUnsubscribeEmailCommand' => 'applications/subscriptions/command/PhabricatorSubscriptionsUnsubscribeEmailCommand.php',
'PhabricatorSupportApplication' => 'applications/support/application/PhabricatorSupportApplication.php',
'PhabricatorSyntaxHighlighter' => 'infrastructure/markup/PhabricatorSyntaxHighlighter.php',
'PhabricatorSyntaxHighlightingConfigOptions' => 'applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php',
@ -4320,6 +4322,7 @@ phutil_register_library_map(array(
'ManiphestTransactionSaveController' => 'ManiphestController',
'ManiphestUpdateConduitAPIMethod' => 'ManiphestConduitAPIMethod',
'ManiphestView' => 'AphrontView',
'MetaMTAEmailTransactionCommand' => 'Phobject',
'MetaMTAMailReceivedGarbageCollector' => 'PhabricatorGarbageCollector',
'MetaMTAMailSentGarbageCollector' => 'PhabricatorGarbageCollector',
'MetaMTANotificationType' => 'MetaMTAConstants',
@ -5921,6 +5924,7 @@ phutil_register_library_map(array(
'PhabricatorSubscriptionsListController' => 'PhabricatorController',
'PhabricatorSubscriptionsTransactionController' => 'PhabricatorController',
'PhabricatorSubscriptionsUIEventListener' => 'PhabricatorEventListener',
'PhabricatorSubscriptionsUnsubscribeEmailCommand' => 'MetaMTAEmailTransactionCommand',
'PhabricatorSupportApplication' => 'PhabricatorApplication',
'PhabricatorSyntaxHighlightingConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorSystemActionEngine' => 'Phobject',

View file

@ -13,22 +13,4 @@ final class FileReplyHandler
return 'F';
}
protected function processMailCommands(array $commands) {
$actor = $this->getActor();
$xactions = array();
foreach ($commands as $command) {
switch (head($command)) {
case 'unsubscribe':
$xaction = id(new PhabricatorFileTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
->setNewValue(array('-' => array($actor->getPHID())));
$xactions[] = $xaction;
break;
}
}
return $xactions;
}
}

View file

@ -0,0 +1,81 @@
<?php
abstract class MetaMTAEmailTransactionCommand extends Phobject {
abstract public function getCommand();
abstract public function isCommandSupportedForObject(
PhabricatorApplicationTransactionInterface $object);
abstract public function buildTransactions(
PhabricatorUser $viewer,
PhabricatorApplicationTransactionInterface $object,
PhabricatorMetaMTAReceivedMail $mail,
$command,
array $argv);
public function getCommandAliases() {
return array();
}
public function getCommandObjects() {
return array($this);
}
public static function getAllCommands() {
static $commands;
if ($commands === null) {
$kinds = id(new PhutilSymbolLoader())
->setAncestorClass(__CLASS__)
->loadObjects();
$commands = array();
foreach ($kinds as $kind) {
foreach ($kind->getCommandObjects() as $command) {
$commands[] = $command;
}
}
}
return $commands;
}
public static function getAllCommandsForObject(
PhabricatorApplicationTransactionInterface $object) {
$commands = self::getAllCommands();
foreach ($commands as $key => $command) {
if (!$command->isCommandSupportedForObject($object)) {
unset($commands[$key]);
}
}
return $commands;
}
public static function getCommandMap(array $commands) {
assert_instances_of($commands, 'MetaMTAEmailTransactionCommand');
$map = array();
foreach ($commands as $command) {
$keywords = $command->getCommandAliases();
$keywords[] = $command->getCommand();
foreach ($keywords as $keyword) {
$keyword = phutil_utf8_strtolower($keyword);
if (empty($map[$keyword])) {
$map[$keyword] = $command;
} else {
throw new Exception(
pht(
'Mail commands "%s" and "%s" both respond to keyword "%s". '.
'Keywords must be uniquely associated with commands.',
get_class($command),
get_class($map[$keyword]),
$keyword));
}
}
}
return $map;
}
}

View file

@ -13,22 +13,4 @@ final class PasteReplyHandler
return 'P';
}
protected function processMailCommands(array $commands) {
$actor = $this->getActor();
$xactions = array();
foreach ($commands as $command) {
switch (head($command)) {
case 'unsubscribe':
$xaction = id(new PhabricatorPasteTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
->setNewValue(array('-' => array($actor->getPHID())));
$xactions[] = $xaction;
break;
}
}
return $xactions;
}
}

View file

@ -0,0 +1,33 @@
<?php
final class PhabricatorSubscriptionsUnsubscribeEmailCommand
extends MetaMTAEmailTransactionCommand {
public function getCommand() {
return 'unsubscribe';
}
public function isCommandSupportedForObject(
PhabricatorApplicationTransactionInterface $object) {
return ($object instanceof PhabricatorSubscribableInterface);
}
public function buildTransactions(
PhabricatorUser $viewer,
PhabricatorApplicationTransactionInterface $object,
PhabricatorMetaMTAReceivedMail $mail,
$command,
array $argv) {
$xactions = array();
$xactions[] = $object->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
->setNewValue(
array(
'-' => array($viewer->getPHID()),
));
return $xactions;
}
}

View file

@ -49,7 +49,9 @@ abstract class PhabricatorApplicationTransactionReplyHandler
$body_data = $mail->parseBody();
$xactions = $this->processMailCommands($body_data['commands']);
$xactions = $this->processMailCommands(
$mail,
$body_data['commands']);
// If this object is subscribable, subscribe all the users who were
// CC'd on the message.
@ -84,9 +86,60 @@ abstract class PhabricatorApplicationTransactionReplyHandler
->applyTransactions($target, $xactions);
}
protected function processMailCommands(array $commands) {
// TODO: Modularize this.
return array();
private function processMailCommands(
PhabricatorMetaMTAReceivedMail $mail,
array $command_list) {
$viewer = $this->getActor();
$object = $this->getMailReceiver();
$list = MetaMTAEmailTransactionCommand::getAllCommandsForObject($object);
$map = MetaMTAEmailTransactionCommand::getCommandMap($list);
$xactions = array();
foreach ($command_list as $command_argv) {
$command = head($command_argv);
$argv = array_slice($command_argv, 1);
$handler = idx($map, phutil_utf8_strtolower($command));
if ($handler) {
$results = $handler->buildTransactions(
$viewer,
$object,
$mail,
$command,
$argv);
foreach ($results as $result) {
$xactions[] = $result;
}
} else {
$valid_commands = array();
foreach ($list as $valid_command) {
$aliases = $valid_command->getCommandAliases();
if ($aliases) {
foreach ($aliases as $key => $alias) {
$aliases[$key] = '!'.$alias;
}
$aliases = implode(', ', $aliases);
$valid_commands[] = pht(
'!%s (or %s)',
$valid_command->getCommand(),
$aliases);
} else {
$valid_commands[] = '!'.$valid_command->getCommand();
}
}
throw new Exception(
pht(
'The command "!%s" is not a supported mail command. Valid '.
'commands for this object are: %s.',
$command,
implode(', ', $valid_commands)));
}
}
return $xactions;
}
}