mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-11 15:21:03 +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:
parent
596435b35e
commit
af71c51f0a
9 changed files with 174 additions and 17 deletions
|
@ -3455,6 +3455,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php',
|
'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php',
|
||||||
'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php',
|
'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php',
|
||||||
'PhabricatorMailRoutingRule' => 'applications/metamta/constants/PhabricatorMailRoutingRule.php',
|
'PhabricatorMailRoutingRule' => 'applications/metamta/constants/PhabricatorMailRoutingRule.php',
|
||||||
|
'PhabricatorMailSMSEngine' => 'applications/metamta/engine/PhabricatorMailSMSEngine.php',
|
||||||
'PhabricatorMailSMSMessage' => 'applications/metamta/message/PhabricatorMailSMSMessage.php',
|
'PhabricatorMailSMSMessage' => 'applications/metamta/message/PhabricatorMailSMSMessage.php',
|
||||||
'PhabricatorMailSMTPAdapter' => 'applications/metamta/adapter/PhabricatorMailSMTPAdapter.php',
|
'PhabricatorMailSMTPAdapter' => 'applications/metamta/adapter/PhabricatorMailSMTPAdapter.php',
|
||||||
'PhabricatorMailSendGridAdapter' => 'applications/metamta/adapter/PhabricatorMailSendGridAdapter.php',
|
'PhabricatorMailSendGridAdapter' => 'applications/metamta/adapter/PhabricatorMailSendGridAdapter.php',
|
||||||
|
@ -9341,6 +9342,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase',
|
'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorMailReplyHandler' => 'Phobject',
|
'PhabricatorMailReplyHandler' => 'Phobject',
|
||||||
'PhabricatorMailRoutingRule' => 'Phobject',
|
'PhabricatorMailRoutingRule' => 'Phobject',
|
||||||
|
'PhabricatorMailSMSEngine' => 'PhabricatorMailMessageEngine',
|
||||||
'PhabricatorMailSMSMessage' => 'PhabricatorMailExternalMessage',
|
'PhabricatorMailSMSMessage' => 'PhabricatorMailExternalMessage',
|
||||||
'PhabricatorMailSMTPAdapter' => 'PhabricatorMailAdapter',
|
'PhabricatorMailSMTPAdapter' => 'PhabricatorMailAdapter',
|
||||||
'PhabricatorMailSendGridAdapter' => 'PhabricatorMailAdapter',
|
'PhabricatorMailSendGridAdapter' => 'PhabricatorMailAdapter',
|
||||||
|
|
|
@ -8,6 +8,7 @@ final class PhabricatorAuthContactNumberQuery
|
||||||
private $objectPHIDs;
|
private $objectPHIDs;
|
||||||
private $statuses;
|
private $statuses;
|
||||||
private $uniqueKeys;
|
private $uniqueKeys;
|
||||||
|
private $isPrimary;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -34,6 +35,11 @@ final class PhabricatorAuthContactNumberQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withIsPrimary($is_primary) {
|
||||||
|
$this->isPrimary = $is_primary;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function newResultObject() {
|
public function newResultObject() {
|
||||||
return new PhabricatorAuthContactNumber();
|
return new PhabricatorAuthContactNumber();
|
||||||
}
|
}
|
||||||
|
@ -80,6 +86,13 @@ final class PhabricatorAuthContactNumberQuery
|
||||||
$this->uniqueKeys);
|
$this->uniqueKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->isPrimary !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'isPrimary = %d',
|
||||||
|
(int)$this->isPrimary);
|
||||||
|
}
|
||||||
|
|
||||||
return $where;
|
return $where;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
75
src/applications/metamta/engine/PhabricatorMailSMSEngine.php
Normal file
75
src/applications/metamta/engine/PhabricatorMailSMSEngine.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,8 +7,7 @@ final class PhabricatorMailManagementListOutboundWorkflow
|
||||||
$this
|
$this
|
||||||
->setName('list-outbound')
|
->setName('list-outbound')
|
||||||
->setSynopsis(pht('List outbound messages sent by Phabricator.'))
|
->setSynopsis(pht('List outbound messages sent by Phabricator.'))
|
||||||
->setExamples(
|
->setExamples('**list-outbound**')
|
||||||
'**list-outbound**')
|
|
||||||
->setArguments(
|
->setArguments(
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
|
@ -39,6 +38,7 @@ final class PhabricatorMailManagementListOutboundWorkflow
|
||||||
->addColumn('id', array('title' => pht('ID')))
|
->addColumn('id', array('title' => pht('ID')))
|
||||||
->addColumn('encrypt', array('title' => pht('#')))
|
->addColumn('encrypt', array('title' => pht('#')))
|
||||||
->addColumn('status', array('title' => pht('Status')))
|
->addColumn('status', array('title' => pht('Status')))
|
||||||
|
->addColumn('type', array('title' => pht('Type')))
|
||||||
->addColumn('subject', array('title' => pht('Subject')));
|
->addColumn('subject', array('title' => pht('Subject')));
|
||||||
|
|
||||||
foreach (array_reverse($mails) as $mail) {
|
foreach (array_reverse($mails) as $mail) {
|
||||||
|
@ -48,6 +48,7 @@ final class PhabricatorMailManagementListOutboundWorkflow
|
||||||
'id' => $mail->getID(),
|
'id' => $mail->getID(),
|
||||||
'encrypt' => ($mail->getMustEncrypt() ? '#' : ' '),
|
'encrypt' => ($mail->getMustEncrypt() ? '#' : ' '),
|
||||||
'status' => PhabricatorMailOutboundStatus::getStatusName($status),
|
'status' => PhabricatorMailOutboundStatus::getStatusName($status),
|
||||||
|
'type' => $mail->getMessageType(),
|
||||||
'subject' => $mail->getSubject(),
|
'subject' => $mail->getSubject(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,12 @@ final class PhabricatorMailManagementSendTestWorkflow
|
||||||
'name' => 'bulk',
|
'name' => 'bulk',
|
||||||
'help' => pht('Send with bulk headers.'),
|
'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();
|
$console = PhutilConsole::getConsole();
|
||||||
$viewer = $this->getViewer();
|
$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');
|
$from = $args->getArg('from');
|
||||||
if ($from) {
|
if ($from) {
|
||||||
$user = id(new PhabricatorPeopleQuery())
|
$user = id(new PhabricatorPeopleQuery())
|
||||||
|
@ -86,9 +106,8 @@ final class PhabricatorMailManagementSendTestWorkflow
|
||||||
if (!$tos && !$ccs) {
|
if (!$tos && !$ccs) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
'Specify one or more users to send mail to with `%s` and `%s`.',
|
'Specify one or more users to send a message to with "--to" and/or '.
|
||||||
'--to',
|
'"--cc".'));
|
||||||
'--cc'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$names = array_merge($tos, $ccs);
|
$names = array_merge($tos, $ccs);
|
||||||
|
@ -166,26 +185,32 @@ final class PhabricatorMailManagementSendTestWorkflow
|
||||||
$mail->setFrom($from->getPHID());
|
$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');
|
$mailer_key = $args->getArg('mailer');
|
||||||
if ($mailer_key !== null) {
|
if ($mailer_key !== null) {
|
||||||
$mailers = PhabricatorMetaMTAMail::newMailers(array());
|
|
||||||
|
|
||||||
$mailers = mpull($mailers, null, 'getKey');
|
|
||||||
if (!isset($mailers[$mailer_key])) {
|
if (!isset($mailers[$mailer_key])) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
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,
|
$mailer_key,
|
||||||
|
$type,
|
||||||
implode(', ', array_keys($mailers))));
|
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));
|
$mail->setTryMailers(array($mailer_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,6 +222,8 @@ final class PhabricatorMailManagementSendTestWorkflow
|
||||||
$mail->addAttachment($file);
|
$mail->addAttachment($file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$mail->setMessageType($type);
|
||||||
|
|
||||||
PhabricatorWorker::setRunAllTasksInProcess(true);
|
PhabricatorWorker::setRunAllTasksInProcess(true);
|
||||||
$mail->save();
|
$mail->save();
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,10 @@ final class PhabricatorMailEmailMessage
|
||||||
private $textBody;
|
private $textBody;
|
||||||
private $htmlBody;
|
private $htmlBody;
|
||||||
|
|
||||||
|
public function newMailMessageEngine() {
|
||||||
|
return new PhabricatorMailEmailEngine();
|
||||||
|
}
|
||||||
|
|
||||||
public function setFromAddress(PhutilEmailAddress $from_address) {
|
public function setFromAddress(PhutilEmailAddress $from_address) {
|
||||||
$this->fromAddress = $from_address;
|
$this->fromAddress = $from_address;
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
@ -7,4 +7,11 @@ abstract class PhabricatorMailExternalMessage
|
||||||
return $this->getPhobjectClassConstant('MESSAGETYPE');
|
return $this->getPhobjectClassConstant('MESSAGETYPE');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public static function getAllMessageTypes() {
|
||||||
|
return id(new PhutilClassMapQuery())
|
||||||
|
->setAncestorClass(__CLASS__)
|
||||||
|
->setUniqueMethod('getMessageType')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,10 @@ final class PhabricatorMailSMSMessage
|
||||||
private $toNumber;
|
private $toNumber;
|
||||||
private $textBody;
|
private $textBody;
|
||||||
|
|
||||||
|
public function newMailMessageEngine() {
|
||||||
|
return new PhabricatorMailSMSEngine();
|
||||||
|
}
|
||||||
|
|
||||||
public function setToNumber(PhabricatorPhoneNumber $to_number) {
|
public function setToNumber(PhabricatorPhoneNumber $to_number) {
|
||||||
$this->toNumber = $to_number;
|
$this->toNumber = $to_number;
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
@ -400,6 +400,18 @@ final class PhabricatorMetaMTAMail
|
||||||
return $this->getParam('cc', array());
|
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
|
* Force delivery of a message, even if recipients have preferences which
|
||||||
* would otherwise drop the message.
|
* would otherwise drop the message.
|
||||||
|
@ -529,6 +541,9 @@ final class PhabricatorMetaMTAMail
|
||||||
$mailers = self::newMailers(
|
$mailers = self::newMailers(
|
||||||
array(
|
array(
|
||||||
'outbound' => true,
|
'outbound' => true,
|
||||||
|
'media' => array(
|
||||||
|
$this->getMessageType(),
|
||||||
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
$try_mailers = $this->getParam('mailers.try');
|
$try_mailers = $this->getParam('mailers.try');
|
||||||
|
@ -699,10 +714,19 @@ final class PhabricatorMetaMTAMail
|
||||||
$file->attachToObject($this->getPHID());
|
$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();
|
$exceptions = array();
|
||||||
foreach ($mailers as $mailer) {
|
foreach ($mailers as $mailer) {
|
||||||
try {
|
try {
|
||||||
$message = id(new PhabricatorMailEmailEngine())
|
$message = $type->newMailMessageEngine()
|
||||||
->setMailer($mailer)
|
->setMailer($mailer)
|
||||||
->setMail($this)
|
->setMail($this)
|
||||||
->setActors($actors)
|
->setActors($actors)
|
||||||
|
|
Loading…
Reference in a new issue