1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 14:00:56 +01:00

Modernize PhortuneAccount with EditEngine/Modular Transactions

Summary: This updates the backend of PhortuneAccount to use EditEngine and Modular Transactions and updates language to "account manager" for clarity of role.

Test Plan:
- Wiped `phortune_account` table
- Visit Phortune, see new account automatically created.
- Edit name and managers
- Try to set no name or remove myself as a manager, get error messages
- Visit `/phortune/` and create another new account

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Differential Revision: https://secure.phabricator.com/D17585
This commit is contained in:
Chad Little 2017-04-11 11:53:42 -07:00
parent 21709a2bbc
commit 5dd18a7ec1
15 changed files with 255 additions and 247 deletions

View file

@ -4322,13 +4322,16 @@ phutil_register_library_map(array(
'PhortuneAccount' => 'applications/phortune/storage/PhortuneAccount.php',
'PhortuneAccountChargeListController' => 'applications/phortune/controller/account/PhortuneAccountChargeListController.php',
'PhortuneAccountEditController' => 'applications/phortune/controller/account/PhortuneAccountEditController.php',
'PhortuneAccountEditEngine' => 'applications/phortune/editor/PhortuneAccountEditEngine.php',
'PhortuneAccountEditor' => 'applications/phortune/editor/PhortuneAccountEditor.php',
'PhortuneAccountHasMemberEdgeType' => 'applications/phortune/edge/PhortuneAccountHasMemberEdgeType.php',
'PhortuneAccountListController' => 'applications/phortune/controller/account/PhortuneAccountListController.php',
'PhortuneAccountNameTransaction' => 'applications/phortune/xaction/PhortuneAccountNameTransaction.php',
'PhortuneAccountPHIDType' => 'applications/phortune/phid/PhortuneAccountPHIDType.php',
'PhortuneAccountQuery' => 'applications/phortune/query/PhortuneAccountQuery.php',
'PhortuneAccountTransaction' => 'applications/phortune/storage/PhortuneAccountTransaction.php',
'PhortuneAccountTransactionQuery' => 'applications/phortune/query/PhortuneAccountTransactionQuery.php',
'PhortuneAccountTransactionType' => 'applications/phortune/xaction/PhortuneAccountTransactionType.php',
'PhortuneAccountViewController' => 'applications/phortune/controller/account/PhortuneAccountViewController.php',
'PhortuneAdHocCart' => 'applications/phortune/cart/PhortuneAdHocCart.php',
'PhortuneAdHocProduct' => 'applications/phortune/product/PhortuneAdHocProduct.php',
@ -9771,13 +9774,16 @@ phutil_register_library_map(array(
),
'PhortuneAccountChargeListController' => 'PhortuneController',
'PhortuneAccountEditController' => 'PhortuneController',
'PhortuneAccountEditEngine' => 'PhabricatorEditEngine',
'PhortuneAccountEditor' => 'PhabricatorApplicationTransactionEditor',
'PhortuneAccountHasMemberEdgeType' => 'PhabricatorEdgeType',
'PhortuneAccountListController' => 'PhortuneController',
'PhortuneAccountNameTransaction' => 'PhortuneAccountTransactionType',
'PhortuneAccountPHIDType' => 'PhabricatorPHIDType',
'PhortuneAccountQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneAccountTransaction' => 'PhabricatorApplicationTransaction',
'PhortuneAccountTransaction' => 'PhabricatorModularTransaction',
'PhortuneAccountTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhortuneAccountTransactionType' => 'PhabricatorModularTransactionType',
'PhortuneAccountViewController' => 'PhortuneController',
'PhortuneAdHocCart' => 'PhortuneCartImplementation',
'PhortuneAdHocProduct' => 'PhortuneProductImplementation',

View file

@ -67,7 +67,8 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
),
'account/' => array(
'' => 'PhortuneAccountListController',
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneAccountEditController',
$this->getEditRoutePattern('edit/')
=> 'PhortuneAccountEditController',
),
'product/' => array(
'' => 'PhortuneProductListController',

View file

@ -1,137 +1,11 @@
<?php
final class PhortuneAccountEditController extends PhortuneController {
final class PhortuneAccountEditController extends
PhortuneController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
if ($id) {
$account = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$account) {
return new Aphront404Response();
}
$is_new = false;
} else {
$account = PhortuneAccount::initializeNewAccount($viewer);
$account->attachMemberPHIDs(array($viewer->getPHID()));
$is_new = true;
}
$v_name = $account->getName();
$e_name = true;
$v_members = $account->getMemberPHIDs();
$e_members = null;
$validation_exception = null;
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$v_members = $request->getArr('memberPHIDs');
$type_name = PhortuneAccountTransaction::TYPE_NAME;
$type_edge = PhabricatorTransactions::TYPE_EDGE;
$xactions = array();
$xactions[] = id(new PhortuneAccountTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(new PhortuneAccountTransaction())
->setTransactionType($type_edge)
->setMetadataValue(
'edge:type',
PhortuneAccountHasMemberEdgeType::EDGECONST)
->setNewValue(
array(
'=' => array_fuse($v_members),
));
$editor = id(new PhortuneAccountEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
try {
$editor->applyTransactions($account, $xactions);
$account_uri = $this->getApplicationURI($account->getID().'/');
return id(new AphrontRedirectResponse())->setURI($account_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_name = $ex->getShortMessage($type_name);
$e_members = $ex->getShortMessage($type_edge);
}
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->setBorder(true);
if ($is_new) {
$cancel_uri = $this->getApplicationURI('account/');
$crumbs->addTextCrumb(pht('Accounts'), $cancel_uri);
$crumbs->addTextCrumb(pht('Create Account'));
$title = pht('Create Payment Account');
$submit_button = pht('Create Account');
} else {
$cancel_uri = $this->getApplicationURI($account->getID().'/');
$crumbs->addTextCrumb($account->getName(), $cancel_uri);
$crumbs->addTextCrumb(pht('Edit'));
$title = pht('Edit %s', $account->getName());
$submit_button = pht('Save Changes');
}
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($v_name)
->setError($e_name))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setLabel(pht('Members'))
->setName('memberPHIDs')
->setValue($v_members)
->setError($e_members))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($submit_button)
->addCancelButton($cancel_uri));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Account'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setValidationException($validation_exception)
->setForm($form);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
return id(new PhortuneAccountEditEngine())
->setController($this)
->buildResponse();
}
}

View file

@ -129,7 +129,7 @@ final class PhortuneAccountViewController extends PhortuneController {
->appendChild($status_view);
$curtain->newPanel()
->setHeaderText(pht('Members'))
->setHeaderText(pht('Managers'))
->appendChild($member_list);
return $curtain;

View file

@ -21,7 +21,7 @@ final class PhortuneMerchantPictureController
return new Aphront404Response();
}
$uri = $merchant->getViewURI();
$uri = $merchant->getURI();
$supported_formats = PhabricatorFile::getTransformableImageFormats();
$e_file = true;

View file

@ -14,7 +14,7 @@ final class PhortuneAccountHasMemberEdgeType extends PhabricatorEdgeType {
$add_edges) {
return pht(
'%s added %s account member(s): %s.',
'%s added %s account manager(s): %s.',
$actor,
$add_count,
$add_edges);
@ -26,7 +26,7 @@ final class PhortuneAccountHasMemberEdgeType extends PhabricatorEdgeType {
$rem_edges) {
return pht(
'%s removed %s account member(s): %s.',
'%s removed %s account manager(s): %s.',
$actor,
$rem_count,
$rem_edges);
@ -41,7 +41,7 @@ final class PhortuneAccountHasMemberEdgeType extends PhabricatorEdgeType {
$rem_edges) {
return pht(
'%s edited %s account member(s), added %s: %s; removed %s: %s.',
'%s edited %s account manager(s), added %s: %s; removed %s: %s.',
$actor,
$total_count,
$add_count,
@ -57,7 +57,7 @@ final class PhortuneAccountHasMemberEdgeType extends PhabricatorEdgeType {
$add_edges) {
return pht(
'%s added %s account member(s) to %s: %s.',
'%s added %s account manager(s) to %s: %s.',
$actor,
$add_count,
$object,
@ -71,7 +71,7 @@ final class PhortuneAccountHasMemberEdgeType extends PhabricatorEdgeType {
$rem_edges) {
return pht(
'%s removed %s account member(s) from %s: %s.',
'%s removed %s account manager(s) from %s: %s.',
$actor,
$rem_count,
$object,
@ -88,7 +88,7 @@ final class PhortuneAccountHasMemberEdgeType extends PhabricatorEdgeType {
$rem_edges) {
return pht(
'%s edited %s account member(s) for %s, added %s: %s; removed %s: %s.',
'%s edited %s account manager(s) for %s, added %s: %s; removed %s: %s.',
$actor,
$total_count,
$object,

View file

@ -0,0 +1,108 @@
<?php
final class PhortuneAccountEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'phortune.account';
public function getEngineName() {
return pht('Phortune Accounts');
}
public function getEngineApplicationClass() {
return 'PhabricatorPhortuneApplication';
}
public function getSummaryHeader() {
return pht('Configure Phortune Account Forms');
}
public function getSummaryText() {
return pht('Configure creation and editing forms in Phortune Accounts.');
}
public function isEngineConfigurable() {
return false;
}
protected function newEditableObject() {
return PhortuneAccount::initializeNewAccount($this->getViewer());
}
protected function newObjectQuery() {
return new PhortuneAccountQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Payment Account');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Account: %s', $object->getName());
}
protected function getObjectEditShortText($object) {
return $object->getName();
}
protected function getObjectCreateShortText() {
return pht('Create Account');
}
protected function getObjectName() {
return pht('Account');
}
protected function getObjectCreateCancelURI($object) {
return $this->getApplication()->getApplicationURI('/');
}
protected function getEditorURI() {
return $this->getApplication()->getApplicationURI('edit/');
}
protected function getObjectViewURI($object) {
return $object->getURI();
}
protected function buildCustomEditFields($object) {
$viewer = $this->getViewer();
if ($this->getIsCreate()) {
$member_phids = array($viewer->getPHID());
} else {
$member_phids = $object->getMemberPHIDs();
}
$fields = array(
id(new PhabricatorTextEditField())
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Account name.'))
->setConduitTypeDescription(pht('New account name.'))
->setTransactionType(
PhortuneAccountNameTransaction::TRANSACTIONTYPE)
->setValue($object->getName())
->setIsRequired(true),
id(new PhabricatorUsersEditField())
->setKey('managers')
->setAliases(array('memberPHIDs', 'managerPHIDs'))
->setLabel(pht('Managers'))
->setUseEdgeTransactions(true)
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue(
'edge:type',
PhortuneAccountHasMemberEdgeType::EDGECONST)
->setDescription(pht('Initial account managers.'))
->setConduitDescription(pht('Set account managers.'))
->setConduitTypeDescription(pht('New list of managers.'))
->setInitialValue($object->getMemberPHIDs())
->setValue($member_phids),
);
return $fields;
}
}

View file

@ -1,6 +1,5 @@
<?php
final class PhortuneAccountEditor
extends PhabricatorApplicationTransactionEditor {
@ -12,57 +11,16 @@ final class PhortuneAccountEditor
return pht('Phortune Accounts');
}
public function getCreateObjectTitle($author, $object) {
return pht('%s created this payment account.', $author);
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhortuneAccountTransaction::TYPE_NAME;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhortuneAccountTransaction::TYPE_NAME:
return $object->getName();
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhortuneAccountTransaction::TYPE_NAME:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhortuneAccountTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhortuneAccountTransaction::TYPE_NAME:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
@ -71,47 +29,53 @@ final class PhortuneAccountEditor
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case PhortuneAccountTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Account name is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
case PhabricatorTransactions::TYPE_EDGE:
foreach ($xactions as $xaction) {
switch ($xaction->getMetadataValue('edge:type')) {
case PhortuneAccountHasMemberEdgeType::EDGECONST:
// TODO: This is a bit cumbersome, but validation happens before
// transaction normalization. Maybe provide a cleaner attack on
// this eventually? There's no way to generate "+" or "-"
// transactions right now.
$actor_phid = $this->requireActor()->getPHID();
$new = $xaction->getNewValue();
$set = idx($new, '=', array());
$old = $object->getMemberPHIDs();
if (empty($set[$this->requireActor()->getPHID()])) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht('You can not remove yourself as an account member.'),
$xaction);
$errors[] = $error;
// Check if user is trying to not set themselves on creation
if (!$old) {
$set = idx($new, '+', array());
$actor_set = false;
foreach ($set as $phid) {
if ($actor_phid == $phid) {
$actor_set = true;
}
}
if (!$actor_set) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht('You can not remove yourself as an account manager.'),
$xaction);
$errors[] = $error;
}
}
break;
// Check if user is trying to remove themselves on edit
$set = idx($new, '-', array());
foreach ($set as $phid) {
if ($actor_phid == $phid) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht('You can not remove yourself as an account manager.'),
$xaction);
$errors[] = $error;
}
}
break;
}
}
break;
}
return $errors;
}
}

View file

@ -58,7 +58,7 @@ final class PhortuneMerchantEditEngine
}
protected function getObjectViewURI($object) {
return $object->getViewURI();
return $object->getURI();
}
public function isEngineConfigurable() {

View file

@ -17,7 +17,6 @@ final class PhortuneAccount extends PhortuneDAO
public static function initializeNewAccount(PhabricatorUser $actor) {
$account = id(new PhortuneAccount());
$account->memberPHIDs = array();
return $account;
@ -31,7 +30,7 @@ final class PhortuneAccount extends PhortuneDAO
$xactions = array();
$xactions[] = id(new PhortuneAccountTransaction())
->setTransactionType(PhortuneAccountTransaction::TYPE_NAME)
->setTransactionType(PhortuneAccountNameTransaction::TRANSACTIONTYPE)
->setNewValue(pht('Default Account'));
$xactions[] = id(new PhortuneAccountTransaction())
@ -96,6 +95,10 @@ final class PhortuneAccount extends PhortuneDAO
return $this;
}
public function getURI() {
return '/phortune/'.$this->getID().'/';
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */

View file

@ -1,9 +1,7 @@
<?php
final class PhortuneAccountTransaction
extends PhabricatorApplicationTransaction {
const TYPE_NAME = 'phortune:name';
extends PhabricatorModularTransaction {
public function getApplicationName() {
return 'phortune';
@ -17,29 +15,8 @@ final class PhortuneAccountTransaction
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 account.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s renamed this account from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
break;
}
return parent::getTitle();
public function getBaseTransactionClass() {
return 'PhortuneAccountTransactionType';
}
}

View file

@ -53,7 +53,7 @@ final class PhortuneMerchant extends PhortuneDAO
return $this;
}
public function getViewURI() {
public function getURI() {
return '/phortune/merchant/'.$this->getID().'/';
}
@ -70,6 +70,7 @@ final class PhortuneMerchant extends PhortuneDAO
return $this->assertAttached($this->profileImageFile);
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */

View file

@ -0,0 +1,55 @@
<?php
final class PhortuneAccountNameTransaction
extends PhortuneAccountTransactionType {
const TRANSACTIONTYPE = 'phortune:name';
public function generateOldValue($object) {
return $object->getName();
}
public function applyInternalEffects($object, $value) {
$object->setName($value);
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
if (strlen($old) && strlen($new)) {
return pht(
'%s renamed this account from %s to %s.',
$this->renderAuthor(),
$this->renderOldValue(),
$this->renderNewValue());
} else {
return pht(
'%s created this account.',
$this->renderAuthor());
}
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
$errors[] = $this->newRequiredError(
pht('Accounts must have a name.'));
}
$max_length = $object->getColumnMaximumByteLength('name');
foreach ($xactions as $xaction) {
$new_value = $xaction->getNewValue();
$new_length = strlen($new_value);
if ($new_length > $max_length) {
$errors[] = $this->newRequiredError(
pht('The name can be no longer than %s characters.',
new PhutilNumber($max_length)));
}
}
return $errors;
}
}

View file

@ -0,0 +1,4 @@
<?php
abstract class PhortuneAccountTransactionType
extends PhabricatorModularTransactionType {}

View file

@ -1624,6 +1624,21 @@ final class PhabricatorUSEnglishTranslation
'%s removed merchant managers: %3$s.',
),
),
'%s added %s account manager(s): %s.' => array(
array(
'%s added an account manager: %3$s.',
'%s added account managers: %3$s.',
),
),
'%s removed %s account manager(s): %s.' => array(
array(
'%s removed an account manager: %3$s.',
'%s removed account managers: %3$s.',
),
),
);
}