Give Phortune merchants explicit members

Summary:
Ref T2787. Make this a little more concrete with explicit membership instead of a general edit policy. In particular, we need to know who to email when orders happen, and can't reasonably do that with an edit policy.

I imagine this might eventually get more nuanced (e.g., users who can only approve orders vs users who can manage the merchant itself) but that's a long ways away.

Test Plan: {F216284}

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T2787

Differential Revision: https://secure.phabricator.com/D10681
This commit is contained in:
epriestley
2014-10-13 11:13:50 -07:00
parent 3e1918d89f
commit 2d0ee77bd4
10 changed files with 216 additions and 21 deletions

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phortune.phortune_merchant
DROP editPolicy;

View File

@@ -2582,11 +2582,13 @@ phutil_register_library_map(array(
'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php', 'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php',
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php', 'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
'PhortuneMemberHasAccountEdgeType' => 'applications/phortune/edge/PhortuneMemberHasAccountEdgeType.php', 'PhortuneMemberHasAccountEdgeType' => 'applications/phortune/edge/PhortuneMemberHasAccountEdgeType.php',
'PhortuneMemberHasMerchantEdgeType' => 'applications/phortune/edge/PhortuneMemberHasMerchantEdgeType.php',
'PhortuneMerchant' => 'applications/phortune/storage/PhortuneMerchant.php', 'PhortuneMerchant' => 'applications/phortune/storage/PhortuneMerchant.php',
'PhortuneMerchantCapability' => 'applications/phortune/capability/PhortuneMerchantCapability.php', 'PhortuneMerchantCapability' => 'applications/phortune/capability/PhortuneMerchantCapability.php',
'PhortuneMerchantController' => 'applications/phortune/controller/PhortuneMerchantController.php', 'PhortuneMerchantController' => 'applications/phortune/controller/PhortuneMerchantController.php',
'PhortuneMerchantEditController' => 'applications/phortune/controller/PhortuneMerchantEditController.php', 'PhortuneMerchantEditController' => 'applications/phortune/controller/PhortuneMerchantEditController.php',
'PhortuneMerchantEditor' => 'applications/phortune/editor/PhortuneMerchantEditor.php', 'PhortuneMerchantEditor' => 'applications/phortune/editor/PhortuneMerchantEditor.php',
'PhortuneMerchantHasMemberEdgeType' => 'applications/phortune/edge/PhortuneMerchantHasMemberEdgeType.php',
'PhortuneMerchantListController' => 'applications/phortune/controller/PhortuneMerchantListController.php', 'PhortuneMerchantListController' => 'applications/phortune/controller/PhortuneMerchantListController.php',
'PhortuneMerchantPHIDType' => 'applications/phortune/phid/PhortuneMerchantPHIDType.php', 'PhortuneMerchantPHIDType' => 'applications/phortune/phid/PhortuneMerchantPHIDType.php',
'PhortuneMerchantQuery' => 'applications/phortune/query/PhortuneMerchantQuery.php', 'PhortuneMerchantQuery' => 'applications/phortune/query/PhortuneMerchantQuery.php',
@@ -5644,6 +5646,7 @@ phutil_register_library_map(array(
'PhortuneErrCode' => 'PhortuneConstants', 'PhortuneErrCode' => 'PhortuneConstants',
'PhortuneLandingController' => 'PhortuneController', 'PhortuneLandingController' => 'PhortuneController',
'PhortuneMemberHasAccountEdgeType' => 'PhabricatorEdgeType', 'PhortuneMemberHasAccountEdgeType' => 'PhabricatorEdgeType',
'PhortuneMemberHasMerchantEdgeType' => 'PhabricatorEdgeType',
'PhortuneMerchant' => array( 'PhortuneMerchant' => array(
'PhortuneDAO', 'PhortuneDAO',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
@@ -5652,6 +5655,7 @@ phutil_register_library_map(array(
'PhortuneMerchantController' => 'PhortuneController', 'PhortuneMerchantController' => 'PhortuneController',
'PhortuneMerchantEditController' => 'PhortuneMerchantController', 'PhortuneMerchantEditController' => 'PhortuneMerchantController',
'PhortuneMerchantEditor' => 'PhabricatorApplicationTransactionEditor', 'PhortuneMerchantEditor' => 'PhabricatorApplicationTransactionEditor',
'PhortuneMerchantHasMemberEdgeType' => 'PhabricatorEdgeType',
'PhortuneMerchantListController' => 'PhortuneMerchantController', 'PhortuneMerchantListController' => 'PhortuneMerchantController',
'PhortuneMerchantPHIDType' => 'PhabricatorPHIDType', 'PhortuneMerchantPHIDType' => 'PhabricatorPHIDType',
'PhortuneMerchantQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhortuneMerchantQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',

View File

@@ -18,11 +18,7 @@ final class PhortuneAccountListController extends PhortuneController {
$merchants = id(new PhortuneMerchantQuery()) $merchants = id(new PhortuneMerchantQuery())
->setViewer($viewer) ->setViewer($viewer)
->requireCapabilities( ->withMemberPHIDs(array($viewer->getPHID()))
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute(); ->execute();
$title = pht('Accounts'); $title = pht('Accounts');

View File

@@ -32,6 +32,7 @@ final class PhortuneMerchantEditController
PhortuneMerchantCapability::CAPABILITY); PhortuneMerchantCapability::CAPABILITY);
$merchant = PhortuneMerchant::initializeNewMerchant($viewer); $merchant = PhortuneMerchant::initializeNewMerchant($viewer);
$merchant->attachMemberPHIDs(array($viewer->getPHID()));
$is_new = true; $is_new = true;
} }
@@ -52,6 +53,8 @@ final class PhortuneMerchantEditController
$e_name = true; $e_name = true;
$v_name = $merchant->getName(); $v_name = $merchant->getName();
$v_desc = $merchant->getDescription(); $v_desc = $merchant->getDescription();
$v_members = $merchant->getMemberPHIDs();
$e_members = null;
$validation_exception = null; $validation_exception = null;
if ($request->isFormPost()) { if ($request->isFormPost()) {
@@ -59,11 +62,14 @@ final class PhortuneMerchantEditController
$v_desc = $request->getStr('desc'); $v_desc = $request->getStr('desc');
$v_view = $request->getStr('viewPolicy'); $v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy'); $v_edit = $request->getStr('editPolicy');
$v_members = $request->getArr('memberPHIDs');
$type_name = PhortuneMerchantTransaction::TYPE_NAME; $type_name = PhortuneMerchantTransaction::TYPE_NAME;
$type_desc = PhortuneMerchantTransaction::TYPE_DESCRIPTION; $type_desc = PhortuneMerchantTransaction::TYPE_DESCRIPTION;
$type_edge = PhabricatorTransactions::TYPE_EDGE;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY; $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
$edge_members = PhortuneMerchantHasMemberEdgeType::EDGECONST;
$xactions = array(); $xactions = array();
@@ -80,8 +86,12 @@ final class PhortuneMerchantEditController
->setNewValue($v_view); ->setNewValue($v_view);
$xactions[] = id(new PhortuneMerchantTransaction()) $xactions[] = id(new PhortuneMerchantTransaction())
->setTransactionType($type_edit) ->setTransactionType($type_edge)
->setNewValue($v_edit); ->setMetadataValue('edge:type', $edge_members)
->setNewValue(
array(
'=' => array_fuse($v_members),
));
$editor = id(new PhortuneMerchantEditor()) $editor = id(new PhortuneMerchantEditor())
->setActor($viewer) ->setActor($viewer)
@@ -98,9 +108,9 @@ final class PhortuneMerchantEditController
$validation_exception = $ex; $validation_exception = $ex;
$e_name = $ex->getShortMessage($type_name); $e_name = $ex->getShortMessage($type_name);
$e_mbmers = $ex->getShortMessage($type_edge);
$merchant->setViewPolicy($v_view); $merchant->setViewPolicy($v_view);
$merchant->setEditPolicy($v_edit);
} }
} }
@@ -109,6 +119,8 @@ final class PhortuneMerchantEditController
->setObject($merchant) ->setObject($merchant)
->execute(); ->execute();
$member_handles = $this->loadViewerHandles($v_members);
$form = id(new AphrontFormView()) $form = id(new AphrontFormView())
->setUser($viewer) ->setUser($viewer)
->appendChild( ->appendChild(
@@ -122,18 +134,19 @@ final class PhortuneMerchantEditController
->setName('desc') ->setName('desc')
->setLabel(pht('Description')) ->setLabel(pht('Description'))
->setValue($v_desc)) ->setValue($v_desc))
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setLabel(pht('Members'))
->setName('memberPHIDs')
->setValue($member_handles)
->setError($e_members))
->appendChild( ->appendChild(
id(new AphrontFormPolicyControl()) id(new AphrontFormPolicyControl())
->setName('viewPolicy') ->setName('viewPolicy')
->setPolicyObject($merchant) ->setPolicyObject($merchant)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW) ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicies($policies)) ->setPolicies($policies))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($merchant)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicies($policies))
->appendChild( ->appendChild(
id(new AphrontFormSubmitControl()) id(new AphrontFormSubmitControl())
->setValue($button_text) ->setValue($button_text)

View File

@@ -140,6 +140,12 @@ final class PhortuneMerchantViewController
$view->addProperty(pht('Status'), $status_view); $view->addProperty(pht('Status'), $status_view);
$this->loadHandles($merchant->getMemberPHIDs());
$view->addProperty(
pht('Members'),
$this->renderHandlesForPHIDs($merchant->getMemberPHIDs()));
$view->invokeWillRenderEvent(); $view->invokeWillRenderEvent();
$description = $merchant->getDescription(); $description = $merchant->getDescription();

View File

@@ -0,0 +1,12 @@
<?php
final class PhortuneMemberHasMerchantEdgeType
extends PhabricatorEdgeType {
const EDGECONST = 54;
public function getInverseEdgeConstant() {
return PhortuneMerchantHasMemberEdgeType::EDGECONST;
}
}

View File

@@ -0,0 +1,101 @@
<?php
final class PhortuneMerchantHasMemberEdgeType extends PhabricatorEdgeType {
const EDGECONST = 53;
public function getInverseEdgeConstant() {
return PhortuneMemberHasMerchantEdgeType::EDGECONST;
}
public function getTransactionAddString(
$actor,
$add_count,
$add_edges) {
return pht(
'%s added %s merchant member(s): %s.',
$actor,
$add_count,
$add_edges);
}
public function getTransactionRemoveString(
$actor,
$rem_count,
$rem_edges) {
return pht(
'%s removed %s merchant member(s): %s.',
$actor,
$rem_count,
$rem_edges);
}
public function getTransactionEditString(
$actor,
$total_count,
$add_count,
$add_edges,
$rem_count,
$rem_edges) {
return pht(
'%s edited %s merchant member(s), added %s: %s; removed %s: %s.',
$actor,
$total_count,
$add_count,
$add_edges,
$rem_count,
$rem_edges);
}
public function getFeedAddString(
$actor,
$object,
$add_count,
$add_edges) {
return pht(
'%s added %s merchant member(s) to %s: %s.',
$actor,
$add_count,
$object,
$add_edges);
}
public function getFeedRemoveString(
$actor,
$object,
$rem_count,
$rem_edges) {
return pht(
'%s removed %s merchant member(s) from %s: %s.',
$actor,
$rem_count,
$object,
$rem_edges);
}
public function getFeedEditString(
$actor,
$object,
$total_count,
$add_count,
$add_edges,
$rem_count,
$rem_edges) {
return pht(
'%s edited %s merchant member(s) for %s, added %s: %s; removed %s: %s.',
$actor,
$total_count,
$object,
$add_count,
$add_edges,
$rem_count,
$rem_edges);
}
}

View File

@@ -17,7 +17,7 @@ final class PhortuneMerchantEditor
$types[] = PhortuneMerchantTransaction::TYPE_NAME; $types[] = PhortuneMerchantTransaction::TYPE_NAME;
$types[] = PhortuneMerchantTransaction::TYPE_DESCRIPTION; $types[] = PhortuneMerchantTransaction::TYPE_DESCRIPTION;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_EDGE;
return $types; return $types;
} }
@@ -59,6 +59,8 @@ final class PhortuneMerchantEditor
case PhortuneMerchantTransaction::TYPE_DESCRIPTION: case PhortuneMerchantTransaction::TYPE_DESCRIPTION:
$object->setDescription($xaction->getNewValue()); $object->setDescription($xaction->getNewValue());
return; return;
case PhabricatorTransactions::TYPE_EDGE:
return;
} }
return parent::applyCustomInternalTransaction($object, $xaction); return parent::applyCustomInternalTransaction($object, $xaction);
@@ -71,6 +73,7 @@ final class PhortuneMerchantEditor
switch ($xaction->getTransactionType()) { switch ($xaction->getTransactionType()) {
case PhortuneMerchantTransaction::TYPE_NAME: case PhortuneMerchantTransaction::TYPE_NAME:
case PhortuneMerchantTransaction::TYPE_DESCRIPTION: case PhortuneMerchantTransaction::TYPE_DESCRIPTION:
case PhabricatorTransactions::TYPE_EDGE:
return; return;
} }

View File

@@ -5,6 +5,7 @@ final class PhortuneMerchantQuery
private $ids; private $ids;
private $phids; private $phids;
private $memberPHIDs;
public function withIDs(array $ids) { public function withIDs(array $ids) {
$this->ids = $ids; $this->ids = $ids;
@@ -16,14 +17,20 @@ final class PhortuneMerchantQuery
return $this; return $this;
} }
public function withMemberPHIDs(array $member_phids) {
$this->memberPHIDs = $member_phids;
return $this;
}
protected function loadPage() { protected function loadPage() {
$table = new PhortuneMerchant(); $table = new PhortuneMerchant();
$conn = $table->establishConnection('r'); $conn = $table->establishConnection('r');
$rows = queryfx_all( $rows = queryfx_all(
$conn, $conn,
'SELECT * FROM %T %Q %Q %Q', 'SELECT m.* FROM %T m %Q %Q %Q %Q',
$table->getTableName(), $table->getTableName(),
$this->buildJoinClause($conn),
$this->buildWhereClause($conn), $this->buildWhereClause($conn),
$this->buildOrderClause($conn), $this->buildOrderClause($conn),
$this->buildLimitClause($conn)); $this->buildLimitClause($conn));
@@ -31,6 +38,21 @@ final class PhortuneMerchantQuery
return $table->loadAllFromArray($rows); return $table->loadAllFromArray($rows);
} }
protected function willFilterPage(array $merchants) {
$query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(mpull($merchants, 'getPHID'))
->withEdgeTypes(array(PhortuneMerchantHasMemberEdgeType::EDGECONST));
$query->execute();
foreach ($merchants as $merchant) {
$member_phids = $query->getDestinationPHIDs(array($merchant->getPHID()));
$member_phids = array_reverse($member_phids);
$merchant->attachMemberPHIDs($member_phids);
}
return $merchants;
}
private function buildWhereClause(AphrontDatabaseConnection $conn) { private function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array(); $where = array();
@@ -48,11 +70,32 @@ final class PhortuneMerchantQuery
$this->phids); $this->phids);
} }
if ($this->memberPHIDs !== null) {
$where[] = qsprintf(
$conn,
'e.dst IN (%Ls)',
$this->memberPHIDs);
}
$where[] = $this->buildPagingClause($conn); $where[] = $this->buildPagingClause($conn);
return $this->formatWhereClause($where); return $this->formatWhereClause($where);
} }
private function buildJoinClause(AphrontDatabaseConnection $conn) {
$joins = array();
if ($this->memberPHIDs !== null) {
$joins[] = qsprintf(
$conn,
'LEFT JOIN %T e ON m.phid = e.src AND e.type = %d',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhortuneMerchantHasMemberEdgeType::EDGECONST);
}
return implode(' ', $joins);
}
public function getQueryApplicationClass() { public function getQueryApplicationClass() {
return 'PhabricatorPhortuneApplication'; return 'PhabricatorPhortuneApplication';
} }

View File

@@ -5,13 +5,14 @@ final class PhortuneMerchant extends PhortuneDAO
protected $name; protected $name;
protected $viewPolicy; protected $viewPolicy;
protected $editPolicy;
protected $description; protected $description;
private $memberPHIDs = self::ATTACHABLE;
public static function initializeNewMerchant(PhabricatorUser $actor) { public static function initializeNewMerchant(PhabricatorUser $actor) {
return id(new PhortuneMerchant()) return id(new PhortuneMerchant())
->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
->setEditPolicy($actor->getPHID()); ->attachMemberPHIDs(array());
} }
public function getConfiguration() { public function getConfiguration() {
@@ -29,6 +30,15 @@ final class PhortuneMerchant extends PhortuneDAO
PhortuneMerchantPHIDType::TYPECONST); PhortuneMerchantPHIDType::TYPECONST);
} }
public function getMemberPHIDs() {
return $this->assertAttached($this->memberPHIDs);
}
public function attachMemberPHIDs(array $member_phids) {
$this->memberPHIDs = $member_phids;
return $this;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */
@@ -45,16 +55,21 @@ final class PhortuneMerchant extends PhortuneDAO
case PhabricatorPolicyCapability::CAN_VIEW: case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy(); return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT: case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy(); return PhabricatorPolicies::POLICY_NOONE;
} }
} }
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
$members = array_fuse($this->getMemberPHIDs());
if (isset($members[$viewer->getPHID()])) {
return true;
}
return false; return false;
} }
public function describeAutomaticCapability($capability) { public function describeAutomaticCapability($capability) {
return null; return pht("A merchant's members an always view and edit it.");
} }
} }