1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-01 19:22:42 +01:00

Add Merchants to Phortune

Summary:
Ref T2787. Currently, you add payment providers (Stripe, Paypal, etc) in global configuration.

Generally, this approach is cumbersome, limiting, and often hard for users to figure out. It also doesn't provide a natural way to segment payment receivers or provide web access to administrative payment functions like issuing refunds, canceling orders, etc. I think that stuff definitely needs to be in the web UI, and the rule for access to it can't reasonably just be "all administrators" in a lot of reasonable cases.

The only real advantage is that it prevents an attacker from adjusting settings and pointing something at an account they control. But this attack can be mitigated through notifications, some sort of CLI-only merchant lock, payment accounts being relatively identifiable, etc.

So introduce "merchants", which are basically payable entities. An individual merchant will have attached Paypal, Stripe, etc., accounts, and access rules. When you buy something in an application, the merchant to pay is also specified. They also provide an umbrella for dealing with permissions down the line.

This may get a //little// cumbersome because if there are several merchants your saved card information is not shared across them. I think that will be fine in the normal case (most installs will have only one merchant). Even if it isn't and we leave providers global, I think introducing this is the right call from a web UI / permissions point of view. I'll play around with it in the next couple of diffs and figure out exactly where the line goes.

Test Plan: Listed, created, edited, viewed merchants.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T2787

Differential Revision: https://secure.phabricator.com/D10648
This commit is contained in:
epriestley 2014-10-07 10:55:16 -07:00
parent 61b1fe78c7
commit fcd2025a85
16 changed files with 806 additions and 0 deletions

View file

@ -0,0 +1,10 @@
CREATE TABLE {$NAMESPACE}_phortune.phortune_merchant (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
name VARCHAR(255) NOT NULL COLLATE utf8_bin,
viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (phid)
) ENGINE=InnoDB, COLLATE=utf8_bin;

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_phortune.phortune_merchanttransaction (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARCHAR(64) COLLATE utf8_bin NOT NULL,
authorPHID VARCHAR(64) COLLATE utf8_bin NOT NULL,
objectPHID VARCHAR(64) COLLATE utf8_bin NOT NULL,
viewPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL,
editPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL,
commentPHID VARCHAR(64) COLLATE utf8_bin DEFAULT NULL,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) COLLATE utf8_bin NOT NULL,
oldValue LONGTEXT COLLATE utf8_bin NOT NULL,
newValue LONGTEXT COLLATE utf8_bin NOT NULL,
contentSource LONGTEXT COLLATE utf8_bin NOT NULL,
metadata LONGTEXT COLLATE utf8_bin NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB, COLLATE utf8_general_ci;

View file

@ -2573,6 +2573,18 @@ phutil_register_library_map(array(
'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php', 'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php',
'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php', 'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php',
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php', 'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
'PhortuneMerchant' => 'applications/phortune/storage/PhortuneMerchant.php',
'PhortuneMerchantCapability' => 'applications/phortune/capability/PhortuneMerchantCapability.php',
'PhortuneMerchantController' => 'applications/phortune/controller/PhortuneMerchantController.php',
'PhortuneMerchantEditController' => 'applications/phortune/controller/PhortuneMerchantEditController.php',
'PhortuneMerchantEditor' => 'applications/phortune/editor/PhortuneMerchantEditor.php',
'PhortuneMerchantListController' => 'applications/phortune/controller/PhortuneMerchantListController.php',
'PhortuneMerchantPHIDType' => 'applications/phortune/phid/PhortuneMerchantPHIDType.php',
'PhortuneMerchantQuery' => 'applications/phortune/query/PhortuneMerchantQuery.php',
'PhortuneMerchantSearchEngine' => 'applications/phortune/query/PhortuneMerchantSearchEngine.php',
'PhortuneMerchantTransaction' => 'applications/phortune/storage/PhortuneMerchantTransaction.php',
'PhortuneMerchantTransactionQuery' => 'applications/phortune/query/PhortuneMerchantTransactionQuery.php',
'PhortuneMerchantViewController' => 'applications/phortune/controller/PhortuneMerchantViewController.php',
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php', 'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php',
'PhortuneMultiplePaymentProvidersException' => 'applications/phortune/exception/PhortuneMultiplePaymentProvidersException.php', 'PhortuneMultiplePaymentProvidersException' => 'applications/phortune/exception/PhortuneMultiplePaymentProvidersException.php',
'PhortuneNoPaymentProviderException' => 'applications/phortune/exception/PhortuneNoPaymentProviderException.php', 'PhortuneNoPaymentProviderException' => 'applications/phortune/exception/PhortuneNoPaymentProviderException.php',
@ -5608,6 +5620,21 @@ phutil_register_library_map(array(
'PhortuneDAO' => 'PhabricatorLiskDAO', 'PhortuneDAO' => 'PhabricatorLiskDAO',
'PhortuneErrCode' => 'PhortuneConstants', 'PhortuneErrCode' => 'PhortuneConstants',
'PhortuneLandingController' => 'PhortuneController', 'PhortuneLandingController' => 'PhortuneController',
'PhortuneMerchant' => array(
'PhortuneDAO',
'PhabricatorPolicyInterface',
),
'PhortuneMerchantCapability' => 'PhabricatorPolicyCapability',
'PhortuneMerchantController' => 'PhortuneController',
'PhortuneMerchantEditController' => 'PhortuneMerchantController',
'PhortuneMerchantEditor' => 'PhabricatorApplicationTransactionEditor',
'PhortuneMerchantListController' => 'PhortuneMerchantController',
'PhortuneMerchantPHIDType' => 'PhabricatorPHIDType',
'PhortuneMerchantQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneMerchantSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhortuneMerchantTransaction' => 'PhabricatorApplicationTransaction',
'PhortuneMerchantTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhortuneMerchantViewController' => 'PhortuneMerchantController',
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl', 'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
'PhortuneMultiplePaymentProvidersException' => 'Exception', 'PhortuneMultiplePaymentProvidersException' => 'Exception',
'PhortuneNoPaymentProviderException' => 'Exception', 'PhortuneNoPaymentProviderException' => 'Exception',

View file

@ -60,6 +60,20 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
), ),
'provider/(?P<digest>[^/]+)/(?P<action>[^/]+)/' 'provider/(?P<digest>[^/]+)/(?P<action>[^/]+)/'
=> 'PhortuneProviderController', => 'PhortuneProviderController',
'merchant/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhortuneMerchantListController',
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneMerchantEditController',
'(?P<id>\d+)/' => 'PhortuneMerchantViewController',
),
),
);
}
protected function getCustomCapabilities() {
return array(
PhortuneMerchantCapability::CAPABILITY => array(
'caption' => pht('Merchant accounts can receive payments.'),
'default' => PhabricatorPolicies::POLICY_ADMIN,
), ),
); );
} }

View file

@ -0,0 +1,17 @@
<?php
final class PhortuneMerchantCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'phortune.merchant';
public function getCapabilityName() {
return pht('Can Create Merchants');
}
public function describeCapabilityRejection() {
return pht(
'You do not have permission to create Phortune merchant accounts.');
}
}

View file

@ -0,0 +1,13 @@
<?php
abstract class PhortuneMerchantController
extends PhortuneController {
public function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Merchants'),
$this->getApplicationURI('merchant/'));
return $crumbs;
}
}

View file

@ -0,0 +1,155 @@
<?php
final class PhortuneMerchantEditController
extends PhortuneMerchantController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
if ($this->id) {
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$merchant) {
return new Aphront404Response();
}
$is_new = false;
} else {
$this->requireApplicationCapability(
PhortuneMerchantCapability::CAPABILITY);
$merchant = PhortuneMerchant::initializeNewMerchant($viewer);
$is_new = true;
}
if ($is_new) {
$title = pht('Create Merchant');
$button_text = pht('Create Merchant');
$cancel_uri = $this->getApplicationURI('merchant/');
} else {
$title = pht(
'Edit Merchant %d %s',
$merchant->getID(),
$merchant->getName());
$button_text = pht('Save Changes');
$cancel_uri = $this->getApplicationURI(
'/merchant/'.$merchant->getID().'/');
}
$e_name = true;
$v_name = $merchant->getName();
$validation_exception = null;
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$type_name = PhortuneMerchantTransaction::TYPE_NAME;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
$xactions = array();
$xactions[] = id(new PhortuneMerchantTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(new PhortuneMerchantTransaction())
->setTransactionType($type_view)
->setNewValue($v_view);
$xactions[] = id(new PhortuneMerchantTransaction())
->setTransactionType($type_edit)
->setNewValue($v_edit);
$editor = id(new PhortuneMerchantEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
try {
$editor->applyTransactions($merchant, $xactions);
$id = $merchant->getID();
$merchant_uri = $this->getApplicationURI("merchant/{$id}/");
return id(new AphrontRedirectResponse())->setURI($merchant_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_name = $ex->getShortMessage($type_name);
$merchant->setViewPolicy($v_view);
$merchant->setEditPolicy($v_edit);
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($merchant)
->execute();
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($merchant)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicies($policies))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($merchant)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicies($policies))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($button_text)
->addCancelButton($cancel_uri));
$crumbs = $this->buildApplicationCrumbs();
if ($is_new) {
$crumbs->addTextCrumb(pht('Create Merchant'));
} else {
$crumbs->addTextCrumb(
pht('Merchant %d', $merchant->getID()),
$this->getApplicationURI('/merchant/'.$merchant->getID().'/'));
$crumbs->addTextCrumb(pht('Edit'));
}
$box = id(new PHUIObjectBoxView())
->setValidationException($validation_exception)
->setHeaderText($title)
->appendChild($form);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
),
array(
'title' => $title,
));
}
}

View file

@ -0,0 +1,58 @@
<?php
final class PhortuneMerchantListController
extends PhortuneMerchantController {
private $queryKey;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->queryKey = idx($data, 'queryKey');
}
public function processRequest() {
$request = $this->getRequest();
$controller = id(new PhabricatorApplicationSearchController($request))
->setQueryKey($this->queryKey)
->setSearchEngine(new PhortuneMerchantSearchEngine())
->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 PhortuneMerchantSearchEngine())
->setViewer($viewer)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
public function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$can_create = $this->hasApplicationCapability(
PhortuneMerchantCapability::CAPABILITY);
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create Merchant'))
->setHref($this->getApplicationURI('merchant/edit/'))
->setIcon('fa-plus-square')
->setWorkflow(!$can_create)
->setDisabled(!$can_create));
return $crumbs;
}
}

View file

@ -0,0 +1,101 @@
<?php
final class PhortuneMerchantViewController
extends PhortuneMerchantController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->executeOne();
if (!$merchant) {
return new Aphront404Response();
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Merchant %d', $merchant->getID()));
$title = pht(
'Merchant %d %s',
$merchant->getID(),
$merchant->getName());
$header = id(new PHUIHeaderView())
->setObjectName(pht('Merchant %d', $merchant->getID()))
->setHeader($merchant->getName())
->setUser($viewer)
->setPolicyObject($merchant);
$properties = $this->buildPropertyListView($merchant);
$actions = $this->buildActionListView($merchant);
$properties->setActionList($actions);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($properties);
$xactions = id(new PhortuneMerchantTransactionQuery())
->setViewer($viewer)
->withObjectPHIDs(array($merchant->getPHID()))
->execute();
$timeline = id(new PhabricatorApplicationTransactionView())
->setUser($viewer)
->setObjectPHID($merchant->getPHID())
->setTransactions($xactions);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$timeline,
),
array(
'title' => $title,
));
}
private function buildPropertyListView(PhortuneMerchant $merchant) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($merchant);
return $view;
}
private function buildActionListView(PhortuneMerchant $merchant) {
$viewer = $this->getRequest()->getUser();
$id = $merchant->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$merchant,
PhabricatorPolicyCapability::CAN_EDIT);
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($merchant);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Merchant'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($this->getApplicationURI("merchant/edit/{$id}/")));
return $view;
}
}

View file

@ -0,0 +1,102 @@
<?php
final class PhortuneMerchantEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorPhortuneApplication';
}
public function getEditorObjectsDescription() {
return pht('Phortune Merchants');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhortuneMerchantTransaction::TYPE_NAME;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhortuneMerchantTransaction::TYPE_NAME:
return $object->getName();
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhortuneMerchantTransaction::TYPE_NAME:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhortuneMerchantTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhortuneMerchantTransaction::TYPE_NAME:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case PhortuneMerchantTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Merchant name is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
}
return $errors;
}
}

View file

@ -0,0 +1,38 @@
<?php
final class PhortuneMerchantPHIDType extends PhabricatorPHIDType {
const TYPECONST = 'PMRC';
public function getTypeName() {
return pht('Phortune Merchant');
}
public function newObject() {
return new PhortuneMerchant();
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhortuneMerchantQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$merchant = $objects[$phid];
$id = $merchant->getID();
$handle->setName(pht('Merchant %d', $id));
$handle->setURI("/phortune/merchant/{$id}/");
}
}
}

View file

@ -0,0 +1,60 @@
<?php
final class PhortuneMerchantQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
protected function loadPage() {
$table = new PhortuneMerchant();
$conn = $table->establishConnection('r');
$rows = queryfx_all(
$conn,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn),
$this->buildOrderClause($conn),
$this->buildLimitClause($conn));
return $table->loadAllFromArray($rows);
}
private function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array();
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
$where[] = $this->buildPagingClause($conn);
return $this->formatWhereClause($where);
}
public function getQueryApplicationClass() {
return 'PhabricatorPhortuneApplication';
}
}

View file

@ -0,0 +1,79 @@
<?php
final class PhortuneMerchantSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Phortune Merchants');
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhortuneMerchantQuery());
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {}
protected function getURI($path) {
return '/phortune/merchant/'.$path;
}
public function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Merchants'),
);
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 $merchants,
PhabricatorSavedQuery $query) {
return array();
}
protected function renderResultList(
array $merchants,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($merchants, 'PhortuneMerchant');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($merchants as $merchant) {
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Merchant %d', $merchant->getID()))
->setHeader($merchant->getName())
->setHref('/phortune/merchant/'.$merchant->getID().'/')
->setObject($merchant);
$list->addItem($item);
}
return $list;
}
}

View file

@ -0,0 +1,10 @@
<?php
final class PhortuneMerchantTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new PhortuneMerchantTransaction();
}
}

View file

@ -0,0 +1,58 @@
<?php
final class PhortuneMerchant extends PhortuneDAO
implements PhabricatorPolicyInterface {
protected $name;
protected $viewPolicy;
protected $editPolicy;
public static function initializeNewMerchant(PhabricatorUser $actor) {
return id(new PhortuneMerchant())
->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
->setEditPolicy($actor->getPHID());
}
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text255',
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhortuneMerchantPHIDType::TYPECONST);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
}

View file

@ -0,0 +1,45 @@
<?php
final class PhortuneMerchantTransaction
extends PhabricatorApplicationTransaction {
const TYPE_NAME = 'merchant:name';
public function getApplicationName() {
return 'phortune';
}
public function getApplicationTransactionType() {
return PhortuneMerchantPHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return null;
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_NAME:
if ($old === null) {
return pht(
'%s created this merchant.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s renamed this merchant from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
break;
}
return parent::getTitle();
}
}