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

Roughly support external/email user views of Phortune recipts and invoices

Summary: Ref T13366. This gives each account email address an "external portal" section so they can access invoices and receipts without an account.

Test Plan: Viewed portal as user with authority and in an incognito window.

Maniphest Tasks: T13366

Differential Revision: https://secure.phabricator.com/D20737

+293 -10
+4
src/__phutil_library_map__.php
··· 5294 5294 'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php', 5295 5295 'PhortuneDisplayException' => 'applications/phortune/exception/PhortuneDisplayException.php', 5296 5296 'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php', 5297 + 'PhortuneExternalController' => 'applications/phortune/controller/external/PhortuneExternalController.php', 5298 + 'PhortuneExternalOverviewController' => 'applications/phortune/controller/external/PhortuneExternalOverviewController.php', 5297 5299 'PhortuneInvoiceView' => 'applications/phortune/view/PhortuneInvoiceView.php', 5298 5300 'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php', 5299 5301 'PhortuneMemberHasAccountEdgeType' => 'applications/phortune/edge/PhortuneMemberHasAccountEdgeType.php', ··· 11879 11881 'PhortuneDAO' => 'PhabricatorLiskDAO', 11880 11882 'PhortuneDisplayException' => 'Exception', 11881 11883 'PhortuneErrCode' => 'PhortuneConstants', 11884 + 'PhortuneExternalController' => 'PhortuneController', 11885 + 'PhortuneExternalOverviewController' => 'PhortuneExternalController', 11882 11886 'PhortuneInvoiceView' => 'AphrontTagView', 11883 11887 'PhortuneLandingController' => 'PhortuneController', 11884 11888 'PhortuneMemberHasAccountEdgeType' => 'PhabricatorEdgeType',
+3
src/applications/phortune/application/PhabricatorPhortuneApplication.php
··· 104 104 '(?P<id>\d+)/(?P<action>[^/]+)/' 105 105 => 'PhortuneProviderActionController', 106 106 ), 107 + 'external/(?P<addressKey>[^/]+)/(?P<accessKey>[^/]+)/' => array( 108 + '' => 'PhortuneExternalOverviewController', 109 + ), 107 110 'merchant/' => array( 108 111 $this->getQueryRoutePattern() 109 112 => 'PhortuneMerchantListController',
+14
src/applications/phortune/controller/account/PhortuneAccountEmailViewController.php
··· 67 67 $account->getID(), 68 68 $address->getID())); 69 69 70 + if ($can_edit) { 71 + $external_uri = $address->getExternalURI(); 72 + } else { 73 + $external_uri = null; 74 + } 75 + 70 76 $curtain = $this->newCurtainView($account); 71 77 72 78 $curtain->addAction( ··· 76 82 ->setHref($edit_uri) 77 83 ->setDisabled(!$can_edit) 78 84 ->setWorkflow(!$can_edit)); 85 + 86 + $curtain->addAction( 87 + id(new PhabricatorActionView()) 88 + ->setName(pht('Show External View')) 89 + ->setIcon('fa-eye') 90 + ->setHref($external_uri) 91 + ->setDisabled(!$can_edit) 92 + ->setOpenInNewWindow(true)); 79 93 80 94 return $curtain; 81 95 }
+147
src/applications/phortune/controller/external/PhortuneExternalController.php
··· 1 + <?php 2 + 3 + abstract class PhortuneExternalController 4 + extends PhortuneController { 5 + 6 + private $email; 7 + 8 + final public function shouldAllowPublic() { 9 + return true; 10 + } 11 + 12 + abstract protected function handleExternalRequest(AphrontRequest $request); 13 + 14 + final protected function hasAccountEmail() { 15 + return (bool)$this->email; 16 + } 17 + 18 + final protected function getAccountEmail() { 19 + return $this->email; 20 + } 21 + 22 + final protected function getExternalViewer() { 23 + return PhabricatorUser::getOmnipotentUser(); 24 + } 25 + 26 + final public function handleRequest(AphrontRequest $request) { 27 + $address_key = $request->getURIData('addressKey'); 28 + $access_key = $request->getURIData('accessKey'); 29 + 30 + $viewer = $this->getViewer(); 31 + $xviewer = $this->getExternalViewer(); 32 + 33 + $email = id(new PhortuneAccountEmailQuery()) 34 + ->setViewer($xviewer) 35 + ->withAddressKeys(array($address_key)) 36 + ->executeOne(); 37 + if (!$email) { 38 + return new Aphront404Response(); 39 + } 40 + 41 + $account = $email->getAccount(); 42 + 43 + $can_see = PhabricatorPolicyFilter::hasCapability( 44 + $viewer, 45 + $account, 46 + PhabricatorPolicyCapability::CAN_EDIT); 47 + 48 + $email_display = phutil_tag('strong', array(), $email->getAddress()); 49 + $user_display = phutil_tag('strong', array(), $viewer->getUsername()); 50 + 51 + $actual_key = $email->getAccessKey(); 52 + if (!phutil_hashes_are_identical($access_key, $actual_key)) { 53 + $dialog = $this->newDialog() 54 + ->setTitle(pht('Email Access Link Out of Date')) 55 + ->appendParagraph( 56 + pht( 57 + 'You are trying to access this payment account as: %s', 58 + $email_display)) 59 + ->appendParagraph( 60 + pht( 61 + 'The access link you have followed is out of date and no longer '. 62 + 'works.')); 63 + 64 + if ($can_see) { 65 + $dialog->appendParagraph( 66 + pht( 67 + 'You are currently logged in as a user (%s) who has '. 68 + 'permission to manage the payment account, so you can '. 69 + 'continue to the updated link.', 70 + $user_display)); 71 + 72 + $dialog->addCancelButton( 73 + $email->getExternalURI(), 74 + pht('Continue to Updated Link')); 75 + } else { 76 + $dialog->appendParagraph( 77 + pht( 78 + 'To access information about this payment account, follow '. 79 + 'a more recent link or ask a user with access to give you '. 80 + 'an updated link.')); 81 + } 82 + 83 + return $dialog; 84 + } 85 + 86 + // TODO: Test that status is good. 87 + 88 + $this->email = $email; 89 + 90 + return $this->handleExternalRequest($request); 91 + } 92 + 93 + final protected function newExternalCrumbs() { 94 + $viewer = $this->getViewer(); 95 + 96 + $crumbs = new PHUICrumbsView(); 97 + 98 + if ($this->hasAccountEmail()) { 99 + $email = $this->getAccountEmail(); 100 + $account = $email->getAccount(); 101 + 102 + $crumb_name = pht( 103 + 'Payment Account: %s', 104 + $account->getName()); 105 + 106 + $crumb = id(new PHUICrumbView()) 107 + ->setIcon('fa-diamond') 108 + ->setName($crumb_name); 109 + 110 + $can_see = PhabricatorPolicyFilter::hasCapability( 111 + $viewer, 112 + $account, 113 + PhabricatorPolicyCapability::CAN_VIEW); 114 + if ($can_see) { 115 + $crumb->setHref($account->getURI()); 116 + } 117 + 118 + $crumbs 119 + ->addCrumb($crumb) 120 + ->addTextCrumb(pht('Viewing As "%s"', $email->getAddress())); 121 + } else { 122 + $crumb = id(new PHUICrumbView()) 123 + ->setIcon('fa-diamond') 124 + ->setText(pht('External Account View')); 125 + 126 + $crumbs->addCrumb($crumb); 127 + } 128 + 129 + return $crumbs; 130 + } 131 + 132 + final protected function newExternalView() { 133 + $email = $this->getAccountEmail(); 134 + 135 + $messages = array(); 136 + $messages[] = pht( 137 + 'You are viewing this payment account as: %s', 138 + phutil_tag('strong', array(), $email->getAddress())); 139 + $messages[] = pht( 140 + 'Anyone who has a link to this page can view order history for '. 141 + 'this payment account.'); 142 + 143 + return id(new PHUIInfoView()) 144 + ->setSeverity(PHUIInfoView::SEVERITY_WARNING) 145 + ->setErrors($messages); 146 + } 147 + }
+91
src/applications/phortune/controller/external/PhortuneExternalOverviewController.php
··· 1 + <?php 2 + 3 + final class PhortuneExternalOverviewController 4 + extends PhortuneExternalController { 5 + 6 + protected function handleExternalRequest(AphrontRequest $request) { 7 + $xviewer = $this->getExternalViewer(); 8 + $email = $this->getAccountEmail(); 9 + $account = $email->getAccount(); 10 + 11 + $crumbs = $this->newExternalCrumbs() 12 + ->setBorder(true); 13 + 14 + $header = id(new PHUIHeaderView()) 15 + ->setHeader(pht('Invoices and Receipts: %s', $account->getName())); 16 + 17 + $external_view = $this->newExternalView(); 18 + $invoices_view = $this->newInvoicesView(); 19 + $receipts_view = $this->newReceiptsView(); 20 + 21 + $column_view = id(new PHUITwoColumnView()) 22 + ->setHeader($header) 23 + ->setFooter( 24 + array( 25 + $external_view, 26 + $invoices_view, 27 + $receipts_view, 28 + )); 29 + 30 + return $this->newPage() 31 + ->setCrumbs($crumbs) 32 + ->setTitle( 33 + array( 34 + pht('Invoices and Receipts'), 35 + $account->getName(), 36 + )) 37 + ->appendChild($column_view); 38 + } 39 + 40 + private function newInvoicesView() { 41 + $xviewer = $this->getExternalViewer(); 42 + $email = $this->getAccountEmail(); 43 + $account = $email->getAccount(); 44 + 45 + $invoices = id(new PhortuneCartQuery()) 46 + ->setViewer($xviewer) 47 + ->withAccountPHIDs(array($account->getPHID())) 48 + ->needPurchases(true) 49 + ->withInvoices(true) 50 + ->execute(); 51 + 52 + $header = id(new PHUIHeaderView()) 53 + ->setHeader(pht('Invoices')); 54 + 55 + $invoices_table = id(new PhortuneOrderTableView()) 56 + ->setViewer($xviewer) 57 + ->setCarts($invoices) 58 + ->setIsInvoices(true); 59 + 60 + return id(new PHUIObjectBoxView()) 61 + ->setHeader($header) 62 + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) 63 + ->setTable($invoices_table); 64 + } 65 + 66 + private function newReceiptsView() { 67 + $xviewer = $this->getExternalViewer(); 68 + $email = $this->getAccountEmail(); 69 + $account = $email->getAccount(); 70 + 71 + $receipts = id(new PhortuneCartQuery()) 72 + ->setViewer($xviewer) 73 + ->withAccountPHIDs(array($account->getPHID())) 74 + ->needPurchases(true) 75 + ->withInvoices(false) 76 + ->execute(); 77 + 78 + $header = id(new PHUIHeaderView()) 79 + ->setHeader(pht('Receipts')); 80 + 81 + $receipts_table = id(new PhortuneOrderTableView()) 82 + ->setViewer($xviewer) 83 + ->setCarts($receipts); 84 + 85 + return id(new PHUIObjectBoxView()) 86 + ->setHeader($header) 87 + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) 88 + ->setTable($receipts_table); 89 + } 90 + 91 + }
+13
src/applications/phortune/query/PhortuneAccountEmailQuery.php
··· 6 6 private $ids; 7 7 private $phids; 8 8 private $accountPHIDs; 9 + private $addressKeys; 9 10 10 11 public function withIDs(array $ids) { 11 12 $this->ids = $ids; ··· 19 20 20 21 public function withAccountPHIDs(array $phids) { 21 22 $this->accountPHIDs = $phids; 23 + return $this; 24 + } 25 + 26 + public function withAddressKeys(array $keys) { 27 + $this->addressKeys = $keys; 22 28 return $this; 23 29 } 24 30 ··· 75 81 $conn, 76 82 'address.accountPHID IN (%Ls)', 77 83 $this->accountPHIDs); 84 + } 85 + 86 + if ($this->addressKeys !== null) { 87 + $where[] = qsprintf( 88 + $conn, 89 + 'address.addressKey IN (%Ls)', 90 + $this->addressKeys); 78 91 } 79 92 80 93 return $where;
+7
src/applications/phortune/storage/PhortuneAccountEmail.php
··· 78 78 $this->getID()); 79 79 } 80 80 81 + public function getExternalURI() { 82 + return urisprintf( 83 + '/phortune/external/%s/%s/', 84 + $this->getAddressKey(), 85 + $this->getAccessKey()); 86 + } 87 + 81 88 82 89 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 83 90
+14 -10
src/applications/phortune/view/PhortuneOrderTableView.php
··· 49 49 50 50 $is_invoices = $this->getIsInvoices(); 51 51 $is_merchant = $this->getIsMerchantView(); 52 + $is_external = (!$viewer->getPHID()); 52 53 53 54 $phids = array(); 54 55 foreach ($carts as $cart) { ··· 69 70 70 71 if (count($purchases) == 1) { 71 72 $purchase = head($purchases); 72 - $purchase_name = $handles[$purchase->getPHID()]->renderLink(); 73 + $purchase_name = $handles[$purchase->getPHID()]->getName(); 73 74 $purchases = array(); 74 75 } else { 75 76 $purchase_name = ''; 76 77 } 77 78 78 79 if ($is_invoices) { 79 - $merchant_link = $handles[$cart->getMerchantPHID()]->renderLink(); 80 + if ($is_external) { 81 + $merchant_link = $handles[$cart->getMerchantPHID()]->getName(); 82 + } else { 83 + $merchant_link = $handles[$cart->getMerchantPHID()]->renderLink(); 84 + } 80 85 } else { 81 86 $merchant_link = null; 82 87 } ··· 97 102 PhortuneCart::getNameForStatus($cart->getStatus()), 98 103 phabricator_datetime($cart->getDateModified(), $viewer), 99 104 phabricator_datetime($cart->getDateCreated(), $viewer), 100 - phutil_tag( 101 - 'a', 102 - array( 103 - 'href' => $cart->getCheckoutURI(), 104 - 'class' => 'small button button-green', 105 - ), 106 - pht('Pay Now')), 105 + id(new PHUIButtonView()) 106 + ->setTag('a') 107 + ->setColor('green') 108 + ->setHref($cart->getCheckoutURI()) 109 + ->setText(pht('Pay Now')) 110 + ->setIcon('fa-credit-card'), 107 111 ); 108 112 foreach ($purchases as $purchase) { 109 113 $id = $purchase->getID(); ··· 164 168 165 169 // We show "Pay Now" for due invoices, but not if the viewer is the 166 170 // merchant, since it doesn't make sense for them to pay. 167 - ($is_invoices && !$is_merchant), 171 + ($is_invoices && !$is_merchant && !$is_external), 168 172 )); 169 173 170 174 return $table;