1
0
Fork 0
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:
epriestley 2015-01-27 14:50:20 -08:00
parent 5b6b2ee850
commit 7720b799e4
12 changed files with 744 additions and 1 deletions

View 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};

View file

@ -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',

View file

@ -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',
), ),
), ),

View file

@ -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();

View file

@ -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;
} }

View file

@ -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;
}
}

View file

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

View 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';
}
}

View file

@ -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);
}
}

View 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.'),
);
}
}

View file

@ -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());
}
}

View file

@ -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;
}
}