1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-29 10:12:41 +01:00

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
This commit is contained in:
epriestley 2019-08-23 08:07:41 -07:00
parent a39a37fc0e
commit 8f6a1ab015
8 changed files with 293 additions and 10 deletions

View file

@ -5294,6 +5294,8 @@ phutil_register_library_map(array(
'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php',
'PhortuneDisplayException' => 'applications/phortune/exception/PhortuneDisplayException.php',
'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php',
'PhortuneExternalController' => 'applications/phortune/controller/external/PhortuneExternalController.php',
'PhortuneExternalOverviewController' => 'applications/phortune/controller/external/PhortuneExternalOverviewController.php',
'PhortuneInvoiceView' => 'applications/phortune/view/PhortuneInvoiceView.php',
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
'PhortuneMemberHasAccountEdgeType' => 'applications/phortune/edge/PhortuneMemberHasAccountEdgeType.php',
@ -11879,6 +11881,8 @@ phutil_register_library_map(array(
'PhortuneDAO' => 'PhabricatorLiskDAO',
'PhortuneDisplayException' => 'Exception',
'PhortuneErrCode' => 'PhortuneConstants',
'PhortuneExternalController' => 'PhortuneController',
'PhortuneExternalOverviewController' => 'PhortuneExternalController',
'PhortuneInvoiceView' => 'AphrontTagView',
'PhortuneLandingController' => 'PhortuneController',
'PhortuneMemberHasAccountEdgeType' => 'PhabricatorEdgeType',

View file

@ -104,6 +104,9 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
'(?P<id>\d+)/(?P<action>[^/]+)/'
=> 'PhortuneProviderActionController',
),
'external/(?P<addressKey>[^/]+)/(?P<accessKey>[^/]+)/' => array(
'' => 'PhortuneExternalOverviewController',
),
'merchant/' => array(
$this->getQueryRoutePattern()
=> 'PhortuneMerchantListController',

View file

@ -67,6 +67,12 @@ final class PhortuneAccountEmailViewController
$account->getID(),
$address->getID()));
if ($can_edit) {
$external_uri = $address->getExternalURI();
} else {
$external_uri = null;
}
$curtain = $this->newCurtainView($account);
$curtain->addAction(
@ -77,6 +83,14 @@ final class PhortuneAccountEmailViewController
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Show External View'))
->setIcon('fa-eye')
->setHref($external_uri)
->setDisabled(!$can_edit)
->setOpenInNewWindow(true));
return $curtain;
}

View file

@ -0,0 +1,147 @@
<?php
abstract class PhortuneExternalController
extends PhortuneController {
private $email;
final public function shouldAllowPublic() {
return true;
}
abstract protected function handleExternalRequest(AphrontRequest $request);
final protected function hasAccountEmail() {
return (bool)$this->email;
}
final protected function getAccountEmail() {
return $this->email;
}
final protected function getExternalViewer() {
return PhabricatorUser::getOmnipotentUser();
}
final public function handleRequest(AphrontRequest $request) {
$address_key = $request->getURIData('addressKey');
$access_key = $request->getURIData('accessKey');
$viewer = $this->getViewer();
$xviewer = $this->getExternalViewer();
$email = id(new PhortuneAccountEmailQuery())
->setViewer($xviewer)
->withAddressKeys(array($address_key))
->executeOne();
if (!$email) {
return new Aphront404Response();
}
$account = $email->getAccount();
$can_see = PhabricatorPolicyFilter::hasCapability(
$viewer,
$account,
PhabricatorPolicyCapability::CAN_EDIT);
$email_display = phutil_tag('strong', array(), $email->getAddress());
$user_display = phutil_tag('strong', array(), $viewer->getUsername());
$actual_key = $email->getAccessKey();
if (!phutil_hashes_are_identical($access_key, $actual_key)) {
$dialog = $this->newDialog()
->setTitle(pht('Email Access Link Out of Date'))
->appendParagraph(
pht(
'You are trying to access this payment account as: %s',
$email_display))
->appendParagraph(
pht(
'The access link you have followed is out of date and no longer '.
'works.'));
if ($can_see) {
$dialog->appendParagraph(
pht(
'You are currently logged in as a user (%s) who has '.
'permission to manage the payment account, so you can '.
'continue to the updated link.',
$user_display));
$dialog->addCancelButton(
$email->getExternalURI(),
pht('Continue to Updated Link'));
} else {
$dialog->appendParagraph(
pht(
'To access information about this payment account, follow '.
'a more recent link or ask a user with access to give you '.
'an updated link.'));
}
return $dialog;
}
// TODO: Test that status is good.
$this->email = $email;
return $this->handleExternalRequest($request);
}
final protected function newExternalCrumbs() {
$viewer = $this->getViewer();
$crumbs = new PHUICrumbsView();
if ($this->hasAccountEmail()) {
$email = $this->getAccountEmail();
$account = $email->getAccount();
$crumb_name = pht(
'Payment Account: %s',
$account->getName());
$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());
}
$crumbs
->addCrumb($crumb)
->addTextCrumb(pht('Viewing As "%s"', $email->getAddress()));
} else {
$crumb = id(new PHUICrumbView())
->setIcon('fa-diamond')
->setText(pht('External Account View'));
$crumbs->addCrumb($crumb);
}
return $crumbs;
}
final protected function newExternalView() {
$email = $this->getAccountEmail();
$messages = array();
$messages[] = pht(
'You are viewing this payment account as: %s',
phutil_tag('strong', array(), $email->getAddress()));
$messages[] = pht(
'Anyone who has a link to this page can view order history for '.
'this payment account.');
return id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors($messages);
}
}

View file

@ -0,0 +1,91 @@
<?php
final class PhortuneExternalOverviewController
extends PhortuneExternalController {
protected function handleExternalRequest(AphrontRequest $request) {
$xviewer = $this->getExternalViewer();
$email = $this->getAccountEmail();
$account = $email->getAccount();
$crumbs = $this->newExternalCrumbs()
->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Invoices and Receipts: %s', $account->getName()));
$external_view = $this->newExternalView();
$invoices_view = $this->newInvoicesView();
$receipts_view = $this->newReceiptsView();
$column_view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$external_view,
$invoices_view,
$receipts_view,
));
return $this->newPage()
->setCrumbs($crumbs)
->setTitle(
array(
pht('Invoices and Receipts'),
$account->getName(),
))
->appendChild($column_view);
}
private function newInvoicesView() {
$xviewer = $this->getExternalViewer();
$email = $this->getAccountEmail();
$account = $email->getAccount();
$invoices = id(new PhortuneCartQuery())
->setViewer($xviewer)
->withAccountPHIDs(array($account->getPHID()))
->needPurchases(true)
->withInvoices(true)
->execute();
$header = id(new PHUIHeaderView())
->setHeader(pht('Invoices'));
$invoices_table = id(new PhortuneOrderTableView())
->setViewer($xviewer)
->setCarts($invoices)
->setIsInvoices(true);
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($invoices_table);
}
private function newReceiptsView() {
$xviewer = $this->getExternalViewer();
$email = $this->getAccountEmail();
$account = $email->getAccount();
$receipts = id(new PhortuneCartQuery())
->setViewer($xviewer)
->withAccountPHIDs(array($account->getPHID()))
->needPurchases(true)
->withInvoices(false)
->execute();
$header = id(new PHUIHeaderView())
->setHeader(pht('Receipts'));
$receipts_table = id(new PhortuneOrderTableView())
->setViewer($xviewer)
->setCarts($receipts);
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($receipts_table);
}
}

View file

@ -6,6 +6,7 @@ final class PhortuneAccountEmailQuery
private $ids;
private $phids;
private $accountPHIDs;
private $addressKeys;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -22,6 +23,11 @@ final class PhortuneAccountEmailQuery
return $this;
}
public function withAddressKeys(array $keys) {
$this->addressKeys = $keys;
return $this;
}
public function newResultObject() {
return new PhortuneAccountEmail();
}
@ -77,6 +83,13 @@ final class PhortuneAccountEmailQuery
$this->accountPHIDs);
}
if ($this->addressKeys !== null) {
$where[] = qsprintf(
$conn,
'address.addressKey IN (%Ls)',
$this->addressKeys);
}
return $where;
}

View file

@ -78,6 +78,13 @@ final class PhortuneAccountEmail
$this->getID());
}
public function getExternalURI() {
return urisprintf(
'/phortune/external/%s/%s/',
$this->getAddressKey(),
$this->getAccessKey());
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -49,6 +49,7 @@ final class PhortuneOrderTableView extends AphrontView {
$is_invoices = $this->getIsInvoices();
$is_merchant = $this->getIsMerchantView();
$is_external = (!$viewer->getPHID());
$phids = array();
foreach ($carts as $cart) {
@ -69,14 +70,18 @@ final class PhortuneOrderTableView extends AphrontView {
if (count($purchases) == 1) {
$purchase = head($purchases);
$purchase_name = $handles[$purchase->getPHID()]->renderLink();
$purchase_name = $handles[$purchase->getPHID()]->getName();
$purchases = array();
} else {
$purchase_name = '';
}
if ($is_invoices) {
if ($is_external) {
$merchant_link = $handles[$cart->getMerchantPHID()]->getName();
} else {
$merchant_link = $handles[$cart->getMerchantPHID()]->renderLink();
}
} else {
$merchant_link = null;
}
@ -97,13 +102,12 @@ final class PhortuneOrderTableView extends AphrontView {
PhortuneCart::getNameForStatus($cart->getStatus()),
phabricator_datetime($cart->getDateModified(), $viewer),
phabricator_datetime($cart->getDateCreated(), $viewer),
phutil_tag(
'a',
array(
'href' => $cart->getCheckoutURI(),
'class' => 'small button button-green',
),
pht('Pay Now')),
id(new PHUIButtonView())
->setTag('a')
->setColor('green')
->setHref($cart->getCheckoutURI())
->setText(pht('Pay Now'))
->setIcon('fa-credit-card'),
);
foreach ($purchases as $purchase) {
$id = $purchase->getID();
@ -164,7 +168,7 @@ final class PhortuneOrderTableView extends AphrontView {
// We show "Pay Now" for due invoices, but not if the viewer is the
// merchant, since it doesn't make sense for them to pay.
($is_invoices && !$is_merchant),
($is_invoices && !$is_merchant && !$is_external),
));
return $table;