@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+174 -17
+2
src/__phutil_library_map__.php
··· 3455 3455 'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php', 3456 3456 'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php', 3457 3457 'PhabricatorMailRoutingRule' => 'applications/metamta/constants/PhabricatorMailRoutingRule.php', 3458 + 'PhabricatorMailSMSEngine' => 'applications/metamta/engine/PhabricatorMailSMSEngine.php', 3458 3459 'PhabricatorMailSMSMessage' => 'applications/metamta/message/PhabricatorMailSMSMessage.php', 3459 3460 'PhabricatorMailSMTPAdapter' => 'applications/metamta/adapter/PhabricatorMailSMTPAdapter.php', 3460 3461 'PhabricatorMailSendGridAdapter' => 'applications/metamta/adapter/PhabricatorMailSendGridAdapter.php', ··· 9341 9342 'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase', 9342 9343 'PhabricatorMailReplyHandler' => 'Phobject', 9343 9344 'PhabricatorMailRoutingRule' => 'Phobject', 9345 + 'PhabricatorMailSMSEngine' => 'PhabricatorMailMessageEngine', 9344 9346 'PhabricatorMailSMSMessage' => 'PhabricatorMailExternalMessage', 9345 9347 'PhabricatorMailSMTPAdapter' => 'PhabricatorMailAdapter', 9346 9348 'PhabricatorMailSendGridAdapter' => 'PhabricatorMailAdapter',
+13
src/applications/auth/query/PhabricatorAuthContactNumberQuery.php
··· 8 8 private $objectPHIDs; 9 9 private $statuses; 10 10 private $uniqueKeys; 11 + private $isPrimary; 11 12 12 13 public function withIDs(array $ids) { 13 14 $this->ids = $ids; ··· 31 32 32 33 public function withUniqueKeys(array $unique_keys) { 33 34 $this->uniqueKeys = $unique_keys; 35 + return $this; 36 + } 37 + 38 + public function withIsPrimary($is_primary) { 39 + $this->isPrimary = $is_primary; 34 40 return $this; 35 41 } 36 42 ··· 78 84 $conn, 79 85 'uniqueKey IN (%Ls)', 80 86 $this->uniqueKeys); 87 + } 88 + 89 + if ($this->isPrimary !== null) { 90 + $where[] = qsprintf( 91 + $conn, 92 + 'isPrimary = %d', 93 + (int)$this->isPrimary); 81 94 } 82 95 83 96 return $where;
+75
src/applications/metamta/engine/PhabricatorMailSMSEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorMailSMSEngine 4 + extends PhabricatorMailMessageEngine { 5 + 6 + public function newMessage() { 7 + $mailer = $this->getMailer(); 8 + $mail = $this->getMail(); 9 + 10 + $message = new PhabricatorMailSMSMessage(); 11 + 12 + $phids = $mail->getToPHIDs(); 13 + if (!$phids) { 14 + $mail->setMessage(pht('Message has no "To" recipient.')); 15 + return null; 16 + } 17 + 18 + if (count($phids) > 1) { 19 + $mail->setMessage(pht('Message has more than one "To" recipient.')); 20 + return null; 21 + } 22 + 23 + $phid = head($phids); 24 + 25 + $actor = $this->getActor($phid); 26 + if (!$actor) { 27 + $mail->setMessage(pht('Message recipient has no mailable actor.')); 28 + return null; 29 + } 30 + 31 + if (!$actor->isDeliverable()) { 32 + $mail->setMessage(pht('Message recipient is not deliverable.')); 33 + return null; 34 + } 35 + 36 + $omnipotent = PhabricatorUser::getOmnipotentUser(); 37 + 38 + $contact_numbers = id(new PhabricatorAuthContactNumberQuery()) 39 + ->setViewer($omnipotent) 40 + ->withObjectPHIDs(array($phid)) 41 + ->withStatuses( 42 + array( 43 + PhabricatorAuthContactNumber::STATUS_ACTIVE, 44 + )) 45 + ->withIsPrimary(true) 46 + ->execute(); 47 + 48 + if (!$contact_numbers) { 49 + $mail->setMessage( 50 + pht('Message recipient has no primary contact number.')); 51 + return null; 52 + } 53 + 54 + // The database does not strictly guarantee that only one number is 55 + // primary, so make sure no one has monkeyed with stuff. 56 + if (count($contact_numbers) > 1) { 57 + $mail->setMessage( 58 + pht('Message recipient has more than one primary contact number.')); 59 + return null; 60 + } 61 + 62 + $contact_number = head($contact_numbers); 63 + $contact_number = $contact_number->getContactNumber(); 64 + $to_number = new PhabricatorPhoneNumber($contact_number); 65 + $message->setToNumber($to_number); 66 + 67 + $body = $mail->getBody(); 68 + if ($body !== null) { 69 + $message->setTextBody($body); 70 + } 71 + 72 + return $message; 73 + } 74 + 75 + }
+3 -2
src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php
··· 7 7 $this 8 8 ->setName('list-outbound') 9 9 ->setSynopsis(pht('List outbound messages sent by Phabricator.')) 10 - ->setExamples( 11 - '**list-outbound**') 10 + ->setExamples('**list-outbound**') 12 11 ->setArguments( 13 12 array( 14 13 array( ··· 39 38 ->addColumn('id', array('title' => pht('ID'))) 40 39 ->addColumn('encrypt', array('title' => pht('#'))) 41 40 ->addColumn('status', array('title' => pht('Status'))) 41 + ->addColumn('type', array('title' => pht('Type'))) 42 42 ->addColumn('subject', array('title' => pht('Subject'))); 43 43 44 44 foreach (array_reverse($mails) as $mail) { ··· 48 48 'id' => $mail->getID(), 49 49 'encrypt' => ($mail->getMustEncrypt() ? '#' : ' '), 50 50 'status' => PhabricatorMailOutboundStatus::getStatusName($status), 51 + 'type' => $mail->getMessageType(), 51 52 'subject' => $mail->getSubject(), 52 53 )); 53 54 }
+41 -14
src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php
··· 60 60 'name' => 'bulk', 61 61 'help' => pht('Send with bulk headers.'), 62 62 ), 63 + array( 64 + 'name' => 'type', 65 + 'param' => 'message-type', 66 + 'help' => pht( 67 + 'Send the specified type of message (email, sms, ...).'), 68 + ), 63 69 )); 64 70 } 65 71 ··· 67 73 $console = PhutilConsole::getConsole(); 68 74 $viewer = $this->getViewer(); 69 75 76 + $type = $args->getArg('type'); 77 + if (!strlen($type)) { 78 + $type = PhabricatorMailEmailMessage::MESSAGETYPE; 79 + } 80 + 81 + $type_map = PhabricatorMailExternalMessage::getAllMessageTypes(); 82 + if (!isset($type_map[$type])) { 83 + throw new PhutilArgumentUsageException( 84 + pht( 85 + 'Message type "%s" is unknown, supported message types are: %s.', 86 + $type, 87 + implode(', ', array_keys($type_map)))); 88 + } 89 + 70 90 $from = $args->getArg('from'); 71 91 if ($from) { 72 92 $user = id(new PhabricatorPeopleQuery()) ··· 86 106 if (!$tos && !$ccs) { 87 107 throw new PhutilArgumentUsageException( 88 108 pht( 89 - 'Specify one or more users to send mail to with `%s` and `%s`.', 90 - '--to', 91 - '--cc')); 109 + 'Specify one or more users to send a message to with "--to" and/or '. 110 + '"--cc".')); 92 111 } 93 112 94 113 $names = array_merge($tos, $ccs); ··· 166 185 $mail->setFrom($from->getPHID()); 167 186 } 168 187 188 + $mailers = PhabricatorMetaMTAMail::newMailers( 189 + array( 190 + 'media' => array($type), 191 + 'outbound' => true, 192 + )); 193 + $mailers = mpull($mailers, null, 'getKey'); 194 + 195 + if (!$mailers) { 196 + throw new PhutilArgumentUsageException( 197 + pht( 198 + 'No configured mailers support outbound messages of type "%s".', 199 + $type)); 200 + } 201 + 169 202 $mailer_key = $args->getArg('mailer'); 170 203 if ($mailer_key !== null) { 171 - $mailers = PhabricatorMetaMTAMail::newMailers(array()); 172 - 173 - $mailers = mpull($mailers, null, 'getKey'); 174 204 if (!isset($mailers[$mailer_key])) { 175 205 throw new PhutilArgumentUsageException( 176 206 pht( 177 - 'Mailer key ("%s") is not configured. Available keys are: %s.', 207 + 'Mailer key ("%s") is not configured, or does not support '. 208 + 'outbound messages of type "%s". Available mailers are: %s.', 178 209 $mailer_key, 210 + $type, 179 211 implode(', ', array_keys($mailers)))); 180 - } 181 - 182 - if (!$mailers[$mailer_key]->getSupportsOutbound()) { 183 - throw new PhutilArgumentUsageException( 184 - pht( 185 - 'Mailer ("%s") is not configured to support outbound mail.', 186 - $mailer_key)); 187 212 } 188 213 189 214 $mail->setTryMailers(array($mailer_key)); ··· 196 221 $file = new PhabricatorMailAttachment($data, $name, $mime); 197 222 $mail->addAttachment($file); 198 223 } 224 + 225 + $mail->setMessageType($type); 199 226 200 227 PhabricatorWorker::setRunAllTasksInProcess(true); 201 228 $mail->save();
+4
src/applications/metamta/message/PhabricatorMailEmailMessage.php
··· 15 15 private $textBody; 16 16 private $htmlBody; 17 17 18 + public function newMailMessageEngine() { 19 + return new PhabricatorMailEmailEngine(); 20 + } 21 + 18 22 public function setFromAddress(PhutilEmailAddress $from_address) { 19 23 $this->fromAddress = $from_address; 20 24 return $this;
+7
src/applications/metamta/message/PhabricatorMailExternalMessage.php
··· 7 7 return $this->getPhobjectClassConstant('MESSAGETYPE'); 8 8 } 9 9 10 + final public static function getAllMessageTypes() { 11 + return id(new PhutilClassMapQuery()) 12 + ->setAncestorClass(__CLASS__) 13 + ->setUniqueMethod('getMessageType') 14 + ->execute(); 15 + } 16 + 10 17 }
+4
src/applications/metamta/message/PhabricatorMailSMSMessage.php
··· 8 8 private $toNumber; 9 9 private $textBody; 10 10 11 + public function newMailMessageEngine() { 12 + return new PhabricatorMailSMSEngine(); 13 + } 14 + 11 15 public function setToNumber(PhabricatorPhoneNumber $to_number) { 12 16 $this->toNumber = $to_number; 13 17 return $this;
+25 -1
src/applications/metamta/storage/PhabricatorMetaMTAMail.php
··· 400 400 return $this->getParam('cc', array()); 401 401 } 402 402 403 + public function setMessageType($message_type) { 404 + return $this->setParam('message.type', $message_type); 405 + } 406 + 407 + public function getMessageType() { 408 + return $this->getParam( 409 + 'message.type', 410 + PhabricatorMailEmailMessage::MESSAGETYPE); 411 + } 412 + 413 + 414 + 403 415 /** 404 416 * Force delivery of a message, even if recipients have preferences which 405 417 * would otherwise drop the message. ··· 529 541 $mailers = self::newMailers( 530 542 array( 531 543 'outbound' => true, 544 + 'media' => array( 545 + $this->getMessageType(), 546 + ), 532 547 )); 533 548 534 549 $try_mailers = $this->getParam('mailers.try'); ··· 699 714 $file->attachToObject($this->getPHID()); 700 715 } 701 716 717 + $type_map = PhabricatorMailExternalMessage::getAllMessageTypes(); 718 + $type = idx($type_map, $this->getMessageType()); 719 + if (!$type) { 720 + throw new Exception( 721 + pht( 722 + 'Unable to send message with unknown message type "%s".', 723 + $type)); 724 + } 725 + 702 726 $exceptions = array(); 703 727 foreach ($mailers as $mailer) { 704 728 try { 705 - $message = id(new PhabricatorMailEmailEngine()) 729 + $message = $type->newMailMessageEngine() 706 730 ->setMailer($mailer) 707 731 ->setMail($this) 708 732 ->setActors($actors)