Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/phortune/controller/merchant/PhortuneMerchantInvoiceCreateController.php b/src/applications/phortune/controller/merchant/PhortuneMerchantInvoiceCreateController.php
index 300525cdd2..8bd070853c 100644
--- a/src/applications/phortune/controller/merchant/PhortuneMerchantInvoiceCreateController.php
+++ b/src/applications/phortune/controller/merchant/PhortuneMerchantInvoiceCreateController.php
@@ -1,257 +1,258 @@
<?php
final class PhortuneMerchantInvoiceCreateController
extends PhortuneMerchantProfileController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$merchant = $this->loadMerchantAuthority();
if (!$merchant) {
return new Aphront404Response();
}
$this->setMerchant($merchant);
$merchant_id = $merchant->getID();
$cancel_uri = $this->getApplicationURI("/merchant/{$merchant_id}/");
// Load the user to invoice, or prompt the viewer to select one.
$target_user = null;
$user_phid = head($request->getArr('userPHID'));
if (!$user_phid) {
$user_phid = $request->getStr('userPHID');
}
if ($user_phid) {
$target_user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array($user_phid))
->executeOne();
}
if (!$target_user) {
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions(pht('Choose a user to invoice.'))
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('User'))
->setDatasource(new PhabricatorPeopleDatasource())
->setName('userPHID')
->setLimit(1));
return $this->newDialog()
->setTitle(pht('Choose User'))
->appendForm($form)
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Continue'));
}
// Load the account to invoice, or prompt the viewer to select one.
$target_account = null;
$account_phid = $request->getStr('accountPHID');
if ($account_phid) {
$target_account = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withPHIDs(array($account_phid))
->withMemberPHIDs(array($target_user->getPHID()))
->executeOne();
}
if (!$target_account) {
- $accounts = PhortuneAccountQuery::loadAccountsForUser(
- $target_user,
- PhabricatorContentSource::newFromRequest($request));
+ $accounts = id(new PhortuneAccountQuery())
+ ->setViewer($viewer)
+ ->withMemberPHIDs(array($target_user->getPHID()))
+ ->execute();
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('userPHID', $target_user->getPHID())
->appendRemarkupInstructions(pht('Choose which account to invoice.'))
->appendControl(
id(new AphrontFormMarkupControl())
->setLabel(pht('User'))
->setValue($viewer->renderHandle($target_user->getPHID())))
->appendControl(
id(new AphrontFormSelectControl())
->setLabel(pht('Account'))
->setName('accountPHID')
->setValue($account_phid)
->setOptions(mpull($accounts, 'getName', 'getPHID')));
return $this->newDialog()
->setTitle(pht('Choose Account'))
->appendForm($form)
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Continue'));
}
// Now we build the actual invoice.
$title = pht('New Invoice');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title);
$v_title = $request->getStr('title');
$e_title = true;
$v_name = $request->getStr('name');
$e_name = true;
$v_cost = $request->getStr('cost');
$e_cost = true;
$v_desc = $request->getStr('description');
$v_quantity = 1;
$e_quantity = null;
$errors = array();
if ($request->isFormPost() && $request->getStr('invoice')) {
$v_quantity = $request->getStr('quantity');
$e_title = null;
$e_name = null;
$e_cost = null;
$e_quantity = null;
if (!strlen($v_title)) {
$e_title = pht('Required');
$errors[] = pht('You must title this invoice.');
}
if (!strlen($v_name)) {
$e_name = pht('Required');
$errors[] = pht('You must provide a name for this purchase.');
}
if (!strlen($v_cost)) {
$e_cost = pht('Required');
$errors[] = pht('You must provide a cost for this purchase.');
} else {
try {
$v_currency = PhortuneCurrency::newFromUserInput(
$viewer,
$v_cost);
} catch (Exception $ex) {
$errors[] = $ex->getMessage();
$e_cost = pht('Invalid');
}
}
if ((int)$v_quantity <= 0) {
$e_quantity = pht('Invalid');
$errors[] = pht('Quantity must be a positive integer.');
}
if (!$errors) {
$unique = Filesystem::readRandomCharacters(16);
$product = id(new PhortuneProductQuery())
->setViewer($target_user)
->withClassAndRef('PhortuneAdHocProduct', $unique)
->executeOne();
$cart_implementation = new PhortuneAdHocCart();
$cart = $target_account->newCart(
$target_user,
$cart_implementation,
$merchant);
$cart
->setMetadataValue('adhoc.title', $v_title)
->setMetadataValue('adhoc.description', $v_desc);
$purchase = $cart->newPurchase($target_user, $product)
->setBasePriceAsCurrency($v_currency)
->setQuantity((int)$v_quantity)
->setMetadataValue('adhoc.name', $v_name)
->save();
$cart
->setIsInvoice(1)
->save();
$cart->activateCart();
$cart_id = $cart->getID();
$uri = "/merchant/{$merchant_id}/cart/{$cart_id}/";
$uri = $this->getApplicationURI($uri);
return id(new AphrontRedirectResponse())->setURI($uri);
}
}
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('userPHID', $target_user->getPHID())
->addHiddenInput('accountPHID', $target_account->getPHID())
->addHiddenInput('invoice', true)
->appendControl(
id(new AphrontFormMarkupControl())
->setLabel(pht('User'))
->setValue($viewer->renderHandle($target_user->getPHID())))
->appendControl(
id(new AphrontFormMarkupControl())
->setLabel(pht('Account'))
->setValue($viewer->renderHandle($target_account->getPHID())))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Invoice Title'))
->setName('title')
->setValue($v_title)
->setError($e_title))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Purchase Name'))
->setName('name')
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Purchase Cost'))
->setName('cost')
->setValue($v_cost)
->setError($e_cost))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Quantity'))
->setName('quantity')
->setValue($v_quantity)
->setError($e_quantity))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Invoice Description'))
->setName('description')
->setValue($v_desc))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue(pht('Send Invoice')));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setFormErrors($errors)
->setForm($form);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-plus-square');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$box,
));
$navigation = $this->buildSideNavView('orders');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
}
diff --git a/src/applications/phortune/query/PhortuneMerchantQuery.php b/src/applications/phortune/query/PhortuneMerchantQuery.php
index b6cab7dbc2..aef7d8aaf1 100644
--- a/src/applications/phortune/query/PhortuneMerchantQuery.php
+++ b/src/applications/phortune/query/PhortuneMerchantQuery.php
@@ -1,132 +1,193 @@
<?php
final class PhortuneMerchantQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $memberPHIDs;
private $needProfileImage;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withMemberPHIDs(array $member_phids) {
$this->memberPHIDs = $member_phids;
return $this;
}
public function needProfileImage($need) {
$this->needProfileImage = $need;
return $this;
}
public function newResultObject() {
return new PhortuneMerchant();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
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);
}
if ($this->needProfileImage) {
$default = null;
$file_phids = mpull($merchants, 'getProfileImagePHID');
$file_phids = array_filter($file_phids);
if ($file_phids) {
$files = id(new PhabricatorFileQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($file_phids)
->execute();
$files = mpull($files, null, 'getPHID');
} else {
$files = array();
}
foreach ($merchants as $merchant) {
$file = idx($files, $merchant->getProfileImagePHID());
if (!$file) {
if (!$default) {
$default = PhabricatorFile::loadBuiltin(
$this->getViewer(),
'merchant.png');
}
$file = $default;
}
$merchant->attachProfileImageFile($file);
}
}
return $merchants;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
- 'id IN (%Ld)',
+ 'merchant.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
- 'phid IN (%Ls)',
+ 'merchant.phid IN (%Ls)',
$this->phids);
}
if ($this->memberPHIDs !== null) {
$where[] = qsprintf(
$conn,
'e.dst IN (%Ls)',
$this->memberPHIDs);
}
return $where;
}
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->memberPHIDs !== null) {
$joins[] = qsprintf(
$conn,
- 'LEFT JOIN %T e ON m.phid = e.src AND e.type = %d',
+ 'LEFT JOIN %T e ON merchant.phid = e.src AND e.type = %d',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhortuneMerchantHasMemberEdgeType::EDGECONST);
}
return $joins;
}
public function getQueryApplicationClass() {
return 'PhabricatorPhortuneApplication';
}
protected function getPrimaryTableAlias() {
- return 'm';
+ return 'merchant';
+ }
+
+ public static function canViewersEditMerchants(
+ array $viewer_phids,
+ array $merchant_phids) {
+
+ // See T13366 for some discussion. This is an unusual caching construct to
+ // make policy filtering of Accounts easier.
+
+ foreach ($viewer_phids as $key => $viewer_phid) {
+ if (!$viewer_phid) {
+ unset($viewer_phids[$key]);
+ }
+ }
+
+ if (!$viewer_phids) {
+ return array();
+ }
+
+ $cache_key = 'phortune.merchant.can-edit';
+ $cache = PhabricatorCaches::getRequestCache();
+
+ $cache_data = $cache->getKey($cache_key);
+ if (!$cache_data) {
+ $cache_data = array();
+ }
+
+ $load_phids = array();
+ foreach ($viewer_phids as $viewer_phid) {
+ if (!isset($cache_data[$viewer_phid])) {
+ $load_phids[] = $viewer_phid;
+ }
+ }
+
+ $did_write = false;
+ foreach ($load_phids as $load_phid) {
+ $merchants = id(new self())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withMemberPHIDs(array($load_phid))
+ ->execute();
+ foreach ($merchants as $merchant) {
+ $cache_data[$load_phid][$merchant->getPHID()] = true;
+ $did_write = true;
+ }
+ }
+
+ if ($did_write) {
+ $cache->setKey($cache_key, $cache_data);
+ }
+
+ $results = array();
+ foreach ($viewer_phids as $viewer_phid) {
+ foreach ($merchant_phids as $merchant_phid) {
+ if (!isset($cache_data[$viewer_phid][$merchant_phid])) {
+ continue;
+ }
+ $results[$viewer_phid][$merchant_phid] = true;
+ }
+ }
+
+ return $results;
}
}
diff --git a/src/applications/phortune/storage/PhortuneAccount.php b/src/applications/phortune/storage/PhortuneAccount.php
index 6b31805671..182c80f40f 100644
--- a/src/applications/phortune/storage/PhortuneAccount.php
+++ b/src/applications/phortune/storage/PhortuneAccount.php
@@ -1,200 +1,208 @@
<?php
/**
* An account represents a purchasing entity. An account may have multiple users
* on it (e.g., several employees of a company have access to the company
* account), and a user may have several accounts (e.g., a company account and
* a personal account).
*/
final class PhortuneAccount extends PhortuneDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface {
protected $name;
protected $billingName;
protected $billingAddress;
private $memberPHIDs = self::ATTACHABLE;
private $merchantPHIDs = self::ATTACHABLE;
public static function initializeNewAccount(PhabricatorUser $actor) {
return id(new self())
->setBillingName('')
->setBillingAddress('')
->attachMerchantPHIDs(array())
->attachMemberPHIDs(array());
}
public static function createNewAccount(
PhabricatorUser $actor,
PhabricatorContentSource $content_source) {
$account = self::initializeNewAccount($actor);
$xactions = array();
$xactions[] = id(new PhortuneAccountTransaction())
->setTransactionType(PhortuneAccountNameTransaction::TRANSACTIONTYPE)
->setNewValue(pht('Default Account'));
$xactions[] = id(new PhortuneAccountTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue(
'edge:type',
PhortuneAccountHasMemberEdgeType::EDGECONST)
->setNewValue(
array(
'=' => array($actor->getPHID() => $actor->getPHID()),
));
$editor = id(new PhortuneAccountEditor())
->setActor($actor)
->setContentSource($content_source);
// We create an account for you the first time you visit Phortune.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$editor->applyTransactions($account, $xactions);
unset($unguarded);
return $account;
}
public function newCart(
PhabricatorUser $actor,
PhortuneCartImplementation $implementation,
PhortuneMerchant $merchant) {
$cart = PhortuneCart::initializeNewCart($actor, $this, $merchant);
$cart->setCartClass(get_class($implementation));
$cart->attachImplementation($implementation);
$implementation->willCreateCart($actor, $cart);
return $cart->save();
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text255',
'billingName' => 'text255',
'billingAddress' => 'text',
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhortuneAccountPHIDType::TYPECONST);
}
public function getMemberPHIDs() {
return $this->assertAttached($this->memberPHIDs);
}
public function attachMemberPHIDs(array $phids) {
$this->memberPHIDs = $phids;
return $this;
}
public function getURI() {
return '/phortune/'.$this->getID().'/';
}
public function getDetailsURI() {
return urisprintf(
'/phortune/account/%d/details/',
$this->getID());
}
public function getEmailAddressesURI() {
return urisprintf(
'/phortune/account/%d/addresses/',
$this->getID());
}
public function attachMerchantPHIDs(array $merchant_phids) {
$this->merchantPHIDs = $merchant_phids;
return $this;
}
public function getMerchantPHIDs() {
return $this->assertAttached($this->merchantPHIDs);
}
public function writeMerchantEdge(PhortuneMerchant $merchant) {
$edge_src = $this->getPHID();
$edge_type = PhortuneAccountHasMerchantEdgeType::EDGECONST;
$edge_dst = $merchant->getPHID();
id(new PhabricatorEdgeEditor())
->addEdge($edge_src, $edge_type, $edge_dst)
->save();
return $this;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhortuneAccountEditor();
}
public function getApplicationTransactionTemplate() {
return new PhortuneAccountTransaction();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->getPHID() === null) {
// Allow a user to create an account for themselves.
return PhabricatorPolicies::POLICY_USER;
} else {
return PhabricatorPolicies::POLICY_NOONE;
}
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
$members = array_fuse($this->getMemberPHIDs());
if (isset($members[$viewer->getPHID()])) {
return true;
}
- // If the viewer is acting on behalf of a merchant, they can see
- // payment accounts.
+ // See T13366. If the viewer can edit any merchant that this payment
+ // account has a relationship with, they can see the payment account.
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
- foreach ($viewer->getAuthorities() as $authority) {
- if ($authority instanceof PhortuneMerchant) {
- return true;
- }
+ $viewer_phids = array($viewer->getPHID());
+ $merchant_phids = $this->getMerchantPHIDs();
+
+ $any_edit = PhortuneMerchantQuery::canViewersEditMerchants(
+ $viewer_phids,
+ $merchant_phids);
+
+ if ($any_edit) {
+ return true;
}
}
return false;
}
public function describeAutomaticCapability($capability) {
- return pht('Members of an account can always view and edit it.');
+ return array(
+ pht('Members of an account can always view and edit it.'),
+ pht('Merchants an account has established a relationship can view it.'),
+ );
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jul 2, 12:37 PM (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
164404
Default Alt Text
(20 KB)

Event Timeline