1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 16:52:41 +01:00

Update Fund for modular transactions

Summary: Fixes T12627. Updates FundInitiative and FundBacker with modular transactions.

Test Plan: Create an Initiative, back it with fake monies, close initiative, reopen, edit various fields.

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Maniphest Tasks: T12627

Differential Revision: https://secure.phabricator.com/D17782
This commit is contained in:
Chad Little 2017-05-05 11:02:54 -07:00
parent 0dc90b891a
commit a9604c4604
21 changed files with 609 additions and 489 deletions

View file

@ -1124,29 +1124,40 @@ phutil_register_library_map(array(
'FundBackerPHIDType' => 'applications/fund/phid/FundBackerPHIDType.php',
'FundBackerProduct' => 'applications/fund/phortune/FundBackerProduct.php',
'FundBackerQuery' => 'applications/fund/query/FundBackerQuery.php',
'FundBackerRefundTransaction' => 'applications/fund/xaction/FundBackerRefundTransaction.php',
'FundBackerSearchEngine' => 'applications/fund/query/FundBackerSearchEngine.php',
'FundBackerStatusTransaction' => 'applications/fund/xaction/FundBackerStatusTransaction.php',
'FundBackerTransaction' => 'applications/fund/storage/FundBackerTransaction.php',
'FundBackerTransactionQuery' => 'applications/fund/query/FundBackerTransactionQuery.php',
'FundBackerTransactionType' => 'applications/fund/xaction/FundBackerTransactionType.php',
'FundController' => 'applications/fund/controller/FundController.php',
'FundCreateInitiativesCapability' => 'applications/fund/capability/FundCreateInitiativesCapability.php',
'FundDAO' => 'applications/fund/storage/FundDAO.php',
'FundDefaultViewCapability' => 'applications/fund/capability/FundDefaultViewCapability.php',
'FundInitiative' => 'applications/fund/storage/FundInitiative.php',
'FundInitiativeBackController' => 'applications/fund/controller/FundInitiativeBackController.php',
'FundInitiativeBackerTransaction' => 'applications/fund/xaction/FundInitiativeBackerTransaction.php',
'FundInitiativeCloseController' => 'applications/fund/controller/FundInitiativeCloseController.php',
'FundInitiativeCommentController' => 'applications/fund/controller/FundInitiativeCommentController.php',
'FundInitiativeDescriptionTransaction' => 'applications/fund/xaction/FundInitiativeDescriptionTransaction.php',
'FundInitiativeEditController' => 'applications/fund/controller/FundInitiativeEditController.php',
'FundInitiativeEditor' => 'applications/fund/editor/FundInitiativeEditor.php',
'FundInitiativeFulltextEngine' => 'applications/fund/search/FundInitiativeFulltextEngine.php',
'FundInitiativeListController' => 'applications/fund/controller/FundInitiativeListController.php',
'FundInitiativeMerchantTransaction' => 'applications/fund/xaction/FundInitiativeMerchantTransaction.php',
'FundInitiativeNameTransaction' => 'applications/fund/xaction/FundInitiativeNameTransaction.php',
'FundInitiativePHIDType' => 'applications/fund/phid/FundInitiativePHIDType.php',
'FundInitiativeQuery' => 'applications/fund/query/FundInitiativeQuery.php',
'FundInitiativeRefundTransaction' => 'applications/fund/xaction/FundInitiativeRefundTransaction.php',
'FundInitiativeRemarkupRule' => 'applications/fund/remarkup/FundInitiativeRemarkupRule.php',
'FundInitiativeReplyHandler' => 'applications/fund/mail/FundInitiativeReplyHandler.php',
'FundInitiativeRisksTransaction' => 'applications/fund/xaction/FundInitiativeRisksTransaction.php',
'FundInitiativeSearchEngine' => 'applications/fund/query/FundInitiativeSearchEngine.php',
'FundInitiativeStatusTransaction' => 'applications/fund/xaction/FundInitiativeStatusTransaction.php',
'FundInitiativeTransaction' => 'applications/fund/storage/FundInitiativeTransaction.php',
'FundInitiativeTransactionComment' => 'applications/fund/storage/FundInitiativeTransactionComment.php',
'FundInitiativeTransactionQuery' => 'applications/fund/query/FundInitiativeTransactionQuery.php',
'FundInitiativeTransactionType' => 'applications/fund/xaction/FundInitiativeTransactionType.php',
'FundInitiativeViewController' => 'applications/fund/controller/FundInitiativeViewController.php',
'FundSchemaSpec' => 'applications/fund/storage/FundSchemaSpec.php',
'HarbormasterArcLintBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcLintBuildStepImplementation.php',
@ -6066,9 +6077,12 @@ phutil_register_library_map(array(
'FundBackerPHIDType' => 'PhabricatorPHIDType',
'FundBackerProduct' => 'PhortuneProductImplementation',
'FundBackerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'FundBackerRefundTransaction' => 'FundBackerTransactionType',
'FundBackerSearchEngine' => 'PhabricatorApplicationSearchEngine',
'FundBackerTransaction' => 'PhabricatorApplicationTransaction',
'FundBackerStatusTransaction' => 'FundBackerTransactionType',
'FundBackerTransaction' => 'PhabricatorModularTransaction',
'FundBackerTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'FundBackerTransactionType' => 'PhabricatorModularTransactionType',
'FundController' => 'PhabricatorController',
'FundCreateInitiativesCapability' => 'PhabricatorPolicyCapability',
'FundDAO' => 'PhabricatorLiskDAO',
@ -6086,20 +6100,28 @@ phutil_register_library_map(array(
'PhabricatorFulltextInterface',
),
'FundInitiativeBackController' => 'FundController',
'FundInitiativeBackerTransaction' => 'FundInitiativeTransactionType',
'FundInitiativeCloseController' => 'FundController',
'FundInitiativeCommentController' => 'FundController',
'FundInitiativeDescriptionTransaction' => 'FundInitiativeTransactionType',
'FundInitiativeEditController' => 'FundController',
'FundInitiativeEditor' => 'PhabricatorApplicationTransactionEditor',
'FundInitiativeFulltextEngine' => 'PhabricatorFulltextEngine',
'FundInitiativeListController' => 'FundController',
'FundInitiativeMerchantTransaction' => 'FundInitiativeTransactionType',
'FundInitiativeNameTransaction' => 'FundInitiativeTransactionType',
'FundInitiativePHIDType' => 'PhabricatorPHIDType',
'FundInitiativeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'FundInitiativeRefundTransaction' => 'FundInitiativeTransactionType',
'FundInitiativeRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'FundInitiativeReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'FundInitiativeRisksTransaction' => 'FundInitiativeTransactionType',
'FundInitiativeSearchEngine' => 'PhabricatorApplicationSearchEngine',
'FundInitiativeTransaction' => 'PhabricatorApplicationTransaction',
'FundInitiativeStatusTransaction' => 'FundInitiativeTransactionType',
'FundInitiativeTransaction' => 'PhabricatorModularTransaction',
'FundInitiativeTransactionComment' => 'PhabricatorApplicationTransactionComment',
'FundInitiativeTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'FundInitiativeTransactionType' => 'PhabricatorModularTransactionType',
'FundInitiativeViewController' => 'FundController',
'FundSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'HarbormasterArcLintBuildStepImplementation' => 'HarbormasterBuildStepImplementation',

View file

@ -96,7 +96,7 @@ final class FundInitiativeBackController
$xactions = array();
$xactions[] = id(new FundBackerTransaction())
->setTransactionType(FundBackerTransaction::TYPE_STATUS)
->setTransactionType(FundBackerStatusTransaction::TRANSACTIONTYPE)
->setNewValue(FundBacker::STATUS_IN_CART);
$editor = id(new FundBackerEditor())

View file

@ -25,7 +25,7 @@ final class FundInitiativeCloseController
$is_close = !$initiative->isClosed();
if ($request->isFormPost()) {
$type_status = FundInitiativeTransaction::TYPE_STATUS;
$type_status = FundInitiativeStatusTransaction::TRANSACTIONTYPE;
if ($is_close) {
$new_status = FundInitiative::STATUS_CLOSED;

View file

@ -68,10 +68,10 @@ final class FundInitiativeEditController
$v_merchant = $request->getStr('merchantPHID');
$v_projects = $request->getArr('projects');
$type_name = FundInitiativeTransaction::TYPE_NAME;
$type_desc = FundInitiativeTransaction::TYPE_DESCRIPTION;
$type_risk = FundInitiativeTransaction::TYPE_RISKS;
$type_merchant = FundInitiativeTransaction::TYPE_MERCHANT;
$type_name = FundInitiativeNameTransaction::TRANSACTIONTYPE;
$type_desc = FundInitiativeDescriptionTransaction::TRANSACTIONTYPE;
$type_risk = FundInitiativeRisksTransaction::TRANSACTIONTYPE;
$type_merchant = FundInitiativeMerchantTransaction::TRANSACTIONTYPE;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;

View file

@ -29,8 +29,8 @@ final class FundInitiativeViewController
$initiative->getName());
if ($initiative->isClosed()) {
$status_icon = 'fa-times';
$status_color = 'bluegrey';
$status_icon = 'fa-ban';
$status_color = 'indigo';
} else {
$status_icon = 'fa-check';
$status_color = 'bluegrey';

View file

@ -11,67 +11,4 @@ final class FundBackerEditor
return pht('Fund Backing');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = FundBackerTransaction::TYPE_STATUS;
$types[] = FundBackerTransaction::TYPE_REFUND;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case FundBackerTransaction::TYPE_STATUS:
return $object->getStatus();
case FundBackerTransaction::TYPE_REFUND:
return null;
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case FundBackerTransaction::TYPE_STATUS:
case FundBackerTransaction::TYPE_REFUND:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case FundBackerTransaction::TYPE_STATUS:
$object->setStatus($xaction->getNewValue());
return;
case FundBackerTransaction::TYPE_REFUND:
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case FundBackerTransaction::TYPE_STATUS:
case FundBackerTransaction::TYPE_REFUND:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
}

View file

@ -11,16 +11,16 @@ final class FundInitiativeEditor
return pht('Fund Initiatives');
}
public function getCreateObjectTitle($author, $object) {
return pht('%s created this initiative.', $author);
}
public function getCreateObjectTitleForFeed($author, $object) {
return pht('%s created %s.', $author, $object);
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = FundInitiativeTransaction::TYPE_NAME;
$types[] = FundInitiativeTransaction::TYPE_DESCRIPTION;
$types[] = FundInitiativeTransaction::TYPE_RISKS;
$types[] = FundInitiativeTransaction::TYPE_STATUS;
$types[] = FundInitiativeTransaction::TYPE_BACKER;
$types[] = FundInitiativeTransaction::TYPE_REFUND;
$types[] = FundInitiativeTransaction::TYPE_MERCHANT;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
@ -28,204 +28,6 @@ final class FundInitiativeEditor
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case FundInitiativeTransaction::TYPE_NAME:
return $object->getName();
case FundInitiativeTransaction::TYPE_DESCRIPTION:
return $object->getDescription();
case FundInitiativeTransaction::TYPE_RISKS:
return $object->getRisks();
case FundInitiativeTransaction::TYPE_STATUS:
return $object->getStatus();
case FundInitiativeTransaction::TYPE_BACKER:
case FundInitiativeTransaction::TYPE_REFUND:
return null;
case FundInitiativeTransaction::TYPE_MERCHANT:
return $object->getMerchantPHID();
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case FundInitiativeTransaction::TYPE_NAME:
case FundInitiativeTransaction::TYPE_DESCRIPTION:
case FundInitiativeTransaction::TYPE_RISKS:
case FundInitiativeTransaction::TYPE_STATUS:
case FundInitiativeTransaction::TYPE_BACKER:
case FundInitiativeTransaction::TYPE_REFUND:
case FundInitiativeTransaction::TYPE_MERCHANT:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$type = $xaction->getTransactionType();
switch ($type) {
case FundInitiativeTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
case FundInitiativeTransaction::TYPE_DESCRIPTION:
$object->setDescription($xaction->getNewValue());
return;
case FundInitiativeTransaction::TYPE_RISKS:
$object->setRisks($xaction->getNewValue());
return;
case FundInitiativeTransaction::TYPE_MERCHANT:
$object->setMerchantPHID($xaction->getNewValue());
return;
case FundInitiativeTransaction::TYPE_STATUS:
$object->setStatus($xaction->getNewValue());
return;
case FundInitiativeTransaction::TYPE_BACKER:
case FundInitiativeTransaction::TYPE_REFUND:
$amount = $xaction->getMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT);
$amount = PhortuneCurrency::newFromString($amount);
if ($type == FundInitiativeTransaction::TYPE_REFUND) {
$total = $object->getTotalAsCurrency()->subtract($amount);
} else {
$total = $object->getTotalAsCurrency()->add($amount);
}
$object->setTotalAsCurrency($total);
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$type = $xaction->getTransactionType();
switch ($type) {
case FundInitiativeTransaction::TYPE_NAME:
case FundInitiativeTransaction::TYPE_DESCRIPTION:
case FundInitiativeTransaction::TYPE_RISKS:
case FundInitiativeTransaction::TYPE_STATUS:
case FundInitiativeTransaction::TYPE_MERCHANT:
return;
case FundInitiativeTransaction::TYPE_BACKER:
case FundInitiativeTransaction::TYPE_REFUND:
$backer = id(new FundBackerQuery())
->setViewer($this->requireActor())
->withPHIDs(array($xaction->getNewValue()))
->executeOne();
if (!$backer) {
throw new Exception(pht('Unable to load %s!', 'FundBacker'));
}
$subx = array();
if ($type == FundInitiativeTransaction::TYPE_BACKER) {
$subx[] = id(new FundBackerTransaction())
->setTransactionType(FundBackerTransaction::TYPE_STATUS)
->setNewValue(FundBacker::STATUS_PURCHASED);
} else {
$amount = $xaction->getMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT);
$subx[] = id(new FundBackerTransaction())
->setTransactionType(FundBackerTransaction::TYPE_STATUS)
->setNewValue($amount);
}
$editor = id(new FundBackerEditor())
->setActor($this->requireActor())
->setContentSource($this->getContentSource())
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true);
$editor->applyTransactions($backer, $subx);
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case FundInitiativeTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Initiative name is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
case FundInitiativeTransaction::TYPE_MERCHANT:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Payable merchant is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
} else if ($xactions) {
$merchant_phid = last($xactions)->getNewValue();
// Make sure the actor has permission to edit the merchant they're
// selecting. You aren't allowed to send payments to an account you
// do not control.
$merchants = id(new PhortuneMerchantQuery())
->setViewer($this->requireActor())
->withPHIDs(array($merchant_phid))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
if (!$merchants) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'You must specify a merchant account you control as the '.
'recipient of funds from this initiative.'),
last($xactions));
$errors[] = $error;
}
}
break;
}
return $errors;
}
protected function shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {

View file

@ -105,7 +105,7 @@ final class FundBackerProduct extends PhortuneProductImplementation {
$xactions = array();
$xactions[] = id(new FundInitiativeTransaction())
->setTransactionType(FundInitiativeTransaction::TYPE_BACKER)
->setTransactionType(FundInitiativeBackerTransaction::TRANSACTIONTYPE)
->setMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT,
$backer->getAmountAsCurrency()->serializeForStorage())
@ -134,7 +134,7 @@ final class FundBackerProduct extends PhortuneProductImplementation {
$xactions = array();
$xactions[] = id(new FundInitiativeTransaction())
->setTransactionType(FundInitiativeTransaction::TYPE_REFUND)
->setTransactionType(FundInitiativeRefundTransaction::TRANSACTIONTYPE)
->setMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT,
$amount->serializeForStorage())

View file

@ -1,10 +1,7 @@
<?php
final class FundBackerTransaction
extends PhabricatorApplicationTransaction {
const TYPE_STATUS = 'fund:backer:status';
const TYPE_REFUND = 'fund:backer:refund';
extends PhabricatorModularTransaction {
public function getApplicationName() {
return 'fund';
@ -18,4 +15,8 @@ final class FundBackerTransaction
return null;
}
public function getBaseTransactionClass() {
return 'FundBackerTransactionType';
}
}

View file

@ -1,15 +1,7 @@
<?php
final class FundInitiativeTransaction
extends PhabricatorApplicationTransaction {
const TYPE_NAME = 'fund:name';
const TYPE_DESCRIPTION = 'fund:description';
const TYPE_RISKS = 'fund:risks';
const TYPE_STATUS = 'fund:status';
const TYPE_BACKER = 'fund:backer';
const TYPE_REFUND = 'fund:refund';
const TYPE_MERCHANT = 'fund:merchant';
extends PhabricatorModularTransaction {
const MAILTAG_BACKER = 'fund.backer';
const MAILTAG_STATUS = 'fund.status';
@ -30,174 +22,8 @@ final class FundInitiativeTransaction
return new FundInitiativeTransactionComment();
}
public function getRequiredHandlePHIDs() {
$phids = parent::getRequiredHandlePHIDs();
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_MERCHANT:
if ($old) {
$phids[] = $old;
}
if ($new) {
$phids[] = $new;
}
break;
case self::TYPE_REFUND:
$phids[] = $this->getMetadataValue(self::PROPERTY_BACKER);
break;
}
return $phids;
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_NAME:
if ($old === null) {
return pht(
'%s created this initiative.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s renamed this initiative from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
break;
case self::TYPE_RISKS:
return pht(
'%s edited the risks for this initiative.',
$this->renderHandleLink($author_phid));
case self::TYPE_DESCRIPTION:
return pht(
'%s edited the description of this initiative.',
$this->renderHandleLink($author_phid));
case self::TYPE_STATUS:
switch ($new) {
case FundInitiative::STATUS_OPEN:
return pht(
'%s reopened this initiative.',
$this->renderHandleLink($author_phid));
case FundInitiative::STATUS_CLOSED:
return pht(
'%s closed this initiative.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_BACKER:
$amount = $this->getMetadataValue(self::PROPERTY_AMOUNT);
$amount = PhortuneCurrency::newFromString($amount);
return pht(
'%s backed this initiative with %s.',
$this->renderHandleLink($author_phid),
$amount->formatForDisplay());
case self::TYPE_REFUND:
$amount = $this->getMetadataValue(self::PROPERTY_AMOUNT);
$amount = PhortuneCurrency::newFromString($amount);
$backer_phid = $this->getMetadataValue(self::PROPERTY_BACKER);
return pht(
'%s refunded %s to %s.',
$this->renderHandleLink($author_phid),
$amount->formatForDisplay(),
$this->renderHandleLink($backer_phid));
case self::TYPE_MERCHANT:
if ($old === null) {
return pht(
'%s set this initiative to pay to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($new));
} else {
return pht(
'%s changed the merchant receiving funds from this '.
'initiative from %s to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($old),
$this->renderHandleLink($new));
}
}
return parent::getTitle();
}
public function getTitleForFeed() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_NAME:
if ($old === null) {
return pht(
'%s created %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
} else {
return pht(
'%s renamed %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
}
break;
case self::TYPE_DESCRIPTION:
return pht(
'%s updated the description for %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
case self::TYPE_STATUS:
switch ($new) {
case FundInitiative::STATUS_OPEN:
return pht(
'%s reopened %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
case FundInitiative::STATUS_CLOSED:
return pht(
'%s closed %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
}
break;
case self::TYPE_BACKER:
$amount = $this->getMetadataValue(self::PROPERTY_AMOUNT);
$amount = PhortuneCurrency::newFromString($amount);
return pht(
'%s backed %s with %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid),
$amount->formatForDisplay());
case self::TYPE_REFUND:
$amount = $this->getMetadataValue(self::PROPERTY_AMOUNT);
$amount = PhortuneCurrency::newFromString($amount);
$backer_phid = $this->getMetadataValue(self::PROPERTY_BACKER);
return pht(
'%s refunded %s to %s for %s.',
$this->renderHandleLink($author_phid),
$amount->formatForDisplay(),
$this->renderHandleLink($backer_phid),
$this->renderHandleLink($object_phid));
}
return parent::getTitleForFeed();
public function getBaseTransactionClass() {
return 'FundInitiativeTransactionType';
}
public function getMailTags() {
@ -219,31 +45,4 @@ final class FundInitiativeTransaction
return $tags;
}
public function shouldHide() {
$old = $this->getOldValue();
switch ($this->getTransactionType()) {
case self::TYPE_DESCRIPTION:
case self::TYPE_RISKS:
return ($old === null);
}
return parent::shouldHide();
}
public function hasChangeDetails() {
switch ($this->getTransactionType()) {
case self::TYPE_DESCRIPTION:
case self::TYPE_RISKS:
return ($this->getOldValue() !== null);
}
return parent::hasChangeDetails();
}
public function renderChangeDetails(PhabricatorUser $viewer) {
return $this->renderTextCorpusChangeDetails(
$viewer,
$this->getOldValue(),
$this->getNewValue());
}
}

View file

@ -0,0 +1,13 @@
<?php
final class FundBackerRefundTransaction
extends FundBackerTransactionType {
const TRANSACTIONTYPE = 'fund:backer:refund';
public function generateOldValue($object) {
return null;
}
}

View file

@ -0,0 +1,17 @@
<?php
final class FundBackerStatusTransaction
extends FundBackerTransactionType {
const TRANSACTIONTYPE = 'fund:backer:status';
public function generateOldValue($object) {
return $object->getStatus();
}
public function applyInternalEffects($object, $value) {
$object->setStatus($value);
}
}

View file

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

View file

@ -0,0 +1,74 @@
<?php
final class FundInitiativeBackerTransaction
extends FundInitiativeTransactionType {
const TRANSACTIONTYPE = 'fund:backer';
public function generateOldValue($object) {
return null;
}
public function applyInternalEffects($object, $value) {
$amount = $this->getMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT);
$amount = PhortuneCurrency::newFromString($amount);
$total = $object->getTotalAsCurrency()->add($amount);
$object->setTotalAsCurrency($total);
}
public function applyExternalEffects($object, $value) {
$backer = id(new FundBackerQuery())
->setViewer($this->getActor())
->withPHIDs(array($value))
->executeOne();
if (!$backer) {
throw new Exception(pht('Unable to load %s!', 'FundBacker'));
}
$subx = array();
$subx[] = id(new FundBackerTransaction())
->setTransactionType(FundBackerStatusTransaction::TRANSACTIONTYPE)
->setNewValue(FundBacker::STATUS_PURCHASED);
$content_source = $this->getEditor()->getContentSource();
$editor = id(new FundBackerEditor())
->setActor($this->getActor())
->setContentSource($content_source)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true);
$editor->applyTransactions($backer, $subx);
}
public function getTitle() {
$amount = $this->getMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT);
$amount = PhortuneCurrency::newFromString($amount);
return pht(
'%s backed this initiative with %s.',
$this->renderAuthor(),
$amount->formatForDisplay());
}
public function getTitleForFeed() {
$amount = $this->getMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT);
$amount = PhortuneCurrency::newFromString($amount);
return pht(
'%s backed %s.',
$this->renderAuthor(),
$this->renderObject());
}
public function getIcon() {
return 'fa-heart';
}
public function getColor() {
return 'red';
}
}

View file

@ -0,0 +1,75 @@
<?php
final class FundInitiativeDescriptionTransaction
extends FundInitiativeTransactionType {
const TRANSACTIONTYPE = 'fund:description';
public function generateOldValue($object) {
return $object->getDescription();
}
public function applyInternalEffects($object, $value) {
$object->setDescription($value);
}
public function shouldHide() {
$old = $this->getOldValue();
$new = $this->getNewValue();
if (!strlen($old) && !strlen($new)) {
return true;
}
return false;
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
if ($old === null) {
return pht(
'%s set the initiative description.',
$this->renderAuthor());
} else {
return pht(
'%s updated the initiative description.',
$this->renderAuthor());
}
}
public function getTitleForFeed() {
return pht(
'%s updated the initiative description for %s.',
$this->renderAuthor(),
$this->renderObject());
}
public function hasChangeDetailView() {
return true;
}
public function getMailDiffSectionHeader() {
return pht('CHANGES TO INITIATIVE DESCRIPTION');
}
public function newChangeDetailView() {
$viewer = $this->getViewer();
return id(new PhabricatorApplicationTransactionTextDiffDetailView())
->setViewer($viewer)
->setOldText($this->getOldValue())
->setNewText($this->getNewValue());
}
public function newRemarkupChanges() {
$changes = array();
$changes[] = $this->newRemarkupChange()
->setOldValue($this->getOldValue())
->setNewValue($this->getNewValue());
return $changes;
}
}

View file

@ -0,0 +1,93 @@
<?php
final class FundInitiativeMerchantTransaction
extends FundInitiativeTransactionType {
const TRANSACTIONTYPE = 'fund:merchant';
public function generateOldValue($object) {
return $object->getMerchantPHID();
}
public function applyInternalEffects($object, $value) {
$object->setMerchantPHID($value);
}
public function getTitle() {
$new = $this->getNewValue();
$new_merchant = $this->renderHandleList(array($new));
$old = $this->getOldValue();
$old_merchant = $this->renderHandleList(array($old));
if ($old) {
return pht(
'%s changed the merchant receiving funds from this '.
'initiative from %s to %s.',
$this->renderAuthor(),
$old_merchant,
$new_merchant);
} else {
return pht(
'%s set the merchant receiving funds from this '.
'initiative to %s.',
$this->renderAuthor(),
$new_merchant);
}
}
public function getTitleForFeed() {
$new = $this->getNewValue();
$new_merchant = $this->renderHandleList(array($new));
$old = $this->getOldValue();
$old_merchant = $this->renderHandleList(array($old));
return pht(
'%s changed the merchant receiving funds from %s '.
'initiative from %s to %s.',
$this->renderAuthor(),
$this->renderObject(),
$old_merchant,
$new_merchant);
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getMerchantPHID(), $xactions)) {
$errors[] = $this->newRequiredError(
pht('Initiatives must have a payable merchant.'));
}
foreach ($xactions as $xaction) {
$merchant_phid = $xaction->getNewValue();
// Make sure the actor has permission to edit the merchant they're
// selecting. You aren't allowed to send payments to an account you
// do not control.
$merchants = id(new PhortuneMerchantQuery())
->setViewer($this->getActor())
->withPHIDs(array($merchant_phid))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
if (!$merchants) {
$errors[] = $this->newInvalidError(
pht('You must specify a merchant account you control as the '.
'recipient of funds from this initiative.'));
}
}
return $errors;
}
public function getIcon() {
return 'fa-bank';
}
}

View file

@ -0,0 +1,71 @@
<?php
final class FundInitiativeNameTransaction
extends FundInitiativeTransactionType {
const TRANSACTIONTYPE = 'fund:name';
public function generateOldValue($object) {
return $object->getName();
}
public function applyInternalEffects($object, $value) {
$object->setName($value);
}
public function getTitle() {
$old = $this->getOldValue();
if (!strlen($old)) {
return pht(
'%s created this initiative.',
$this->renderAuthor());
} else {
return pht(
'%s renamed this initiative from %s to %s.',
$this->renderAuthor(),
$this->renderOldValue(),
$this->renderNewValue());
}
}
public function getTitleForFeed() {
$old = $this->getOldValue();
if (!strlen($old)) {
return pht(
'%s created initiative %s.',
$this->renderAuthor(),
$this->renderObject());
} else {
return pht(
'%s renamed %s initiative from %s to %s.',
$this->renderAuthor(),
$this->renderObject(),
$this->renderOldValue(),
$this->renderNewValue());
}
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
$errors[] = $this->newRequiredError(
pht('Initiatives 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->newInvalidError(
pht('The name can be no longer than %s characters.',
new PhutilNumber($max_length)));
}
}
return $errors;
}
}

View file

@ -0,0 +1,77 @@
<?php
final class FundInitiativeRefundTransaction
extends FundInitiativeTransactionType {
const TRANSACTIONTYPE = 'fund:refund';
public function generateOldValue($object) {
return null;
}
public function applyInternalEffects($object, $value) {
$amount = $this->getMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT);
$amount = PhortuneCurrency::newFromString($amount);
$total = $object->getTotalAsCurrency()->subtract($amount);
$object->setTotalAsCurrency($total);
}
public function applyExternalEffects($object, $value) {
$backer = id(new FundBackerQuery())
->setViewer($this->getActor())
->withPHIDs(array($value))
->executeOne();
if (!$backer) {
throw new Exception(pht('Unable to load %s!', 'FundBacker'));
}
$subx = array();
$amount = $this->getMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT);
$subx[] = id(new FundBackerTransaction())
->setTransactionType(FundBackerStatusTransaction::TRANSACTIONTYPE)
->setNewValue($amount);
$content_source = $this->getEditor()->getContentSource();
$editor = id(new FundBackerEditor())
->setActor($this->getActor())
->setContentSource($content_source)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true);
$editor->applyTransactions($backer, $subx);
}
public function getTitle() {
$amount = $this->getMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT);
$amount = PhortuneCurrency::newFromString($amount);
$backer_phid = $this->getMetadataValue(
FundInitiativeTransaction::PROPERTY_BACKER);
return pht(
'%s refunded %s to %s.',
$this->renderAuthor(),
$amount->formatForDisplay(),
$this->renderHandleLink($backer_phid));
}
public function getTitleForFeed() {
$amount = $this->getMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT);
$amount = PhortuneCurrency::newFromString($amount);
$backer_phid = $this->getMetadataValue(
FundInitiativeTransaction::PROPERTY_BACKER);
return pht(
'%s refunded %s to %s for %s.',
$this->renderAuthor(),
$amount->formatForDisplay(),
$this->renderHandleLink($backer_phid),
$this->renderObject());
}
}

View file

@ -0,0 +1,80 @@
<?php
final class FundInitiativeRisksTransaction
extends FundInitiativeTransactionType {
const TRANSACTIONTYPE = 'fund:risks';
public function generateOldValue($object) {
return $object->getRisks();
}
public function applyInternalEffects($object, $value) {
$object->setRisks($value);
}
public function shouldHide() {
$old = $this->getOldValue();
$new = $this->getNewValue();
if (!strlen($old) && !strlen($new)) {
return true;
}
return false;
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
if ($old === null) {
return pht(
'%s set the initiative risks/challenges.',
$this->renderAuthor());
} else {
return pht(
'%s updated the initiative risks/challenges.',
$this->renderAuthor());
}
}
public function getTitleForFeed() {
return pht(
'%s updated the initiative risks/challenges for %s.',
$this->renderAuthor(),
$this->renderObject());
}
public function hasChangeDetailView() {
return true;
}
public function getMailDiffSectionHeader() {
return pht('CHANGES TO INITIATIVE RISKS/CHALLENGES');
}
public function newChangeDetailView() {
$viewer = $this->getViewer();
return id(new PhabricatorApplicationTransactionTextDiffDetailView())
->setViewer($viewer)
->setOldText($this->getOldValue())
->setNewText($this->getNewValue());
}
public function newRemarkupChanges() {
$changes = array();
$changes[] = $this->newRemarkupChange()
->setOldValue($this->getOldValue())
->setNewValue($this->getNewValue());
return $changes;
}
public function getIcon() {
return 'fa-ambulance';
}
}

View file

@ -0,0 +1,51 @@
<?php
final class FundInitiativeStatusTransaction
extends FundInitiativeTransactionType {
const TRANSACTIONTYPE = 'fund:status';
public function generateOldValue($object) {
return $object->getStatus();
}
public function applyInternalEffects($object, $value) {
$object->setStatus($value);
}
public function getTitle() {
if ($this->getNewValue() == FundInitiative::STATUS_CLOSED) {
return pht(
'%s closed this initiative.',
$this->renderAuthor());
} else {
return pht(
'%s reopened this initiative.',
$this->renderAuthor());
}
}
public function getTitleForFeed() {
if ($this->getNewValue() == FundInitiative::STATUS_CLOSED) {
return pht(
'%s closed the initiative %s.',
$this->renderAuthor(),
$this->renderObject());
} else {
return pht(
'%s reopened the initiative %s.',
$this->renderAuthor(),
$this->renderObject());
}
}
public function getIcon() {
if ($this->getNewValue() == FundInitiative::STATUS_CLOSED) {
return 'fa-ban';
} else {
return 'fa-check';
}
}
}

View file

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