From a0a38797127f091db7cdc3c782fc693206937183 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 23 Aug 2019 13:07:45 -0700 Subject: [PATCH] In Phortune, send order email to account external addresses Summary: Depends on D20738. Ref T13366. Fixes T8389. Now that the infrastructure is in place, actually send email to external addresses. Test Plan: Used `bin/phortune invoice` to generate invoices and saw associated external accounts receive mail in `bin/mail list-outbound`. Maniphest Tasks: T13366, T8389 Differential Revision: https://secure.phabricator.com/D20739 --- src/__phutil_library_map__.php | 2 + .../PhabricatorPhortuneApplication.php | 3 + .../external/PhortuneExternalController.php | 25 ++--- .../PhortuneExternalOrderController.php | 40 ++++++++ .../PhortuneExternalOverviewController.php | 3 + .../phortune/editor/PhortuneCartEditor.php | 97 +++++++++++++++++++ .../query/PhortuneAccountEmailQuery.php | 13 +++ .../phortune/storage/PhortuneAccountEmail.php | 8 ++ .../phortune/storage/PhortuneCart.php | 4 + .../phortune/view/PhortuneOrderTableView.php | 25 ++++- .../worker/PhortuneSubscriptionWorker.php | 1 + ...habricatorApplicationTransactionEditor.php | 8 ++ 12 files changed, 216 insertions(+), 13 deletions(-) create mode 100644 src/applications/phortune/controller/external/PhortuneExternalOrderController.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c4be7187bd..97de6148f6 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -5299,6 +5299,7 @@ phutil_register_library_map(array( 'PhortuneDisplayException' => 'applications/phortune/exception/PhortuneDisplayException.php', 'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php', 'PhortuneExternalController' => 'applications/phortune/controller/external/PhortuneExternalController.php', + 'PhortuneExternalOrderController' => 'applications/phortune/controller/external/PhortuneExternalOrderController.php', 'PhortuneExternalOverviewController' => 'applications/phortune/controller/external/PhortuneExternalOverviewController.php', 'PhortuneExternalUnsubscribeController' => 'applications/phortune/controller/external/PhortuneExternalUnsubscribeController.php', 'PhortuneInvoiceView' => 'applications/phortune/view/PhortuneInvoiceView.php', @@ -11891,6 +11892,7 @@ phutil_register_library_map(array( 'PhortuneDisplayException' => 'Exception', 'PhortuneErrCode' => 'PhortuneConstants', 'PhortuneExternalController' => 'PhortuneController', + 'PhortuneExternalOrderController' => 'PhortuneExternalController', 'PhortuneExternalOverviewController' => 'PhortuneExternalController', 'PhortuneExternalUnsubscribeController' => 'PhortuneExternalController', 'PhortuneInvoiceView' => 'AphrontTagView', diff --git a/src/applications/phortune/application/PhabricatorPhortuneApplication.php b/src/applications/phortune/application/PhabricatorPhortuneApplication.php index 9fc1a0ad01..fbc168eb58 100644 --- a/src/applications/phortune/application/PhabricatorPhortuneApplication.php +++ b/src/applications/phortune/application/PhabricatorPhortuneApplication.php @@ -112,6 +112,9 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication { 'external/(?P[^/]+)/(?P[^/]+)/' => array( '' => 'PhortuneExternalOverviewController', 'unsubscribe/' => 'PhortuneExternalUnsubscribeController', + 'order/' => array( + '(?P[^/]+)/' => 'PhortuneExternalOrderController', + ), ), 'merchant/' => array( $this->getQueryRoutePattern() diff --git a/src/applications/phortune/controller/external/PhortuneExternalController.php b/src/applications/phortune/controller/external/PhortuneExternalController.php index a9ae254499..2810142531 100644 --- a/src/applications/phortune/controller/external/PhortuneExternalController.php +++ b/src/applications/phortune/controller/external/PhortuneExternalController.php @@ -127,19 +127,11 @@ abstract class PhortuneExternalController $crumb = id(new PHUICrumbView()) ->setIcon('fa-diamond') - ->setName($crumb_name); - - $can_see = PhabricatorPolicyFilter::hasCapability( - $viewer, - $account, - PhabricatorPolicyCapability::CAN_VIEW); - if ($can_see) { - $crumb->setHref($account->getURI()); - } + ->setName($crumb_name) + ->setHref($email->getExternalURI()); $crumbs - ->addCrumb($crumb) - ->addTextCrumb(pht('Viewing As "%s"', $email->getAddress())); + ->addCrumb($crumb); } else { $crumb = id(new PHUICrumbView()) ->setIcon('fa-diamond') @@ -153,11 +145,22 @@ abstract class PhortuneExternalController final protected function newExternalView() { $email = $this->getAccountEmail(); + $xviewer = $this->getExternalViewer(); + + $origin_phid = $email->getAuthorPHID(); + + $handles = $xviewer->loadHandles(array($origin_phid)); + $messages = array(); $messages[] = pht( 'You are viewing this payment account as: %s', phutil_tag('strong', array(), $email->getAddress())); + + $messages[] = pht( + 'This email address was added to this payment account by: %s', + phutil_tag('strong', array(), $handles[$origin_phid]->getFullName())); + $messages[] = pht( 'Anyone who has a link to this page can view order history for '. 'this payment account.'); diff --git a/src/applications/phortune/controller/external/PhortuneExternalOrderController.php b/src/applications/phortune/controller/external/PhortuneExternalOrderController.php new file mode 100644 index 0000000000..82b231d344 --- /dev/null +++ b/src/applications/phortune/controller/external/PhortuneExternalOrderController.php @@ -0,0 +1,40 @@ +getExternalViewer(); + $email = $this->getAccountEmail(); + $account = $email->getAccount(); + + $order = id(new PhortuneCartQuery()) + ->setViewer($xviewer) + ->withAccountPHIDs(array($account->getPHID())) + ->withIDs(array($request->getURIData('orderID'))) + ->executeOne(); + if (!$order) { + return new Aphront404Response(); + } + + $timeline = $this->buildTransactionTimeline( + $order, + new PhortuneCartTransactionQuery()); + $timeline->setShouldTerminate(true); + + $crumbs = $this->newExternalCrumbs() + ->addTextCrumb($order->getObjectName()); + + $view = id(new PHUITwoColumnView()) + ->setMainColumn( + array( + $timeline, + )); + + return $this->newPage() + ->setTitle(pht('Order %d', $order->getID())) + ->setCrumbs($crumbs) + ->appendChild($view); + } + +} diff --git a/src/applications/phortune/controller/external/PhortuneExternalOverviewController.php b/src/applications/phortune/controller/external/PhortuneExternalOverviewController.php index ec360d4e60..5db74ac75e 100644 --- a/src/applications/phortune/controller/external/PhortuneExternalOverviewController.php +++ b/src/applications/phortune/controller/external/PhortuneExternalOverviewController.php @@ -9,6 +9,7 @@ final class PhortuneExternalOverviewController $account = $email->getAccount(); $crumbs = $this->newExternalCrumbs() + ->addTextCrumb(pht('Viewing As "%s"', $email->getAddress())) ->setBorder(true); $header = id(new PHUIHeaderView()) @@ -61,6 +62,7 @@ final class PhortuneExternalOverviewController $invoices_table = id(new PhortuneOrderTableView()) ->setViewer($xviewer) + ->setAccountEmail($email) ->setCarts($invoices) ->setIsInvoices(true); @@ -87,6 +89,7 @@ final class PhortuneExternalOverviewController $receipts_table = id(new PhortuneOrderTableView()) ->setViewer($xviewer) + ->setAccountEmail($email) ->setCarts($receipts); return id(new PHUIObjectBoxView()) diff --git a/src/applications/phortune/editor/PhortuneCartEditor.php b/src/applications/phortune/editor/PhortuneCartEditor.php index f95ea2450a..aef59f059a 100644 --- a/src/applications/phortune/editor/PhortuneCartEditor.php +++ b/src/applications/phortune/editor/PhortuneCartEditor.php @@ -246,5 +246,102 @@ final class PhortuneCartEditor return $xactions; } + protected function newAuxiliaryMail($object, array $xactions) { + $xviewer = PhabricatorUser::getOmnipotentUser(); + $account = $object->getAccount(); + + $addresses = id(new PhortuneAccountEmailQuery()) + ->setViewer($xviewer) + ->withAccountPHIDs(array($account->getPHID())) + ->withStatuses( + array( + PhortuneAccountEmailStatus::STATUS_ACTIVE, + )) + ->execute(); + + $messages = array(); + foreach ($addresses as $address) { + $message = $this->newExternalMail($address, $object, $xactions); + if ($message) { + $messages[] = $message; + } + } + + return $messages; + } + + private function newExternalMail( + PhortuneAccountEmail $email, + PhortuneCart $cart, + array $xactions) { + $xviewer = PhabricatorUser::getOmnipotentUser(); + $account = $cart->getAccount(); + + $id = $cart->getID(); + $name = $cart->getName(); + + $origin_user = id(new PhabricatorPeopleQuery()) + ->setViewer($xviewer) + ->withPHIDs(array($email->getAuthorPHID())) + ->executeOne(); + if (!$origin_user) { + return null; + } + + if ($this->isInvoice()) { + $subject = pht('[Invoice #%d] %s', $id, $name); + $order_header = pht('INVOICE DETAIL'); + } else { + $subject = pht('[Order #%d] %s', $id, $name); + $order_header = pht('ORDER DETAIL'); + } + + $body = id(new PhabricatorMetaMTAMailBody()) + ->setViewer($xviewer) + ->setContextObject($cart); + + $origin_username = $origin_user->getUsername(); + $origin_realname = $origin_user->getRealName(); + if (strlen($origin_realname)) { + $origin_display = pht('%s (%s)', $origin_username, $origin_realname); + } else { + $origin_display = pht('%s', $origin_username); + } + + $body->addRawSection( + pht( + 'This email address (%s) was added to a payment account (%s) '. + 'by %s.', + $email->getAddress(), + $account->getName(), + $origin_display)); + + $body->addLinkSection( + $order_header, + PhabricatorEnv::getProductionURI($email->getExternalOrderURI($cart))); + + $body->addLinkSection( + pht('FULL ORDER HISTORY'), + PhabricatorEnv::getProductionURI($email->getExternalURI())); + + $body->addLinkSection( + pht('UNSUBSCRIBE'), + PhabricatorEnv::getProductionURI($email->getUnsubscribeURI())); + + return id(new PhabricatorMetaMTAMail()) + ->setFrom($this->getActingAsPHID()) + ->setSubject($subject) + ->addRawTos( + array( + $email->getAddress(), + )) + ->setForceDelivery(true) + ->setIsBulk(true) + ->setSensitiveContent(true) + ->setBody($body->render()) + ->setHTMLBody($body->renderHTML()); + + } + } diff --git a/src/applications/phortune/query/PhortuneAccountEmailQuery.php b/src/applications/phortune/query/PhortuneAccountEmailQuery.php index 81684d1fdd..0bdfdb78dc 100644 --- a/src/applications/phortune/query/PhortuneAccountEmailQuery.php +++ b/src/applications/phortune/query/PhortuneAccountEmailQuery.php @@ -7,6 +7,7 @@ final class PhortuneAccountEmailQuery private $phids; private $accountPHIDs; private $addressKeys; + private $statuses; public function withIDs(array $ids) { $this->ids = $ids; @@ -28,6 +29,11 @@ final class PhortuneAccountEmailQuery return $this; } + public function withStatuses(array $statuses) { + $this->statuses = $statuses; + return $this; + } + public function newResultObject() { return new PhortuneAccountEmail(); } @@ -90,6 +96,13 @@ final class PhortuneAccountEmailQuery $this->addressKeys); } + if ($this->statuses !== null) { + $where[] = qsprintf( + $conn, + 'address.status IN (%Ls)', + $this->statuses); + } + return $where; } diff --git a/src/applications/phortune/storage/PhortuneAccountEmail.php b/src/applications/phortune/storage/PhortuneAccountEmail.php index c33f92928c..e6761500cb 100644 --- a/src/applications/phortune/storage/PhortuneAccountEmail.php +++ b/src/applications/phortune/storage/PhortuneAccountEmail.php @@ -92,6 +92,14 @@ final class PhortuneAccountEmail $this->getAccessKey()); } + public function getExternalOrderURI(PhortuneCart $cart) { + return urisprintf( + '/phortune/external/%s/%s/order/%d/', + $this->getAddressKey(), + $this->getAccessKey(), + $cart->getID()); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/phortune/storage/PhortuneCart.php b/src/applications/phortune/storage/PhortuneCart.php index 624a13d6b0..91171037c6 100644 --- a/src/applications/phortune/storage/PhortuneCart.php +++ b/src/applications/phortune/storage/PhortuneCart.php @@ -656,6 +656,10 @@ final class PhortuneCart extends PhortuneDAO return idx($this->metadata, $key, $default); } + public function getObjectName() { + return pht('Order %d', $this->getID()); + } + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ diff --git a/src/applications/phortune/view/PhortuneOrderTableView.php b/src/applications/phortune/view/PhortuneOrderTableView.php index 28dd1e58b2..d7868f8d49 100644 --- a/src/applications/phortune/view/PhortuneOrderTableView.php +++ b/src/applications/phortune/view/PhortuneOrderTableView.php @@ -6,6 +6,7 @@ final class PhortuneOrderTableView extends AphrontView { private $noDataString; private $isInvoices; private $isMerchantView; + private $accountEmail; public function setCarts(array $carts) { $this->carts = $carts; @@ -43,13 +44,24 @@ final class PhortuneOrderTableView extends AphrontView { return $this->isMerchantView; } + public function setAccountEmail(PhortuneAccountEmail $account_email) { + $this->accountEmail = $account_email; + return $this; + } + + public function getAccountEmail() { + return $this->accountEmail; + } + public function render() { $carts = $this->getCarts(); $viewer = $this->getUser(); $is_invoices = $this->getIsInvoices(); $is_merchant = $this->getIsMerchantView(); - $is_external = (!$viewer->getPHID()); + $is_external = (bool)$this->getAccountEmail(); + + $email = $this->getAccountEmail(); $phids = array(); foreach ($carts as $cart) { @@ -65,7 +77,16 @@ final class PhortuneOrderTableView extends AphrontView { $rows = array(); $rowc = array(); foreach ($carts as $cart) { - $cart_link = $handles[$cart->getPHID()]->renderLink(); + if ($is_external) { + $cart_link = phutil_tag( + 'a', + array( + 'href' => $email->getExternalOrderURI($cart), + ), + $handles[$cart->getPHID()]->getName()); + } else { + $cart_link = $handles[$cart->getPHID()]->renderLink(); + } $purchases = $cart->getPurchases(); if (count($purchases) == 1) { diff --git a/src/applications/phortune/worker/PhortuneSubscriptionWorker.php b/src/applications/phortune/worker/PhortuneSubscriptionWorker.php index 1c9a3f0fa0..438ebf5b1b 100644 --- a/src/applications/phortune/worker/PhortuneSubscriptionWorker.php +++ b/src/applications/phortune/worker/PhortuneSubscriptionWorker.php @@ -22,6 +22,7 @@ final class PhortuneSubscriptionWorker extends PhabricatorWorker { return; } + $account = $subscription->getAccount(); $merchant = $subscription->getMerchant(); diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 01294e308a..c17ac1ec72 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -1520,6 +1520,10 @@ abstract class PhabricatorApplicationTransactionEditor } } + foreach ($this->newAuxiliaryMail($object, $xactions) as $message) { + $messages[] = $message; + } + // NOTE: This actually sends the mail. We do this last to reduce the chance // that we send some mail, hit an exception, then send the mail again when // retrying. @@ -4799,6 +4803,10 @@ abstract class PhabricatorApplicationTransactionEditor return $extensions; } + protected function newAuxiliaryMail($object, array $xactions) { + return array(); + } + private function generateMailStamps($object, $data) { if (!$data || !is_array($data)) { return null;