1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 14:00:56 +01:00

Give "MetaMTAMail" a "message type" and support SMS

Summary:
Depends on D20011. Ref T920. This change lets a "MetaMTAMail" storage object represent various different types of messages, and  makes "all" the `bin/mail` stuff "totally work" with messages of non-email types.

In practice, a lot of the related tooling needs some polish/refinement, but the basics work.

Test Plan: Used `echo beep boop | bin/mail send-test --to epriestley --type sms` to send myself SMS.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T920

Differential Revision: https://secure.phabricator.com/D20012
This commit is contained in:
epriestley 2019-01-21 15:03:45 -08:00
parent 596435b35e
commit af71c51f0a
9 changed files with 174 additions and 17 deletions

View file

@ -3455,6 +3455,7 @@ phutil_register_library_map(array(
'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php',
'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php',
'PhabricatorMailRoutingRule' => 'applications/metamta/constants/PhabricatorMailRoutingRule.php',
'PhabricatorMailSMSEngine' => 'applications/metamta/engine/PhabricatorMailSMSEngine.php',
'PhabricatorMailSMSMessage' => 'applications/metamta/message/PhabricatorMailSMSMessage.php',
'PhabricatorMailSMTPAdapter' => 'applications/metamta/adapter/PhabricatorMailSMTPAdapter.php',
'PhabricatorMailSendGridAdapter' => 'applications/metamta/adapter/PhabricatorMailSendGridAdapter.php',
@ -9341,6 +9342,7 @@ phutil_register_library_map(array(
'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase',
'PhabricatorMailReplyHandler' => 'Phobject',
'PhabricatorMailRoutingRule' => 'Phobject',
'PhabricatorMailSMSEngine' => 'PhabricatorMailMessageEngine',
'PhabricatorMailSMSMessage' => 'PhabricatorMailExternalMessage',
'PhabricatorMailSMTPAdapter' => 'PhabricatorMailAdapter',
'PhabricatorMailSendGridAdapter' => 'PhabricatorMailAdapter',

View file

@ -8,6 +8,7 @@ final class PhabricatorAuthContactNumberQuery
private $objectPHIDs;
private $statuses;
private $uniqueKeys;
private $isPrimary;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -34,6 +35,11 @@ final class PhabricatorAuthContactNumberQuery
return $this;
}
public function withIsPrimary($is_primary) {
$this->isPrimary = $is_primary;
return $this;
}
public function newResultObject() {
return new PhabricatorAuthContactNumber();
}
@ -80,6 +86,13 @@ final class PhabricatorAuthContactNumberQuery
$this->uniqueKeys);
}
if ($this->isPrimary !== null) {
$where[] = qsprintf(
$conn,
'isPrimary = %d',
(int)$this->isPrimary);
}
return $where;
}

View file

@ -0,0 +1,75 @@
<?php
final class PhabricatorMailSMSEngine
extends PhabricatorMailMessageEngine {
public function newMessage() {
$mailer = $this->getMailer();
$mail = $this->getMail();
$message = new PhabricatorMailSMSMessage();
$phids = $mail->getToPHIDs();
if (!$phids) {
$mail->setMessage(pht('Message has no "To" recipient.'));
return null;
}
if (count($phids) > 1) {
$mail->setMessage(pht('Message has more than one "To" recipient.'));
return null;
}
$phid = head($phids);
$actor = $this->getActor($phid);
if (!$actor) {
$mail->setMessage(pht('Message recipient has no mailable actor.'));
return null;
}
if (!$actor->isDeliverable()) {
$mail->setMessage(pht('Message recipient is not deliverable.'));
return null;
}
$omnipotent = PhabricatorUser::getOmnipotentUser();
$contact_numbers = id(new PhabricatorAuthContactNumberQuery())
->setViewer($omnipotent)
->withObjectPHIDs(array($phid))
->withStatuses(
array(
PhabricatorAuthContactNumber::STATUS_ACTIVE,
))
->withIsPrimary(true)
->execute();
if (!$contact_numbers) {
$mail->setMessage(
pht('Message recipient has no primary contact number.'));
return null;
}
// The database does not strictly guarantee that only one number is
// primary, so make sure no one has monkeyed with stuff.
if (count($contact_numbers) > 1) {
$mail->setMessage(
pht('Message recipient has more than one primary contact number.'));
return null;
}
$contact_number = head($contact_numbers);
$contact_number = $contact_number->getContactNumber();
$to_number = new PhabricatorPhoneNumber($contact_number);
$message->setToNumber($to_number);
$body = $mail->getBody();
if ($body !== null) {
$message->setTextBody($body);
}
return $message;
}
}

View file

@ -7,8 +7,7 @@ final class PhabricatorMailManagementListOutboundWorkflow
$this
->setName('list-outbound')
->setSynopsis(pht('List outbound messages sent by Phabricator.'))
->setExamples(
'**list-outbound**')
->setExamples('**list-outbound**')
->setArguments(
array(
array(
@ -39,6 +38,7 @@ final class PhabricatorMailManagementListOutboundWorkflow
->addColumn('id', array('title' => pht('ID')))
->addColumn('encrypt', array('title' => pht('#')))
->addColumn('status', array('title' => pht('Status')))
->addColumn('type', array('title' => pht('Type')))
->addColumn('subject', array('title' => pht('Subject')));
foreach (array_reverse($mails) as $mail) {
@ -48,6 +48,7 @@ final class PhabricatorMailManagementListOutboundWorkflow
'id' => $mail->getID(),
'encrypt' => ($mail->getMustEncrypt() ? '#' : ' '),
'status' => PhabricatorMailOutboundStatus::getStatusName($status),
'type' => $mail->getMessageType(),
'subject' => $mail->getSubject(),
));
}

View file

@ -60,6 +60,12 @@ final class PhabricatorMailManagementSendTestWorkflow
'name' => 'bulk',
'help' => pht('Send with bulk headers.'),
),
array(
'name' => 'type',
'param' => 'message-type',
'help' => pht(
'Send the specified type of message (email, sms, ...).'),
),
));
}
@ -67,6 +73,20 @@ final class PhabricatorMailManagementSendTestWorkflow
$console = PhutilConsole::getConsole();
$viewer = $this->getViewer();
$type = $args->getArg('type');
if (!strlen($type)) {
$type = PhabricatorMailEmailMessage::MESSAGETYPE;
}
$type_map = PhabricatorMailExternalMessage::getAllMessageTypes();
if (!isset($type_map[$type])) {
throw new PhutilArgumentUsageException(
pht(
'Message type "%s" is unknown, supported message types are: %s.',
$type,
implode(', ', array_keys($type_map))));
}
$from = $args->getArg('from');
if ($from) {
$user = id(new PhabricatorPeopleQuery())
@ -86,9 +106,8 @@ final class PhabricatorMailManagementSendTestWorkflow
if (!$tos && !$ccs) {
throw new PhutilArgumentUsageException(
pht(
'Specify one or more users to send mail to with `%s` and `%s`.',
'--to',
'--cc'));
'Specify one or more users to send a message to with "--to" and/or '.
'"--cc".'));
}
$names = array_merge($tos, $ccs);
@ -166,26 +185,32 @@ final class PhabricatorMailManagementSendTestWorkflow
$mail->setFrom($from->getPHID());
}
$mailers = PhabricatorMetaMTAMail::newMailers(
array(
'media' => array($type),
'outbound' => true,
));
$mailers = mpull($mailers, null, 'getKey');
if (!$mailers) {
throw new PhutilArgumentUsageException(
pht(
'No configured mailers support outbound messages of type "%s".',
$type));
}
$mailer_key = $args->getArg('mailer');
if ($mailer_key !== null) {
$mailers = PhabricatorMetaMTAMail::newMailers(array());
$mailers = mpull($mailers, null, 'getKey');
if (!isset($mailers[$mailer_key])) {
throw new PhutilArgumentUsageException(
pht(
'Mailer key ("%s") is not configured. Available keys are: %s.',
'Mailer key ("%s") is not configured, or does not support '.
'outbound messages of type "%s". Available mailers are: %s.',
$mailer_key,
$type,
implode(', ', array_keys($mailers))));
}
if (!$mailers[$mailer_key]->getSupportsOutbound()) {
throw new PhutilArgumentUsageException(
pht(
'Mailer ("%s") is not configured to support outbound mail.',
$mailer_key));
}
$mail->setTryMailers(array($mailer_key));
}
@ -197,6 +222,8 @@ final class PhabricatorMailManagementSendTestWorkflow
$mail->addAttachment($file);
}
$mail->setMessageType($type);
PhabricatorWorker::setRunAllTasksInProcess(true);
$mail->save();

View file

@ -15,6 +15,10 @@ final class PhabricatorMailEmailMessage
private $textBody;
private $htmlBody;
public function newMailMessageEngine() {
return new PhabricatorMailEmailEngine();
}
public function setFromAddress(PhutilEmailAddress $from_address) {
$this->fromAddress = $from_address;
return $this;

View file

@ -7,4 +7,11 @@ abstract class PhabricatorMailExternalMessage
return $this->getPhobjectClassConstant('MESSAGETYPE');
}
final public static function getAllMessageTypes() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getMessageType')
->execute();
}
}

View file

@ -8,6 +8,10 @@ final class PhabricatorMailSMSMessage
private $toNumber;
private $textBody;
public function newMailMessageEngine() {
return new PhabricatorMailSMSEngine();
}
public function setToNumber(PhabricatorPhoneNumber $to_number) {
$this->toNumber = $to_number;
return $this;

View file

@ -400,6 +400,18 @@ final class PhabricatorMetaMTAMail
return $this->getParam('cc', array());
}
public function setMessageType($message_type) {
return $this->setParam('message.type', $message_type);
}
public function getMessageType() {
return $this->getParam(
'message.type',
PhabricatorMailEmailMessage::MESSAGETYPE);
}
/**
* Force delivery of a message, even if recipients have preferences which
* would otherwise drop the message.
@ -529,6 +541,9 @@ final class PhabricatorMetaMTAMail
$mailers = self::newMailers(
array(
'outbound' => true,
'media' => array(
$this->getMessageType(),
),
));
$try_mailers = $this->getParam('mailers.try');
@ -699,10 +714,19 @@ final class PhabricatorMetaMTAMail
$file->attachToObject($this->getPHID());
}
$type_map = PhabricatorMailExternalMessage::getAllMessageTypes();
$type = idx($type_map, $this->getMessageType());
if (!$type) {
throw new Exception(
pht(
'Unable to send message with unknown message type "%s".',
$type));
}
$exceptions = array();
foreach ($mailers as $mailer) {
try {
$message = id(new PhabricatorMailEmailEngine())
$message = $type->newMailMessageEngine()
->setMailer($mailer)
->setMail($this)
->setActors($actors)