mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-18 01:38:39 +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:
parent
a39a37fc0e
commit
8f6a1ab015
8 changed files with 293 additions and 10 deletions
|
@ -5294,6 +5294,8 @@ phutil_register_library_map(array(
|
||||||
'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php',
|
'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php',
|
||||||
'PhortuneDisplayException' => 'applications/phortune/exception/PhortuneDisplayException.php',
|
'PhortuneDisplayException' => 'applications/phortune/exception/PhortuneDisplayException.php',
|
||||||
'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.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',
|
'PhortuneInvoiceView' => 'applications/phortune/view/PhortuneInvoiceView.php',
|
||||||
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
|
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
|
||||||
'PhortuneMemberHasAccountEdgeType' => 'applications/phortune/edge/PhortuneMemberHasAccountEdgeType.php',
|
'PhortuneMemberHasAccountEdgeType' => 'applications/phortune/edge/PhortuneMemberHasAccountEdgeType.php',
|
||||||
|
@ -11879,6 +11881,8 @@ phutil_register_library_map(array(
|
||||||
'PhortuneDAO' => 'PhabricatorLiskDAO',
|
'PhortuneDAO' => 'PhabricatorLiskDAO',
|
||||||
'PhortuneDisplayException' => 'Exception',
|
'PhortuneDisplayException' => 'Exception',
|
||||||
'PhortuneErrCode' => 'PhortuneConstants',
|
'PhortuneErrCode' => 'PhortuneConstants',
|
||||||
|
'PhortuneExternalController' => 'PhortuneController',
|
||||||
|
'PhortuneExternalOverviewController' => 'PhortuneExternalController',
|
||||||
'PhortuneInvoiceView' => 'AphrontTagView',
|
'PhortuneInvoiceView' => 'AphrontTagView',
|
||||||
'PhortuneLandingController' => 'PhortuneController',
|
'PhortuneLandingController' => 'PhortuneController',
|
||||||
'PhortuneMemberHasAccountEdgeType' => 'PhabricatorEdgeType',
|
'PhortuneMemberHasAccountEdgeType' => 'PhabricatorEdgeType',
|
||||||
|
|
|
@ -104,6 +104,9 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
|
||||||
'(?P<id>\d+)/(?P<action>[^/]+)/'
|
'(?P<id>\d+)/(?P<action>[^/]+)/'
|
||||||
=> 'PhortuneProviderActionController',
|
=> 'PhortuneProviderActionController',
|
||||||
),
|
),
|
||||||
|
'external/(?P<addressKey>[^/]+)/(?P<accessKey>[^/]+)/' => array(
|
||||||
|
'' => 'PhortuneExternalOverviewController',
|
||||||
|
),
|
||||||
'merchant/' => array(
|
'merchant/' => array(
|
||||||
$this->getQueryRoutePattern()
|
$this->getQueryRoutePattern()
|
||||||
=> 'PhortuneMerchantListController',
|
=> 'PhortuneMerchantListController',
|
||||||
|
|
|
@ -67,6 +67,12 @@ final class PhortuneAccountEmailViewController
|
||||||
$account->getID(),
|
$account->getID(),
|
||||||
$address->getID()));
|
$address->getID()));
|
||||||
|
|
||||||
|
if ($can_edit) {
|
||||||
|
$external_uri = $address->getExternalURI();
|
||||||
|
} else {
|
||||||
|
$external_uri = null;
|
||||||
|
}
|
||||||
|
|
||||||
$curtain = $this->newCurtainView($account);
|
$curtain = $this->newCurtainView($account);
|
||||||
|
|
||||||
$curtain->addAction(
|
$curtain->addAction(
|
||||||
|
@ -77,6 +83,14 @@ final class PhortuneAccountEmailViewController
|
||||||
->setDisabled(!$can_edit)
|
->setDisabled(!$can_edit)
|
||||||
->setWorkflow(!$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;
|
return $curtain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
147
src/applications/phortune/controller/external/PhortuneExternalController.php
vendored
Normal file
147
src/applications/phortune/controller/external/PhortuneExternalController.php
vendored
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
91
src/applications/phortune/controller/external/PhortuneExternalOverviewController.php
vendored
Normal file
91
src/applications/phortune/controller/external/PhortuneExternalOverviewController.php
vendored
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ final class PhortuneAccountEmailQuery
|
||||||
private $ids;
|
private $ids;
|
||||||
private $phids;
|
private $phids;
|
||||||
private $accountPHIDs;
|
private $accountPHIDs;
|
||||||
|
private $addressKeys;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -22,6 +23,11 @@ final class PhortuneAccountEmailQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withAddressKeys(array $keys) {
|
||||||
|
$this->addressKeys = $keys;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function newResultObject() {
|
public function newResultObject() {
|
||||||
return new PhortuneAccountEmail();
|
return new PhortuneAccountEmail();
|
||||||
}
|
}
|
||||||
|
@ -77,6 +83,13 @@ final class PhortuneAccountEmailQuery
|
||||||
$this->accountPHIDs);
|
$this->accountPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->addressKeys !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'address.addressKey IN (%Ls)',
|
||||||
|
$this->addressKeys);
|
||||||
|
}
|
||||||
|
|
||||||
return $where;
|
return $where;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,13 @@ final class PhortuneAccountEmail
|
||||||
$this->getID());
|
$this->getID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getExternalURI() {
|
||||||
|
return urisprintf(
|
||||||
|
'/phortune/external/%s/%s/',
|
||||||
|
$this->getAddressKey(),
|
||||||
|
$this->getAccessKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ final class PhortuneOrderTableView extends AphrontView {
|
||||||
|
|
||||||
$is_invoices = $this->getIsInvoices();
|
$is_invoices = $this->getIsInvoices();
|
||||||
$is_merchant = $this->getIsMerchantView();
|
$is_merchant = $this->getIsMerchantView();
|
||||||
|
$is_external = (!$viewer->getPHID());
|
||||||
|
|
||||||
$phids = array();
|
$phids = array();
|
||||||
foreach ($carts as $cart) {
|
foreach ($carts as $cart) {
|
||||||
|
@ -69,14 +70,18 @@ final class PhortuneOrderTableView extends AphrontView {
|
||||||
|
|
||||||
if (count($purchases) == 1) {
|
if (count($purchases) == 1) {
|
||||||
$purchase = head($purchases);
|
$purchase = head($purchases);
|
||||||
$purchase_name = $handles[$purchase->getPHID()]->renderLink();
|
$purchase_name = $handles[$purchase->getPHID()]->getName();
|
||||||
$purchases = array();
|
$purchases = array();
|
||||||
} else {
|
} else {
|
||||||
$purchase_name = '';
|
$purchase_name = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($is_invoices) {
|
if ($is_invoices) {
|
||||||
$merchant_link = $handles[$cart->getMerchantPHID()]->renderLink();
|
if ($is_external) {
|
||||||
|
$merchant_link = $handles[$cart->getMerchantPHID()]->getName();
|
||||||
|
} else {
|
||||||
|
$merchant_link = $handles[$cart->getMerchantPHID()]->renderLink();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$merchant_link = null;
|
$merchant_link = null;
|
||||||
}
|
}
|
||||||
|
@ -97,13 +102,12 @@ final class PhortuneOrderTableView extends AphrontView {
|
||||||
PhortuneCart::getNameForStatus($cart->getStatus()),
|
PhortuneCart::getNameForStatus($cart->getStatus()),
|
||||||
phabricator_datetime($cart->getDateModified(), $viewer),
|
phabricator_datetime($cart->getDateModified(), $viewer),
|
||||||
phabricator_datetime($cart->getDateCreated(), $viewer),
|
phabricator_datetime($cart->getDateCreated(), $viewer),
|
||||||
phutil_tag(
|
id(new PHUIButtonView())
|
||||||
'a',
|
->setTag('a')
|
||||||
array(
|
->setColor('green')
|
||||||
'href' => $cart->getCheckoutURI(),
|
->setHref($cart->getCheckoutURI())
|
||||||
'class' => 'small button button-green',
|
->setText(pht('Pay Now'))
|
||||||
),
|
->setIcon('fa-credit-card'),
|
||||||
pht('Pay Now')),
|
|
||||||
);
|
);
|
||||||
foreach ($purchases as $purchase) {
|
foreach ($purchases as $purchase) {
|
||||||
$id = $purchase->getID();
|
$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
|
// 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.
|
// merchant, since it doesn't make sense for them to pay.
|
||||||
($is_invoices && !$is_merchant),
|
($is_invoices && !$is_merchant && !$is_external),
|
||||||
));
|
));
|
||||||
|
|
||||||
return $table;
|
return $table;
|
||||||
|
|
Loading…
Add table
Reference in a new issue