2015-04-01 08:39:21 -07:00
|
|
|
<?php
|
|
|
|
|
|
|
|
abstract class PhabricatorApplicationTransactionReplyHandler
|
|
|
|
extends PhabricatorMailReplyHandler {
|
|
|
|
|
|
|
|
abstract public function getObjectPrefix();
|
|
|
|
|
|
|
|
public function getPrivateReplyHandlerEmailAddress(
|
Build separate mail for each recipient, honoring recipient access levels
Summary:
Ref T6367. Removes `multiplexMail()`!
We can't pass a single body into a function which splits it anymore: we need to split recipients first, then build bodies for each recipient list. This lets us build separate bodies for each recipient's individual translation/access levels.
The new logic does this:
- First, split recipients into groups called "targets".
- Each target corresponds to one actual mail we're going to build.
- Each target has a viewer (whose translation / access levels will be used to generate the mail).
- Each target has a to/cc list (the users who we'll ultimately send the mail to).
- For each target, build a custom mail body based on the viewer's access levels and settings (language prefs not actually implemented).
- Then, deliver the mail.
Test Plan:
- Read new config help.
Then did a bunch of testing, primarily with `bin/mail list-outbound` and `bin/mail show-outbound` (to review generated mail), `bin/phd debug taskmaster` (to run daemons freely) and `bin/worker execute --id <id>` (to repeatedly test a specific piece of code after identifying an issue).
With `one-mail-per-recipient` on (default):
- Sent mail to multiple users.
- Verified mail showed up in `mail list-outbound`.
- Examined mail with `mail show-outbound`.
- Added a project that a subscriber could not see.
- Verified it was not present in `X-Phabricator-Projects`.
- Verified it was rendered as "Restricted Project" for the non-permissioned viewer.
- Added a subscriber, then changed the object policy so they could not see it and sent mail.
- Verified I received mail but the other user did not.
- Enabled public replies and verified mail generated with public addresses.
- Disabld public replies and verified mail generated with private addresses.
With `one-mail-per-recipient` off:
- Verified that one mail is sent to all recipients.
- Verified users who can not see the object are still filtered.
- Verified that partially-visible projects are completely visible in the mail (this violates policies, as documented, as the best available compromise).
- Enabled public replies and verified the mail generated with "Reply To".
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: carlsverre, epriestley
Maniphest Tasks: T6367
Differential Revision: https://secure.phabricator.com/D13131
2015-06-02 14:29:30 -07:00
|
|
|
PhabricatorUser $user) {
|
2015-04-01 08:39:21 -07:00
|
|
|
return $this->getDefaultPrivateReplyHandlerEmailAddress(
|
Build separate mail for each recipient, honoring recipient access levels
Summary:
Ref T6367. Removes `multiplexMail()`!
We can't pass a single body into a function which splits it anymore: we need to split recipients first, then build bodies for each recipient list. This lets us build separate bodies for each recipient's individual translation/access levels.
The new logic does this:
- First, split recipients into groups called "targets".
- Each target corresponds to one actual mail we're going to build.
- Each target has a viewer (whose translation / access levels will be used to generate the mail).
- Each target has a to/cc list (the users who we'll ultimately send the mail to).
- For each target, build a custom mail body based on the viewer's access levels and settings (language prefs not actually implemented).
- Then, deliver the mail.
Test Plan:
- Read new config help.
Then did a bunch of testing, primarily with `bin/mail list-outbound` and `bin/mail show-outbound` (to review generated mail), `bin/phd debug taskmaster` (to run daemons freely) and `bin/worker execute --id <id>` (to repeatedly test a specific piece of code after identifying an issue).
With `one-mail-per-recipient` on (default):
- Sent mail to multiple users.
- Verified mail showed up in `mail list-outbound`.
- Examined mail with `mail show-outbound`.
- Added a project that a subscriber could not see.
- Verified it was not present in `X-Phabricator-Projects`.
- Verified it was rendered as "Restricted Project" for the non-permissioned viewer.
- Added a subscriber, then changed the object policy so they could not see it and sent mail.
- Verified I received mail but the other user did not.
- Enabled public replies and verified mail generated with public addresses.
- Disabld public replies and verified mail generated with private addresses.
With `one-mail-per-recipient` off:
- Verified that one mail is sent to all recipients.
- Verified users who can not see the object are still filtered.
- Verified that partially-visible projects are completely visible in the mail (this violates policies, as documented, as the best available compromise).
- Enabled public replies and verified the mail generated with "Reply To".
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: carlsverre, epriestley
Maniphest Tasks: T6367
Differential Revision: https://secure.phabricator.com/D13131
2015-06-02 14:29:30 -07:00
|
|
|
$user,
|
2015-04-01 08:39:21 -07:00
|
|
|
$this->getObjectPrefix());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getPublicReplyHandlerEmailAddress() {
|
|
|
|
return $this->getDefaultPublicReplyHandlerEmailAddress(
|
|
|
|
$this->getObjectPrefix());
|
|
|
|
}
|
|
|
|
|
|
|
|
private function newEditor(PhabricatorMetaMTAReceivedMail $mail) {
|
2016-03-25 05:56:16 -07:00
|
|
|
$content_source = $mail->newContentSource();
|
2015-04-01 08:39:21 -07:00
|
|
|
|
|
|
|
$editor = $this->getMailReceiver()
|
|
|
|
->getApplicationTransactionEditor()
|
|
|
|
->setActor($this->getActor())
|
|
|
|
->setContentSource($content_source)
|
|
|
|
->setContinueOnMissingFields(true)
|
|
|
|
->setParentMessageID($mail->getMessageID())
|
|
|
|
->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs());
|
|
|
|
|
|
|
|
if ($this->getApplicationEmail()) {
|
|
|
|
$editor->setApplicationEmail($this->getApplicationEmail());
|
|
|
|
}
|
|
|
|
|
|
|
|
return $editor;
|
|
|
|
}
|
|
|
|
|
2015-09-09 14:07:07 -07:00
|
|
|
protected function newTransaction() {
|
2015-04-01 08:39:21 -07:00
|
|
|
return $this->getMailReceiver()->getApplicationTransactionTemplate();
|
|
|
|
}
|
|
|
|
|
2015-04-01 08:40:42 -07:00
|
|
|
protected function didReceiveMail(
|
|
|
|
PhabricatorMetaMTAReceivedMail $mail,
|
|
|
|
$body) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function shouldCreateCommentFromMailBody() {
|
|
|
|
return (bool)$this->getMailReceiver()->getID();
|
|
|
|
}
|
|
|
|
|
2015-04-01 08:39:21 -07:00
|
|
|
final protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) {
|
|
|
|
$viewer = $this->getActor();
|
|
|
|
$object = $this->getMailReceiver();
|
2015-06-11 10:23:56 -07:00
|
|
|
$app_email = $this->getApplicationEmail();
|
|
|
|
|
|
|
|
$is_new = !$object->getID();
|
|
|
|
|
|
|
|
// If this is a new object which implements the Spaces interface and was
|
|
|
|
// created by sending mail to an ApplicationEmail address, put the object
|
|
|
|
// in the same Space the address is in.
|
|
|
|
if ($is_new) {
|
|
|
|
if ($object instanceof PhabricatorSpacesInterface) {
|
|
|
|
if ($app_email) {
|
|
|
|
$space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID(
|
|
|
|
$app_email);
|
|
|
|
$object->setSpacePHID($space_phid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-01 08:39:21 -07:00
|
|
|
|
|
|
|
$body_data = $mail->parseBody();
|
2015-04-01 08:40:42 -07:00
|
|
|
$body = $body_data['body'];
|
|
|
|
$body = $this->enhanceBodyWithAttachments($body, $mail->getAttachments());
|
2015-04-01 08:39:21 -07:00
|
|
|
|
2015-04-01 08:40:42 -07:00
|
|
|
$xactions = $this->didReceiveMail($mail, $body);
|
2015-04-01 08:39:21 -07:00
|
|
|
|
|
|
|
// If this object is subscribable, subscribe all the users who were
|
2015-09-09 14:07:07 -07:00
|
|
|
// recipients on the message.
|
2015-04-01 08:39:21 -07:00
|
|
|
if ($object instanceof PhabricatorSubscribableInterface) {
|
2015-09-09 14:07:07 -07:00
|
|
|
$subscriber_phids = $mail->loadAllRecipientPHIDs();
|
2015-04-01 08:39:21 -07:00
|
|
|
if ($subscriber_phids) {
|
|
|
|
$xactions[] = $this->newTransaction()
|
|
|
|
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
|
|
|
|
->setNewValue(
|
|
|
|
array(
|
2015-09-09 14:07:07 -07:00
|
|
|
'+' => $subscriber_phids,
|
2015-04-01 08:39:21 -07:00
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-01 08:40:42 -07:00
|
|
|
$command_xactions = $this->processMailCommands(
|
|
|
|
$mail,
|
|
|
|
$body_data['commands']);
|
|
|
|
foreach ($command_xactions as $xaction) {
|
|
|
|
$xactions[] = $xaction;
|
|
|
|
}
|
2015-04-01 08:39:21 -07:00
|
|
|
|
2015-04-01 08:40:42 -07:00
|
|
|
if ($this->shouldCreateCommentFromMailBody()) {
|
|
|
|
$comment = $this
|
|
|
|
->newTransaction()
|
|
|
|
->getApplicationTransactionCommentObject()
|
|
|
|
->setContent($body);
|
2015-04-01 08:39:21 -07:00
|
|
|
|
2015-04-01 08:40:42 -07:00
|
|
|
$xactions[] = $this->newTransaction()
|
|
|
|
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
|
|
|
->attachComment($comment);
|
|
|
|
}
|
2015-04-01 08:39:21 -07:00
|
|
|
|
|
|
|
$this->newEditor($mail)
|
|
|
|
->setContinueOnNoEffect(true)
|
2018-12-20 10:41:01 -08:00
|
|
|
->applyTransactions($object, $xactions);
|
2015-04-01 08:39:21 -07:00
|
|
|
}
|
|
|
|
|
2015-04-01 08:40:00 -07:00
|
|
|
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;
|
2015-04-01 08:39:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|