Page MenuHomePhabricator

D11482.diff
No OneTemporary

D11482.diff

diff --git a/resources/sql/autopatches/20150124.subs.1.sql b/resources/sql/autopatches/20150124.subs.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150124.subs.1.sql
@@ -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};
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2804,6 +2804,13 @@
'PhortunePurchaseQuery' => 'applications/phortune/query/PhortunePurchaseQuery.php',
'PhortuneSchemaSpec' => 'applications/phortune/storage/PhortuneSchemaSpec.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',
'PhortuneWePayPaymentProvider' => 'applications/phortune/provider/PhortuneWePayPaymentProvider.php',
'PhragmentBrowseController' => 'applications/phragment/controller/PhragmentBrowseController.php',
@@ -6144,6 +6151,15 @@
'PhortunePurchaseQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhortuneStripePaymentProvider' => 'PhortunePaymentProvider',
+ 'PhortuneSubscription' => array(
+ 'PhortuneDAO',
+ 'PhabricatorPolicyInterface',
+ ),
+ 'PhortuneSubscriptionListController' => 'PhortuneController',
+ 'PhortuneSubscriptionPHIDType' => 'PhabricatorPHIDType',
+ 'PhortuneSubscriptionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhortuneSubscriptionSearchEngine' => 'PhabricatorApplicationSearchEngine',
+ 'PhortuneSubscriptionTableView' => 'AphrontView',
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
'PhragmentBrowseController' => 'PhragmentController',
diff --git a/src/applications/phortune/application/PhabricatorPhortuneApplication.php b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
--- a/src/applications/phortune/application/PhabricatorPhortuneApplication.php
+++ b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
@@ -45,6 +45,8 @@
),
'order/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneCartListController',
+ 'subscription/(?:query/(?P<queryKey>[^/]+)/)?'
+ => 'PhortuneSubscriptionListController',
'charge/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneChargeListController',
),
@@ -77,8 +79,10 @@
'merchant/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhortuneMerchantListController',
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneMerchantEditController',
- 'orders/(?P<merchantID>\d+)/(?:query/(?P<querKey>[^/]+)/)?'
+ 'orders/(?P<merchantID>\d+)/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneCartListController',
+ 'subscription/(?P<merchantID>\d+)/(?:query/(?P<queryKey>[^/]+)/)?'
+ => 'PhortuneSubscriptionListController',
'(?P<id>\d+)/' => 'PhortuneMerchantViewController',
),
),
diff --git a/src/applications/phortune/controller/PhortuneAccountViewController.php b/src/applications/phortune/controller/PhortuneAccountViewController.php
--- a/src/applications/phortune/controller/PhortuneAccountViewController.php
+++ b/src/applications/phortune/controller/PhortuneAccountViewController.php
@@ -70,6 +70,8 @@
$payment_methods = $this->buildPaymentMethodsSection($account);
$purchase_history = $this->buildPurchaseHistorySection($account);
$charge_history = $this->buildChargeHistorySection($account);
+ $subscriptions = $this->buildSubscriptionsSection($account);
+
$timeline = $this->buildTransactionTimeline(
$account,
new PhortuneAccountTransactionQuery());
@@ -86,6 +88,7 @@
$payment_methods,
$purchase_history,
$charge_history,
+ $subscriptions,
$timeline,
),
array(
@@ -259,6 +262,39 @@
->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() {
$crumbs = parent::buildApplicationCrumbs();
diff --git a/src/applications/phortune/controller/PhortuneMerchantViewController.php b/src/applications/phortune/controller/PhortuneMerchantViewController.php
--- a/src/applications/phortune/controller/PhortuneMerchantViewController.php
+++ b/src/applications/phortune/controller/PhortuneMerchantViewController.php
@@ -186,6 +186,14 @@
->setDisabled(!$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;
}
diff --git a/src/applications/phortune/controller/PhortuneSubscriptionListController.php b/src/applications/phortune/controller/PhortuneSubscriptionListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/controller/PhortuneSubscriptionListController.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php b/src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php
@@ -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.
+
+ }
+ }
+
+}
diff --git a/src/applications/phortune/query/PhortuneSubscriptionQuery.php b/src/applications/phortune/query/PhortuneSubscriptionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/query/PhortuneSubscriptionQuery.php
@@ -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';
+ }
+
+}
diff --git a/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php b/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php
@@ -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);
+ }
+}
diff --git a/src/applications/phortune/storage/PhortuneSubscription.php b/src/applications/phortune/storage/PhortuneSubscription.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/storage/PhortuneSubscription.php
@@ -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.'),
+ );
+ }
+}
diff --git a/src/applications/phortune/subscription/PhortuneSubscriptionImplementation.php b/src/applications/phortune/subscription/PhortuneSubscriptionImplementation.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/subscription/PhortuneSubscriptionImplementation.php
@@ -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());
+ }
+
+}
diff --git a/src/applications/phortune/view/PhortuneSubscriptionTableView.php b/src/applications/phortune/view/PhortuneSubscriptionTableView.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/view/PhortuneSubscriptionTableView.php
@@ -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;
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 29, 1:49 AM (8 h, 59 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6941478
Default Alt Text
D11482.diff (26 KB)

Event Timeline