Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F18253465
D17782.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
45 KB
Referenced Files
None
Subscribers
None
D17782.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1124,29 +1124,40 @@
'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 @@
'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 @@
'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',
diff --git a/src/applications/fund/controller/FundInitiativeBackController.php b/src/applications/fund/controller/FundInitiativeBackController.php
--- a/src/applications/fund/controller/FundInitiativeBackController.php
+++ b/src/applications/fund/controller/FundInitiativeBackController.php
@@ -96,7 +96,7 @@
$xactions = array();
$xactions[] = id(new FundBackerTransaction())
- ->setTransactionType(FundBackerTransaction::TYPE_STATUS)
+ ->setTransactionType(FundBackerStatusTransaction::TRANSACTIONTYPE)
->setNewValue(FundBacker::STATUS_IN_CART);
$editor = id(new FundBackerEditor())
diff --git a/src/applications/fund/controller/FundInitiativeCloseController.php b/src/applications/fund/controller/FundInitiativeCloseController.php
--- a/src/applications/fund/controller/FundInitiativeCloseController.php
+++ b/src/applications/fund/controller/FundInitiativeCloseController.php
@@ -25,7 +25,7 @@
$is_close = !$initiative->isClosed();
if ($request->isFormPost()) {
- $type_status = FundInitiativeTransaction::TYPE_STATUS;
+ $type_status = FundInitiativeStatusTransaction::TRANSACTIONTYPE;
if ($is_close) {
$new_status = FundInitiative::STATUS_CLOSED;
diff --git a/src/applications/fund/controller/FundInitiativeEditController.php b/src/applications/fund/controller/FundInitiativeEditController.php
--- a/src/applications/fund/controller/FundInitiativeEditController.php
+++ b/src/applications/fund/controller/FundInitiativeEditController.php
@@ -68,10 +68,10 @@
$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;
diff --git a/src/applications/fund/controller/FundInitiativeViewController.php b/src/applications/fund/controller/FundInitiativeViewController.php
--- a/src/applications/fund/controller/FundInitiativeViewController.php
+++ b/src/applications/fund/controller/FundInitiativeViewController.php
@@ -29,8 +29,8 @@
$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';
diff --git a/src/applications/fund/editor/FundBackerEditor.php b/src/applications/fund/editor/FundBackerEditor.php
--- a/src/applications/fund/editor/FundBackerEditor.php
+++ b/src/applications/fund/editor/FundBackerEditor.php
@@ -11,67 +11,4 @@
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);
- }
-
}
diff --git a/src/applications/fund/editor/FundInitiativeEditor.php b/src/applications/fund/editor/FundInitiativeEditor.php
--- a/src/applications/fund/editor/FundInitiativeEditor.php
+++ b/src/applications/fund/editor/FundInitiativeEditor.php
@@ -11,16 +11,16 @@
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 @@
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) {
diff --git a/src/applications/fund/phortune/FundBackerProduct.php b/src/applications/fund/phortune/FundBackerProduct.php
--- a/src/applications/fund/phortune/FundBackerProduct.php
+++ b/src/applications/fund/phortune/FundBackerProduct.php
@@ -105,7 +105,7 @@
$xactions = array();
$xactions[] = id(new FundInitiativeTransaction())
- ->setTransactionType(FundInitiativeTransaction::TYPE_BACKER)
+ ->setTransactionType(FundInitiativeBackerTransaction::TRANSACTIONTYPE)
->setMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT,
$backer->getAmountAsCurrency()->serializeForStorage())
@@ -134,7 +134,7 @@
$xactions = array();
$xactions[] = id(new FundInitiativeTransaction())
- ->setTransactionType(FundInitiativeTransaction::TYPE_REFUND)
+ ->setTransactionType(FundInitiativeRefundTransaction::TRANSACTIONTYPE)
->setMetadataValue(
FundInitiativeTransaction::PROPERTY_AMOUNT,
$amount->serializeForStorage())
diff --git a/src/applications/fund/storage/FundBackerTransaction.php b/src/applications/fund/storage/FundBackerTransaction.php
--- a/src/applications/fund/storage/FundBackerTransaction.php
+++ b/src/applications/fund/storage/FundBackerTransaction.php
@@ -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 @@
return null;
}
+ public function getBaseTransactionClass() {
+ return 'FundBackerTransactionType';
+ }
+
}
diff --git a/src/applications/fund/storage/FundInitiativeTransaction.php b/src/applications/fund/storage/FundInitiativeTransaction.php
--- a/src/applications/fund/storage/FundInitiativeTransaction.php
+++ b/src/applications/fund/storage/FundInitiativeTransaction.php
@@ -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 @@
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 @@
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());
- }
}
diff --git a/src/applications/fund/xaction/FundBackerRefundTransaction.php b/src/applications/fund/xaction/FundBackerRefundTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fund/xaction/FundBackerRefundTransaction.php
@@ -0,0 +1,13 @@
+<?php
+
+final class FundBackerRefundTransaction
+ extends FundBackerTransactionType {
+
+ const TRANSACTIONTYPE = 'fund:backer:refund';
+
+ public function generateOldValue($object) {
+ return null;
+ }
+
+
+}
diff --git a/src/applications/fund/xaction/FundBackerStatusTransaction.php b/src/applications/fund/xaction/FundBackerStatusTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fund/xaction/FundBackerStatusTransaction.php
@@ -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);
+ }
+
+
+}
diff --git a/src/applications/fund/xaction/FundBackerTransactionType.php b/src/applications/fund/xaction/FundBackerTransactionType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fund/xaction/FundBackerTransactionType.php
@@ -0,0 +1,4 @@
+<?php
+
+abstract class FundBackerTransactionType
+ extends PhabricatorModularTransactionType {}
diff --git a/src/applications/fund/xaction/FundInitiativeBackerTransaction.php b/src/applications/fund/xaction/FundInitiativeBackerTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fund/xaction/FundInitiativeBackerTransaction.php
@@ -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';
+ }
+
+
+}
diff --git a/src/applications/fund/xaction/FundInitiativeDescriptionTransaction.php b/src/applications/fund/xaction/FundInitiativeDescriptionTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fund/xaction/FundInitiativeDescriptionTransaction.php
@@ -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;
+ }
+
+
+}
diff --git a/src/applications/fund/xaction/FundInitiativeMerchantTransaction.php b/src/applications/fund/xaction/FundInitiativeMerchantTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fund/xaction/FundInitiativeMerchantTransaction.php
@@ -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';
+ }
+
+
+}
diff --git a/src/applications/fund/xaction/FundInitiativeNameTransaction.php b/src/applications/fund/xaction/FundInitiativeNameTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fund/xaction/FundInitiativeNameTransaction.php
@@ -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;
+ }
+
+
+}
diff --git a/src/applications/fund/xaction/FundInitiativeRefundTransaction.php b/src/applications/fund/xaction/FundInitiativeRefundTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fund/xaction/FundInitiativeRefundTransaction.php
@@ -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());
+ }
+
+
+}
diff --git a/src/applications/fund/xaction/FundInitiativeRisksTransaction.php b/src/applications/fund/xaction/FundInitiativeRisksTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fund/xaction/FundInitiativeRisksTransaction.php
@@ -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';
+ }
+
+
+}
diff --git a/src/applications/fund/xaction/FundInitiativeStatusTransaction.php b/src/applications/fund/xaction/FundInitiativeStatusTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fund/xaction/FundInitiativeStatusTransaction.php
@@ -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';
+ }
+ }
+
+
+}
diff --git a/src/applications/fund/xaction/FundInitiativeTransactionType.php b/src/applications/fund/xaction/FundInitiativeTransactionType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fund/xaction/FundInitiativeTransactionType.php
@@ -0,0 +1,4 @@
+<?php
+
+abstract class FundInitiativeTransactionType
+ extends PhabricatorModularTransactionType {}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Aug 22, 9:37 PM (19 h, 50 m)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/mq/z4/upjkeqaemgm2v547
Default Alt Text
D17782.diff (45 KB)
Attached To
Mode
D17782: Update Fund for modular transactions
Attached
Detach File
Event Timeline
Log In to Comment