mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-05 19:38:27 +01:00
Add boilerplate scaffolding for Phortune subscriptions
Summary: Ref T6881. This roughs in the major objects, support classes, and controllers. - Show subscriptions on account detail. - Browse all account subscriptions. - Link to active subsciptions from merchant detail. Test Plan: Clicked around in the UI. There's no way to create subscriptions yet, so I basically just kicked the tires on this. I probably missed a few things that I'll clean up in followups. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T6881 Differential Revision: https://secure.phabricator.com/D11482
This commit is contained in:
parent
5b6b2ee850
commit
7720b799e4
12 changed files with 744 additions and 1 deletions
20
resources/sql/autopatches/20150124.subs.1.sql
Normal file
20
resources/sql/autopatches/20150124.subs.1.sql
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_phortune.phortune_subscription (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
phid VARBINARY(64) NOT NULL,
|
||||||
|
accountPHID VARBINARY(64) NOT NULL,
|
||||||
|
merchantPHID VARBINARY(64) NOT NULL,
|
||||||
|
triggerPHID VARBINARY(64) NOT NULL,
|
||||||
|
authorPHID VARBINARY(64) NOT NULL,
|
||||||
|
subscriptionClassKey BINARY(12) NOT NULL,
|
||||||
|
subscriptionClass VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
subscriptionRefKey BINARY(12) NOT NULL,
|
||||||
|
subscriptionRef VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
metadata LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
dateCreated INT UNSIGNED NOT NULL,
|
||||||
|
dateModified INT UNSIGNED NOT NULL,
|
||||||
|
UNIQUE KEY `key_phid` (phid),
|
||||||
|
UNIQUE KEY `key_subscription` (subscriptionClassKey, subscriptionRefKey),
|
||||||
|
KEY `key_account` (accountPHID),
|
||||||
|
KEY `key_merchant` (merchantPHID)
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -2804,6 +2804,13 @@ phutil_register_library_map(array(
|
||||||
'PhortunePurchaseQuery' => 'applications/phortune/query/PhortunePurchaseQuery.php',
|
'PhortunePurchaseQuery' => 'applications/phortune/query/PhortunePurchaseQuery.php',
|
||||||
'PhortuneSchemaSpec' => 'applications/phortune/storage/PhortuneSchemaSpec.php',
|
'PhortuneSchemaSpec' => 'applications/phortune/storage/PhortuneSchemaSpec.php',
|
||||||
'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php',
|
'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php',
|
||||||
|
'PhortuneSubscription' => 'applications/phortune/storage/PhortuneSubscription.php',
|
||||||
|
'PhortuneSubscriptionImplementation' => 'applications/phortune/subscription/PhortuneSubscriptionImplementation.php',
|
||||||
|
'PhortuneSubscriptionListController' => 'applications/phortune/controller/PhortuneSubscriptionListController.php',
|
||||||
|
'PhortuneSubscriptionPHIDType' => 'applications/phortune/phid/PhortuneSubscriptionPHIDType.php',
|
||||||
|
'PhortuneSubscriptionQuery' => 'applications/phortune/query/PhortuneSubscriptionQuery.php',
|
||||||
|
'PhortuneSubscriptionSearchEngine' => 'applications/phortune/query/PhortuneSubscriptionSearchEngine.php',
|
||||||
|
'PhortuneSubscriptionTableView' => 'applications/phortune/view/PhortuneSubscriptionTableView.php',
|
||||||
'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
|
'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
|
||||||
'PhortuneWePayPaymentProvider' => 'applications/phortune/provider/PhortuneWePayPaymentProvider.php',
|
'PhortuneWePayPaymentProvider' => 'applications/phortune/provider/PhortuneWePayPaymentProvider.php',
|
||||||
'PhragmentBrowseController' => 'applications/phragment/controller/PhragmentBrowseController.php',
|
'PhragmentBrowseController' => 'applications/phragment/controller/PhragmentBrowseController.php',
|
||||||
|
@ -6144,6 +6151,15 @@ phutil_register_library_map(array(
|
||||||
'PhortunePurchaseQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhortunePurchaseQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'PhortuneSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
'PhortuneSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||||
'PhortuneStripePaymentProvider' => 'PhortunePaymentProvider',
|
'PhortuneStripePaymentProvider' => 'PhortunePaymentProvider',
|
||||||
|
'PhortuneSubscription' => array(
|
||||||
|
'PhortuneDAO',
|
||||||
|
'PhabricatorPolicyInterface',
|
||||||
|
),
|
||||||
|
'PhortuneSubscriptionListController' => 'PhortuneController',
|
||||||
|
'PhortuneSubscriptionPHIDType' => 'PhabricatorPHIDType',
|
||||||
|
'PhortuneSubscriptionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
'PhortuneSubscriptionSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
|
'PhortuneSubscriptionTableView' => 'AphrontView',
|
||||||
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
|
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
|
||||||
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
|
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
|
||||||
'PhragmentBrowseController' => 'PhragmentController',
|
'PhragmentBrowseController' => 'PhragmentController',
|
||||||
|
|
|
@ -45,6 +45,8 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
|
||||||
),
|
),
|
||||||
'order/(?:query/(?P<queryKey>[^/]+)/)?'
|
'order/(?:query/(?P<queryKey>[^/]+)/)?'
|
||||||
=> 'PhortuneCartListController',
|
=> 'PhortuneCartListController',
|
||||||
|
'subscription/(?:query/(?P<queryKey>[^/]+)/)?'
|
||||||
|
=> 'PhortuneSubscriptionListController',
|
||||||
'charge/(?:query/(?P<queryKey>[^/]+)/)?'
|
'charge/(?:query/(?P<queryKey>[^/]+)/)?'
|
||||||
=> 'PhortuneChargeListController',
|
=> 'PhortuneChargeListController',
|
||||||
),
|
),
|
||||||
|
@ -77,8 +79,10 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
|
||||||
'merchant/' => array(
|
'merchant/' => array(
|
||||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhortuneMerchantListController',
|
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhortuneMerchantListController',
|
||||||
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneMerchantEditController',
|
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneMerchantEditController',
|
||||||
'orders/(?P<merchantID>\d+)/(?:query/(?P<querKey>[^/]+)/)?'
|
'orders/(?P<merchantID>\d+)/(?:query/(?P<queryKey>[^/]+)/)?'
|
||||||
=> 'PhortuneCartListController',
|
=> 'PhortuneCartListController',
|
||||||
|
'subscription/(?P<merchantID>\d+)/(?:query/(?P<queryKey>[^/]+)/)?'
|
||||||
|
=> 'PhortuneSubscriptionListController',
|
||||||
'(?P<id>\d+)/' => 'PhortuneMerchantViewController',
|
'(?P<id>\d+)/' => 'PhortuneMerchantViewController',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -70,6 +70,8 @@ final class PhortuneAccountViewController extends PhortuneController {
|
||||||
$payment_methods = $this->buildPaymentMethodsSection($account);
|
$payment_methods = $this->buildPaymentMethodsSection($account);
|
||||||
$purchase_history = $this->buildPurchaseHistorySection($account);
|
$purchase_history = $this->buildPurchaseHistorySection($account);
|
||||||
$charge_history = $this->buildChargeHistorySection($account);
|
$charge_history = $this->buildChargeHistorySection($account);
|
||||||
|
$subscriptions = $this->buildSubscriptionsSection($account);
|
||||||
|
|
||||||
$timeline = $this->buildTransactionTimeline(
|
$timeline = $this->buildTransactionTimeline(
|
||||||
$account,
|
$account,
|
||||||
new PhortuneAccountTransactionQuery());
|
new PhortuneAccountTransactionQuery());
|
||||||
|
@ -86,6 +88,7 @@ final class PhortuneAccountViewController extends PhortuneController {
|
||||||
$payment_methods,
|
$payment_methods,
|
||||||
$purchase_history,
|
$purchase_history,
|
||||||
$charge_history,
|
$charge_history,
|
||||||
|
$subscriptions,
|
||||||
$timeline,
|
$timeline,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
|
@ -259,6 +262,39 @@ final class PhortuneAccountViewController extends PhortuneController {
|
||||||
->appendChild($table);
|
->appendChild($table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildSubscriptionsSection(PhortuneAccount $account) {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$viewer = $request->getUser();
|
||||||
|
|
||||||
|
$subscriptions = id(new PhortuneSubscriptionQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withAccountPHIDs(array($account->getPHID()))
|
||||||
|
->setLimit(10)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$subscriptions_uri = $this->getApplicationURI(
|
||||||
|
$account->getID().'/subscription/');
|
||||||
|
|
||||||
|
$table = id(new PhortuneSubscriptionTableView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setSubscriptions($subscriptions);
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Recent Subscriptions'))
|
||||||
|
->addActionLink(
|
||||||
|
id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setIcon(
|
||||||
|
id(new PHUIIconView())
|
||||||
|
->setIconFont('fa-list'))
|
||||||
|
->setHref($subscriptions_uri)
|
||||||
|
->setText(pht('View All Subscriptions')));
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($header)
|
||||||
|
->appendChild($table);
|
||||||
|
}
|
||||||
|
|
||||||
protected function buildApplicationCrumbs() {
|
protected function buildApplicationCrumbs() {
|
||||||
$crumbs = parent::buildApplicationCrumbs();
|
$crumbs = parent::buildApplicationCrumbs();
|
||||||
|
|
||||||
|
|
|
@ -186,6 +186,14 @@ final class PhortuneMerchantViewController
|
||||||
->setDisabled(!$can_edit)
|
->setDisabled(!$can_edit)
|
||||||
->setWorkflow(!$can_edit));
|
->setWorkflow(!$can_edit));
|
||||||
|
|
||||||
|
$view->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setName(pht('View Subscriptions'))
|
||||||
|
->setIcon('fa-moon-o')
|
||||||
|
->setHref($this->getApplicationURI("merchant/subscription/{$id}/"))
|
||||||
|
->setDisabled(!$can_edit)
|
||||||
|
->setWorkflow(!$can_edit));
|
||||||
|
|
||||||
return $view;
|
return $view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortuneSubscriptionListController
|
||||||
|
extends PhortuneController {
|
||||||
|
|
||||||
|
private $accountID;
|
||||||
|
private $merchantID;
|
||||||
|
private $queryKey;
|
||||||
|
|
||||||
|
private $merchant;
|
||||||
|
private $account;
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->merchantID = idx($data, 'merchantID');
|
||||||
|
$this->accountID = idx($data, 'accountID');
|
||||||
|
$this->queryKey = idx($data, 'queryKey');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$viewer = $request->getUser();
|
||||||
|
|
||||||
|
$engine = new PhortuneSubscriptionSearchEngine();
|
||||||
|
|
||||||
|
if ($this->merchantID) {
|
||||||
|
$merchant = id(new PhortuneMerchantQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($this->merchantID))
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$merchant) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
$this->merchant = $merchant;
|
||||||
|
$engine->setMerchant($merchant);
|
||||||
|
} else if ($this->accountID) {
|
||||||
|
$account = id(new PhortuneAccountQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($this->accountID))
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$account) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
$this->account = $account;
|
||||||
|
$engine->setAccount($account);
|
||||||
|
} else {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$controller = id(new PhabricatorApplicationSearchController())
|
||||||
|
->setQueryKey($this->queryKey)
|
||||||
|
->setSearchEngine($engine)
|
||||||
|
->setNavigation($this->buildSideNavView());
|
||||||
|
|
||||||
|
return $this->delegateToController($controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildSideNavView() {
|
||||||
|
$viewer = $this->getRequest()->getUser();
|
||||||
|
|
||||||
|
$nav = new AphrontSideNavFilterView();
|
||||||
|
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||||
|
|
||||||
|
id(new PhortuneSubscriptionSearchEngine())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->addNavigationItems($nav->getMenu());
|
||||||
|
|
||||||
|
$nav->selectFilter(null);
|
||||||
|
|
||||||
|
return $nav;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildApplicationCrumbs() {
|
||||||
|
$crumbs = parent::buildApplicationCrumbs();
|
||||||
|
|
||||||
|
$merchant = $this->merchant;
|
||||||
|
if ($merchant) {
|
||||||
|
$id = $merchant->getID();
|
||||||
|
$crumbs->addTextCrumb(
|
||||||
|
$merchant->getName(),
|
||||||
|
$this->getApplicationURI("merchant/{$id}/"));
|
||||||
|
$crumbs->addTextCrumb(
|
||||||
|
pht('Subscriptions'),
|
||||||
|
$this->getApplicationURI("merchant/subscriptions/{$id}/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
$account = $this->account;
|
||||||
|
if ($account) {
|
||||||
|
$id = $account->getID();
|
||||||
|
$crumbs->addTextCrumb(
|
||||||
|
$account->getName(),
|
||||||
|
$this->getApplicationURI("{$id}/"));
|
||||||
|
$crumbs->addTextCrumb(
|
||||||
|
pht('Subscriptions'),
|
||||||
|
$this->getApplicationURI("{$id}/subscription/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $crumbs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortuneSubscriptionPHIDType extends PhabricatorPHIDType {
|
||||||
|
|
||||||
|
const TYPECONST = 'PSUB';
|
||||||
|
|
||||||
|
public function getTypeName() {
|
||||||
|
return pht('Phortune Subscription');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newObject() {
|
||||||
|
return new PhortuneSubscription();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildQueryForObjects(
|
||||||
|
PhabricatorObjectQuery $query,
|
||||||
|
array $phids) {
|
||||||
|
|
||||||
|
return id(new PhortuneSubscriptionQuery())
|
||||||
|
->withPHIDs($phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadHandles(
|
||||||
|
PhabricatorHandleQuery $query,
|
||||||
|
array $handles,
|
||||||
|
array $objects) {
|
||||||
|
|
||||||
|
foreach ($handles as $phid => $handle) {
|
||||||
|
$subscription = $objects[$phid];
|
||||||
|
|
||||||
|
$id = $subscription->getID();
|
||||||
|
|
||||||
|
// TODO: Flesh this out.
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
152
src/applications/phortune/query/PhortuneSubscriptionQuery.php
Normal file
152
src/applications/phortune/query/PhortuneSubscriptionQuery.php
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortuneSubscriptionQuery
|
||||||
|
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
|
|
||||||
|
private $ids;
|
||||||
|
private $phids;
|
||||||
|
private $accountPHIDs;
|
||||||
|
private $merchantPHIDs;
|
||||||
|
private $statuses;
|
||||||
|
|
||||||
|
public function withIDs(array $ids) {
|
||||||
|
$this->ids = $ids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withPHIDs(array $phids) {
|
||||||
|
$this->phids = $phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withAccountPHIDs(array $account_phids) {
|
||||||
|
$this->accountPHIDs = $account_phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withMerchantPHIDs(array $merchant_phids) {
|
||||||
|
$this->merchantPHIDs = $merchant_phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withStatuses(array $statuses) {
|
||||||
|
$this->statuses = $statuses;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadPage() {
|
||||||
|
$table = new PhortuneSubscription();
|
||||||
|
$conn = $table->establishConnection('r');
|
||||||
|
|
||||||
|
$rows = queryfx_all(
|
||||||
|
$conn,
|
||||||
|
'SELECT subscription.* FROM %T subscription %Q %Q %Q',
|
||||||
|
$table->getTableName(),
|
||||||
|
$this->buildWhereClause($conn),
|
||||||
|
$this->buildOrderClause($conn),
|
||||||
|
$this->buildLimitClause($conn));
|
||||||
|
|
||||||
|
return $table->loadAllFromArray($rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function willFilterPage(array $subscriptions) {
|
||||||
|
$accounts = id(new PhortuneAccountQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withPHIDs(mpull($subscriptions, 'getAccountPHID'))
|
||||||
|
->execute();
|
||||||
|
$accounts = mpull($accounts, null, 'getPHID');
|
||||||
|
|
||||||
|
foreach ($subscriptions as $key => $subscription) {
|
||||||
|
$account = idx($accounts, $subscription->getAccountPHID());
|
||||||
|
if (!$account) {
|
||||||
|
unset($subscriptions[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$subscription->attachAccount($account);
|
||||||
|
}
|
||||||
|
|
||||||
|
$merchants = id(new PhortuneMerchantQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withPHIDs(mpull($subscriptions, 'getMerchantPHID'))
|
||||||
|
->execute();
|
||||||
|
$merchants = mpull($merchants, null, 'getPHID');
|
||||||
|
|
||||||
|
foreach ($subscriptions as $key => $subscription) {
|
||||||
|
$merchant = idx($merchants, $subscription->getMerchantPHID());
|
||||||
|
if (!$merchant) {
|
||||||
|
unset($subscriptions[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$subscription->attachMerchant($merchant);
|
||||||
|
}
|
||||||
|
|
||||||
|
$implementations = array();
|
||||||
|
|
||||||
|
$subscription_map = mgroup($subscriptions, 'getSubscriptionClass');
|
||||||
|
foreach ($subscription_map as $class => $class_subscriptions) {
|
||||||
|
$sub = newv($class, array());
|
||||||
|
$implementations += $sub->loadImplementationsForSubscriptions(
|
||||||
|
$this->getViewer(),
|
||||||
|
$class_subscriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($subscriptions as $key => $subscription) {
|
||||||
|
$implementation = idx($implementations, $key);
|
||||||
|
if (!$implementation) {
|
||||||
|
unset($subscriptions[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$subscription->attachImplementation($implementation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $subscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildWhereClause(AphrontDatabaseConnection $conn) {
|
||||||
|
$where = array();
|
||||||
|
|
||||||
|
$where[] = $this->buildPagingClause($conn);
|
||||||
|
|
||||||
|
if ($this->ids !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'subscription.id IN (%Ld)',
|
||||||
|
$this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->phids !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'subscription.phid IN (%Ls)',
|
||||||
|
$this->phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->accountPHIDs !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'subscription.accountPHID IN (%Ls)',
|
||||||
|
$this->accountPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->merchantPHIDs !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'subscription.merchantPHID IN (%Ls)',
|
||||||
|
$this->merchantPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->statuses !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'subscription.status IN (%Ls)',
|
||||||
|
$this->statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->formatWhereClause($where);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryApplicationClass() {
|
||||||
|
return 'PhabricatorPhortuneApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortuneSubscriptionSearchEngine
|
||||||
|
extends PhabricatorApplicationSearchEngine {
|
||||||
|
|
||||||
|
private $merchant;
|
||||||
|
private $account;
|
||||||
|
|
||||||
|
public function setAccount(PhortuneAccount $account) {
|
||||||
|
$this->account = $account;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAccount() {
|
||||||
|
return $this->account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMerchant(PhortuneMerchant $merchant) {
|
||||||
|
$this->merchant = $merchant;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMerchant() {
|
||||||
|
return $this->merchant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResultTypeDescription() {
|
||||||
|
return pht('Phortune Subscriptions');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
||||||
|
$saved = new PhabricatorSavedQuery();
|
||||||
|
|
||||||
|
return $saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||||
|
$query = id(new PhortuneSubscriptionQuery());
|
||||||
|
|
||||||
|
$viewer = $this->requireViewer();
|
||||||
|
|
||||||
|
$merchant = $this->getMerchant();
|
||||||
|
$account = $this->getAccount();
|
||||||
|
if ($merchant) {
|
||||||
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$viewer,
|
||||||
|
$merchant,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
if (!$can_edit) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'You can not query subscriptions for a merchant you do not '.
|
||||||
|
'control.'));
|
||||||
|
}
|
||||||
|
$query->withMerchantPHIDs(array($merchant->getPHID()));
|
||||||
|
} else if ($account) {
|
||||||
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$viewer,
|
||||||
|
$account,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
if (!$can_edit) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'You can not query subscriptions for an account you are not '.
|
||||||
|
'a member of.'));
|
||||||
|
}
|
||||||
|
$query->withAccountPHIDs(array($account->getPHID()));
|
||||||
|
} else {
|
||||||
|
$accounts = id(new PhortuneAccountQuery())
|
||||||
|
->withMemberPHIDs(array($viewer->getPHID()))
|
||||||
|
->execute();
|
||||||
|
if ($accounts) {
|
||||||
|
$query->withAccountPHIDs(mpull($accounts, 'getPHID'));
|
||||||
|
} else {
|
||||||
|
throw new Exception(pht('You have no accounts!'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildSearchForm(
|
||||||
|
AphrontFormView $form,
|
||||||
|
PhabricatorSavedQuery $saved_query) {}
|
||||||
|
|
||||||
|
protected function getURI($path) {
|
||||||
|
$merchant = $this->getMerchant();
|
||||||
|
$account = $this->getAccount();
|
||||||
|
if ($merchant) {
|
||||||
|
return '/phortune/merchant/'.$merchant->getID().'/subscription/'.$path;
|
||||||
|
} else if ($account) {
|
||||||
|
return '/phortune/'.$account->getID().'/subscription/';
|
||||||
|
} else {
|
||||||
|
return '/phortune/subscription/'.$path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getBuiltinQueryNames() {
|
||||||
|
$names = array(
|
||||||
|
'all' => pht('All Subscriptions'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $names;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildSavedQueryFromBuiltin($query_key) {
|
||||||
|
|
||||||
|
$query = $this->newSavedQuery();
|
||||||
|
$query->setQueryKey($query_key);
|
||||||
|
|
||||||
|
switch ($query_key) {
|
||||||
|
case 'all':
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRequiredHandlePHIDsForResultList(
|
||||||
|
array $subscriptions,
|
||||||
|
PhabricatorSavedQuery $query) {
|
||||||
|
$phids = array();
|
||||||
|
foreach ($subscriptions as $subscription) {
|
||||||
|
$phids[] = $subscription->getPHID();
|
||||||
|
$phids[] = $subscription->getMerchantPHID();
|
||||||
|
$phids[] = $subscription->getAuthorPHID();
|
||||||
|
}
|
||||||
|
return $phids;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderResultList(
|
||||||
|
array $subscriptions,
|
||||||
|
PhabricatorSavedQuery $query,
|
||||||
|
array $handles) {
|
||||||
|
assert_instances_of($subscriptions, 'PhortuneSubscription');
|
||||||
|
|
||||||
|
$viewer = $this->requireViewer();
|
||||||
|
|
||||||
|
$table = id(new PhortuneSubscriptionTableView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setSubscriptions($subscriptions);
|
||||||
|
|
||||||
|
$merchant = $this->getMerchant();
|
||||||
|
if ($merchant) {
|
||||||
|
$header = pht('Subscriptions for %s', $merchant->getName());
|
||||||
|
} else {
|
||||||
|
$header = pht('Your Subscriptions');
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText($header)
|
||||||
|
->appendChild($table);
|
||||||
|
}
|
||||||
|
}
|
131
src/applications/phortune/storage/PhortuneSubscription.php
Normal file
131
src/applications/phortune/storage/PhortuneSubscription.php
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A subscription bills users regularly.
|
||||||
|
*/
|
||||||
|
final class PhortuneSubscription extends PhortuneDAO
|
||||||
|
implements PhabricatorPolicyInterface {
|
||||||
|
|
||||||
|
const STATUS_ACTIVE = 'active';
|
||||||
|
const STATUS_CANCELLED = 'cancelled';
|
||||||
|
|
||||||
|
protected $accountPHID;
|
||||||
|
protected $merchantPHID;
|
||||||
|
protected $triggerPHID;
|
||||||
|
protected $authorPHID;
|
||||||
|
protected $subscriptionClassKey;
|
||||||
|
protected $subscriptionClass;
|
||||||
|
protected $subscriptionRefKey;
|
||||||
|
protected $subscriptionRef;
|
||||||
|
protected $status;
|
||||||
|
protected $metadata = array();
|
||||||
|
|
||||||
|
private $merchant = self::ATTACHABLE;
|
||||||
|
private $account = self::ATTACHABLE;
|
||||||
|
private $implementation = self::ATTACHABLE;
|
||||||
|
private $trigger = self::ATTACHABLE;
|
||||||
|
|
||||||
|
protected function getConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::CONFIG_AUX_PHID => true,
|
||||||
|
self::CONFIG_SERIALIZATION => array(
|
||||||
|
'metadata' => self::SERIALIZATION_JSON,
|
||||||
|
),
|
||||||
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
|
'subscriptionClassKey' => 'bytes12',
|
||||||
|
'subscriptionClass' => 'text128',
|
||||||
|
'subscriptionRefKey' => 'bytes12',
|
||||||
|
'subscriptionRef' => 'text128',
|
||||||
|
'status' => 'text32',
|
||||||
|
),
|
||||||
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
'key_subscription' => array(
|
||||||
|
'columns' => array('subscriptionClassKey', 'subscriptionRefKey'),
|
||||||
|
'unique' => true,
|
||||||
|
),
|
||||||
|
'key_account' => array(
|
||||||
|
'columns' => array('accountPHID'),
|
||||||
|
),
|
||||||
|
'key_merchant' => array(
|
||||||
|
'columns' => array('merchantPHID'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) + parent::getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generatePHID() {
|
||||||
|
return PhabricatorPHID::generateNewPHID(
|
||||||
|
PhortuneSubscriptionPHIDType::TYPECONST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function initializeNewSubscription() {
|
||||||
|
return id(new PhortuneSubscription());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachImplementation(
|
||||||
|
PhortuneSubscriptionImplementation $impl) {
|
||||||
|
$this->implementation = $impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getImplementation() {
|
||||||
|
return $this->assertAttached($this->implementation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save() {
|
||||||
|
$this->subscriptionClassKey = PhabricatorHash::digestForIndex(
|
||||||
|
$this->subscriptionClass);
|
||||||
|
|
||||||
|
$this->subscriptionRefKey = PhabricatorHash::digestForIndex(
|
||||||
|
$this->subscriptionRef);
|
||||||
|
|
||||||
|
return parent::save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function getCapabilities() {
|
||||||
|
return array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPolicy($capability) {
|
||||||
|
// NOTE: Both view and edit use the account's edit policy. We punch a hole
|
||||||
|
// through this for merchants, below.
|
||||||
|
return $this
|
||||||
|
->getAccount()
|
||||||
|
->getPolicy(PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||||
|
if ($this->getAccount()->hasAutomaticCapability($capability, $viewer)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the viewer controls the merchant this subscription bills to, they can
|
||||||
|
// view the subscription.
|
||||||
|
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
|
||||||
|
$can_admin = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$viewer,
|
||||||
|
$this->getMerchant(),
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
if ($can_admin) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAutomaticCapability($capability) {
|
||||||
|
return array(
|
||||||
|
pht('Subscriptions inherit the policies of the associated account.'),
|
||||||
|
pht(
|
||||||
|
'The merchant you are subscribed with can review and manage the '.
|
||||||
|
'subscription.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhortuneSubscriptionImplementation {
|
||||||
|
|
||||||
|
abstract public function loadImplementationsForRefs(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
array $refs);
|
||||||
|
|
||||||
|
abstract public function getRef();
|
||||||
|
abstract public function getName(PhortuneSubscription $subscription);
|
||||||
|
|
||||||
|
protected function getContentSource() {
|
||||||
|
return PhabricatorContentSource::newForSource(
|
||||||
|
PhabricatorContentSource::SOURCE_PHORTUNE,
|
||||||
|
array());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortuneSubscriptionTableView extends AphrontView {
|
||||||
|
|
||||||
|
private $subscriptions;
|
||||||
|
private $handles;
|
||||||
|
|
||||||
|
public function setHandles(array $handles) {
|
||||||
|
$this->handles = $handles;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHandles() {
|
||||||
|
return $this->handles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSubscriptions(array $subscriptions) {
|
||||||
|
$this->subscriptions = $subscriptions;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubscriptions() {
|
||||||
|
return $this->subscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
$subscriptions = $this->getSubscriptions();
|
||||||
|
$handles = $this->getHandles();
|
||||||
|
$viewer = $this->getUser();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
$rowc = array();
|
||||||
|
foreach ($subscriptions as $subscription) {
|
||||||
|
$subscription_link = $handles[$subscription->getPHID()]->renderLink();
|
||||||
|
$rows[] = array(
|
||||||
|
$subscription->getID(),
|
||||||
|
phabricator_datetime($subscription->getDateCreated(), $viewer),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = id(new AphrontTableView($rows))
|
||||||
|
->setHeaders(
|
||||||
|
array(
|
||||||
|
pht('ID'),
|
||||||
|
pht('Created'),
|
||||||
|
))
|
||||||
|
->setColumnClasses(
|
||||||
|
array(
|
||||||
|
'',
|
||||||
|
'right',
|
||||||
|
));
|
||||||
|
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue