diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3455,6 +3455,7 @@ '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 @@ 'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase', 'PhabricatorMailReplyHandler' => 'Phobject', 'PhabricatorMailRoutingRule' => 'Phobject', + 'PhabricatorMailSMSEngine' => 'PhabricatorMailMessageEngine', 'PhabricatorMailSMSMessage' => 'PhabricatorMailExternalMessage', 'PhabricatorMailSMTPAdapter' => 'PhabricatorMailAdapter', 'PhabricatorMailSendGridAdapter' => 'PhabricatorMailAdapter', diff --git a/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php --- a/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php +++ b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php @@ -8,6 +8,7 @@ private $objectPHIDs; private $statuses; private $uniqueKeys; + private $isPrimary; public function withIDs(array $ids) { $this->ids = $ids; @@ -34,6 +35,11 @@ return $this; } + public function withIsPrimary($is_primary) { + $this->isPrimary = $is_primary; + return $this; + } + public function newResultObject() { return new PhabricatorAuthContactNumber(); } @@ -80,6 +86,13 @@ $this->uniqueKeys); } + if ($this->isPrimary !== null) { + $where[] = qsprintf( + $conn, + 'isPrimary = %d', + (int)$this->isPrimary); + } + return $where; } diff --git a/src/applications/metamta/engine/PhabricatorMailSMSEngine.php b/src/applications/metamta/engine/PhabricatorMailSMSEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/metamta/engine/PhabricatorMailSMSEngine.php @@ -0,0 +1,75 @@ +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; + } + +} diff --git a/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php --- a/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php @@ -7,8 +7,7 @@ $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 @@ ->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 @@ 'id' => $mail->getID(), 'encrypt' => ($mail->getMustEncrypt() ? '#' : ' '), 'status' => PhabricatorMailOutboundStatus::getStatusName($status), + 'type' => $mail->getMessageType(), 'subject' => $mail->getSubject(), )); } diff --git a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php --- a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php @@ -60,6 +60,12 @@ '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 @@ $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 @@ 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 @@ $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 @@ $mail->addAttachment($file); } + $mail->setMessageType($type); + PhabricatorWorker::setRunAllTasksInProcess(true); $mail->save(); diff --git a/src/applications/metamta/message/PhabricatorMailEmailMessage.php b/src/applications/metamta/message/PhabricatorMailEmailMessage.php --- a/src/applications/metamta/message/PhabricatorMailEmailMessage.php +++ b/src/applications/metamta/message/PhabricatorMailEmailMessage.php @@ -15,6 +15,10 @@ private $textBody; private $htmlBody; + public function newMailMessageEngine() { + return new PhabricatorMailEmailEngine(); + } + public function setFromAddress(PhutilEmailAddress $from_address) { $this->fromAddress = $from_address; return $this; diff --git a/src/applications/metamta/message/PhabricatorMailExternalMessage.php b/src/applications/metamta/message/PhabricatorMailExternalMessage.php --- a/src/applications/metamta/message/PhabricatorMailExternalMessage.php +++ b/src/applications/metamta/message/PhabricatorMailExternalMessage.php @@ -7,4 +7,11 @@ return $this->getPhobjectClassConstant('MESSAGETYPE'); } + final public static function getAllMessageTypes() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getMessageType') + ->execute(); + } + } diff --git a/src/applications/metamta/message/PhabricatorMailSMSMessage.php b/src/applications/metamta/message/PhabricatorMailSMSMessage.php --- a/src/applications/metamta/message/PhabricatorMailSMSMessage.php +++ b/src/applications/metamta/message/PhabricatorMailSMSMessage.php @@ -8,6 +8,10 @@ private $toNumber; private $textBody; + public function newMailMessageEngine() { + return new PhabricatorMailSMSEngine(); + } + public function setToNumber(PhabricatorPhoneNumber $to_number) { $this->toNumber = $to_number; return $this; diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php --- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php @@ -400,6 +400,18 @@ 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 @@ $mailers = self::newMailers( array( 'outbound' => true, + 'media' => array( + $this->getMessageType(), + ), )); $try_mailers = $this->getParam('mailers.try'); @@ -699,10 +714,19 @@ $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)