@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.

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

+216 -13
+2
src/__phutil_library_map__.php
··· 5299 5299 'PhortuneDisplayException' => 'applications/phortune/exception/PhortuneDisplayException.php', 5300 5300 'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php', 5301 5301 'PhortuneExternalController' => 'applications/phortune/controller/external/PhortuneExternalController.php', 5302 + 'PhortuneExternalOrderController' => 'applications/phortune/controller/external/PhortuneExternalOrderController.php', 5302 5303 'PhortuneExternalOverviewController' => 'applications/phortune/controller/external/PhortuneExternalOverviewController.php', 5303 5304 'PhortuneExternalUnsubscribeController' => 'applications/phortune/controller/external/PhortuneExternalUnsubscribeController.php', 5304 5305 'PhortuneInvoiceView' => 'applications/phortune/view/PhortuneInvoiceView.php', ··· 11891 11892 'PhortuneDisplayException' => 'Exception', 11892 11893 'PhortuneErrCode' => 'PhortuneConstants', 11893 11894 'PhortuneExternalController' => 'PhortuneController', 11895 + 'PhortuneExternalOrderController' => 'PhortuneExternalController', 11894 11896 'PhortuneExternalOverviewController' => 'PhortuneExternalController', 11895 11897 'PhortuneExternalUnsubscribeController' => 'PhortuneExternalController', 11896 11898 'PhortuneInvoiceView' => 'AphrontTagView',
+3
src/applications/phortune/application/PhabricatorPhortuneApplication.php
··· 112 112 'external/(?P<addressKey>[^/]+)/(?P<accessKey>[^/]+)/' => array( 113 113 '' => 'PhortuneExternalOverviewController', 114 114 'unsubscribe/' => 'PhortuneExternalUnsubscribeController', 115 + 'order/' => array( 116 + '(?P<orderID>[^/]+)/' => 'PhortuneExternalOrderController', 117 + ), 115 118 ), 116 119 'merchant/' => array( 117 120 $this->getQueryRoutePattern()
+14 -11
src/applications/phortune/controller/external/PhortuneExternalController.php
··· 127 127 128 128 $crumb = id(new PHUICrumbView()) 129 129 ->setIcon('fa-diamond') 130 - ->setName($crumb_name); 131 - 132 - $can_see = PhabricatorPolicyFilter::hasCapability( 133 - $viewer, 134 - $account, 135 - PhabricatorPolicyCapability::CAN_VIEW); 136 - if ($can_see) { 137 - $crumb->setHref($account->getURI()); 138 - } 130 + ->setName($crumb_name) 131 + ->setHref($email->getExternalURI()); 139 132 140 133 $crumbs 141 - ->addCrumb($crumb) 142 - ->addTextCrumb(pht('Viewing As "%s"', $email->getAddress())); 134 + ->addCrumb($crumb); 143 135 } else { 144 136 $crumb = id(new PHUICrumbView()) 145 137 ->setIcon('fa-diamond') ··· 153 145 154 146 final protected function newExternalView() { 155 147 $email = $this->getAccountEmail(); 148 + $xviewer = $this->getExternalViewer(); 149 + 150 + $origin_phid = $email->getAuthorPHID(); 151 + 152 + $handles = $xviewer->loadHandles(array($origin_phid)); 153 + 156 154 157 155 $messages = array(); 158 156 $messages[] = pht( 159 157 'You are viewing this payment account as: %s', 160 158 phutil_tag('strong', array(), $email->getAddress())); 159 + 160 + $messages[] = pht( 161 + 'This email address was added to this payment account by: %s', 162 + phutil_tag('strong', array(), $handles[$origin_phid]->getFullName())); 163 + 161 164 $messages[] = pht( 162 165 'Anyone who has a link to this page can view order history for '. 163 166 'this payment account.');
+40
src/applications/phortune/controller/external/PhortuneExternalOrderController.php
··· 1 + <?php 2 + 3 + final class PhortuneExternalOrderController 4 + extends PhortuneExternalController { 5 + 6 + protected function handleExternalRequest(AphrontRequest $request) { 7 + $xviewer = $this->getExternalViewer(); 8 + $email = $this->getAccountEmail(); 9 + $account = $email->getAccount(); 10 + 11 + $order = id(new PhortuneCartQuery()) 12 + ->setViewer($xviewer) 13 + ->withAccountPHIDs(array($account->getPHID())) 14 + ->withIDs(array($request->getURIData('orderID'))) 15 + ->executeOne(); 16 + if (!$order) { 17 + return new Aphront404Response(); 18 + } 19 + 20 + $timeline = $this->buildTransactionTimeline( 21 + $order, 22 + new PhortuneCartTransactionQuery()); 23 + $timeline->setShouldTerminate(true); 24 + 25 + $crumbs = $this->newExternalCrumbs() 26 + ->addTextCrumb($order->getObjectName()); 27 + 28 + $view = id(new PHUITwoColumnView()) 29 + ->setMainColumn( 30 + array( 31 + $timeline, 32 + )); 33 + 34 + return $this->newPage() 35 + ->setTitle(pht('Order %d', $order->getID())) 36 + ->setCrumbs($crumbs) 37 + ->appendChild($view); 38 + } 39 + 40 + }
+3
src/applications/phortune/controller/external/PhortuneExternalOverviewController.php
··· 9 9 $account = $email->getAccount(); 10 10 11 11 $crumbs = $this->newExternalCrumbs() 12 + ->addTextCrumb(pht('Viewing As "%s"', $email->getAddress())) 12 13 ->setBorder(true); 13 14 14 15 $header = id(new PHUIHeaderView()) ··· 61 62 62 63 $invoices_table = id(new PhortuneOrderTableView()) 63 64 ->setViewer($xviewer) 65 + ->setAccountEmail($email) 64 66 ->setCarts($invoices) 65 67 ->setIsInvoices(true); 66 68 ··· 87 89 88 90 $receipts_table = id(new PhortuneOrderTableView()) 89 91 ->setViewer($xviewer) 92 + ->setAccountEmail($email) 90 93 ->setCarts($receipts); 91 94 92 95 return id(new PHUIObjectBoxView())
+97
src/applications/phortune/editor/PhortuneCartEditor.php
··· 246 246 return $xactions; 247 247 } 248 248 249 + protected function newAuxiliaryMail($object, array $xactions) { 250 + $xviewer = PhabricatorUser::getOmnipotentUser(); 251 + $account = $object->getAccount(); 252 + 253 + $addresses = id(new PhortuneAccountEmailQuery()) 254 + ->setViewer($xviewer) 255 + ->withAccountPHIDs(array($account->getPHID())) 256 + ->withStatuses( 257 + array( 258 + PhortuneAccountEmailStatus::STATUS_ACTIVE, 259 + )) 260 + ->execute(); 261 + 262 + $messages = array(); 263 + foreach ($addresses as $address) { 264 + $message = $this->newExternalMail($address, $object, $xactions); 265 + if ($message) { 266 + $messages[] = $message; 267 + } 268 + } 269 + 270 + return $messages; 271 + } 272 + 273 + private function newExternalMail( 274 + PhortuneAccountEmail $email, 275 + PhortuneCart $cart, 276 + array $xactions) { 277 + $xviewer = PhabricatorUser::getOmnipotentUser(); 278 + $account = $cart->getAccount(); 279 + 280 + $id = $cart->getID(); 281 + $name = $cart->getName(); 282 + 283 + $origin_user = id(new PhabricatorPeopleQuery()) 284 + ->setViewer($xviewer) 285 + ->withPHIDs(array($email->getAuthorPHID())) 286 + ->executeOne(); 287 + if (!$origin_user) { 288 + return null; 289 + } 290 + 291 + if ($this->isInvoice()) { 292 + $subject = pht('[Invoice #%d] %s', $id, $name); 293 + $order_header = pht('INVOICE DETAIL'); 294 + } else { 295 + $subject = pht('[Order #%d] %s', $id, $name); 296 + $order_header = pht('ORDER DETAIL'); 297 + } 298 + 299 + $body = id(new PhabricatorMetaMTAMailBody()) 300 + ->setViewer($xviewer) 301 + ->setContextObject($cart); 302 + 303 + $origin_username = $origin_user->getUsername(); 304 + $origin_realname = $origin_user->getRealName(); 305 + if (strlen($origin_realname)) { 306 + $origin_display = pht('%s (%s)', $origin_username, $origin_realname); 307 + } else { 308 + $origin_display = pht('%s', $origin_username); 309 + } 310 + 311 + $body->addRawSection( 312 + pht( 313 + 'This email address (%s) was added to a payment account (%s) '. 314 + 'by %s.', 315 + $email->getAddress(), 316 + $account->getName(), 317 + $origin_display)); 318 + 319 + $body->addLinkSection( 320 + $order_header, 321 + PhabricatorEnv::getProductionURI($email->getExternalOrderURI($cart))); 322 + 323 + $body->addLinkSection( 324 + pht('FULL ORDER HISTORY'), 325 + PhabricatorEnv::getProductionURI($email->getExternalURI())); 326 + 327 + $body->addLinkSection( 328 + pht('UNSUBSCRIBE'), 329 + PhabricatorEnv::getProductionURI($email->getUnsubscribeURI())); 330 + 331 + return id(new PhabricatorMetaMTAMail()) 332 + ->setFrom($this->getActingAsPHID()) 333 + ->setSubject($subject) 334 + ->addRawTos( 335 + array( 336 + $email->getAddress(), 337 + )) 338 + ->setForceDelivery(true) 339 + ->setIsBulk(true) 340 + ->setSensitiveContent(true) 341 + ->setBody($body->render()) 342 + ->setHTMLBody($body->renderHTML()); 343 + 344 + } 345 + 249 346 250 347 }
+13
src/applications/phortune/query/PhortuneAccountEmailQuery.php
··· 7 7 private $phids; 8 8 private $accountPHIDs; 9 9 private $addressKeys; 10 + private $statuses; 10 11 11 12 public function withIDs(array $ids) { 12 13 $this->ids = $ids; ··· 25 26 26 27 public function withAddressKeys(array $keys) { 27 28 $this->addressKeys = $keys; 29 + return $this; 30 + } 31 + 32 + public function withStatuses(array $statuses) { 33 + $this->statuses = $statuses; 28 34 return $this; 29 35 } 30 36 ··· 88 94 $conn, 89 95 'address.addressKey IN (%Ls)', 90 96 $this->addressKeys); 97 + } 98 + 99 + if ($this->statuses !== null) { 100 + $where[] = qsprintf( 101 + $conn, 102 + 'address.status IN (%Ls)', 103 + $this->statuses); 91 104 } 92 105 93 106 return $where;
+8
src/applications/phortune/storage/PhortuneAccountEmail.php
··· 92 92 $this->getAccessKey()); 93 93 } 94 94 95 + public function getExternalOrderURI(PhortuneCart $cart) { 96 + return urisprintf( 97 + '/phortune/external/%s/%s/order/%d/', 98 + $this->getAddressKey(), 99 + $this->getAccessKey(), 100 + $cart->getID()); 101 + } 102 + 95 103 96 104 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 97 105
+4
src/applications/phortune/storage/PhortuneCart.php
··· 656 656 return idx($this->metadata, $key, $default); 657 657 } 658 658 659 + public function getObjectName() { 660 + return pht('Order %d', $this->getID()); 661 + } 662 + 659 663 660 664 /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 661 665
+23 -2
src/applications/phortune/view/PhortuneOrderTableView.php
··· 6 6 private $noDataString; 7 7 private $isInvoices; 8 8 private $isMerchantView; 9 + private $accountEmail; 9 10 10 11 public function setCarts(array $carts) { 11 12 $this->carts = $carts; ··· 43 44 return $this->isMerchantView; 44 45 } 45 46 47 + public function setAccountEmail(PhortuneAccountEmail $account_email) { 48 + $this->accountEmail = $account_email; 49 + return $this; 50 + } 51 + 52 + public function getAccountEmail() { 53 + return $this->accountEmail; 54 + } 55 + 46 56 public function render() { 47 57 $carts = $this->getCarts(); 48 58 $viewer = $this->getUser(); 49 59 50 60 $is_invoices = $this->getIsInvoices(); 51 61 $is_merchant = $this->getIsMerchantView(); 52 - $is_external = (!$viewer->getPHID()); 62 + $is_external = (bool)$this->getAccountEmail(); 63 + 64 + $email = $this->getAccountEmail(); 53 65 54 66 $phids = array(); 55 67 foreach ($carts as $cart) { ··· 65 77 $rows = array(); 66 78 $rowc = array(); 67 79 foreach ($carts as $cart) { 68 - $cart_link = $handles[$cart->getPHID()]->renderLink(); 80 + if ($is_external) { 81 + $cart_link = phutil_tag( 82 + 'a', 83 + array( 84 + 'href' => $email->getExternalOrderURI($cart), 85 + ), 86 + $handles[$cart->getPHID()]->getName()); 87 + } else { 88 + $cart_link = $handles[$cart->getPHID()]->renderLink(); 89 + } 69 90 $purchases = $cart->getPurchases(); 70 91 71 92 if (count($purchases) == 1) {
+1
src/applications/phortune/worker/PhortuneSubscriptionWorker.php
··· 22 22 return; 23 23 } 24 24 25 + 25 26 $account = $subscription->getAccount(); 26 27 $merchant = $subscription->getMerchant(); 27 28
+8
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 1520 1520 } 1521 1521 } 1522 1522 1523 + foreach ($this->newAuxiliaryMail($object, $xactions) as $message) { 1524 + $messages[] = $message; 1525 + } 1526 + 1523 1527 // NOTE: This actually sends the mail. We do this last to reduce the chance 1524 1528 // that we send some mail, hit an exception, then send the mail again when 1525 1529 // retrying. ··· 4797 4801 } 4798 4802 4799 4803 return $extensions; 4804 + } 4805 + 4806 + protected function newAuxiliaryMail($object, array $xactions) { 4807 + return array(); 4800 4808 } 4801 4809 4802 4810 private function generateMailStamps($object, $data) {