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; case PhabricatorTransactions::TYPE_SUBSCRIBERS: case PhabricatorTransactions::TYPE_EDGE: case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: 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 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; case PhabricatorTransactions::TYPE_SUBSCRIBERS: case PhabricatorTransactions::TYPE_EDGE: case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: 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) { return true; } public function getMailTagsMap() { return array( FundInitiativeTransaction::MAILTAG_BACKER => pht('Someone backs an initiative.'), FundInitiativeTransaction::MAILTAG_STATUS => pht("An initiative's status changes."), FundInitiativeTransaction::MAILTAG_OTHER => pht('Other initiative activity not listed above occurs.'), ); } protected function buildMailTemplate(PhabricatorLiskDAO $object) { $monogram = $object->getMonogram(); $name = $object->getName(); return id(new PhabricatorMetaMTAMail()) ->setSubject("{$monogram}: {$name}") ->addHeader('Thread-Topic', $monogram); } protected function buildMailBody( PhabricatorLiskDAO $object, array $xactions) { $body = parent::buildMailBody($object, $xactions); $body->addTextSection( pht('INITIATIVE DETAIL'), PhabricatorEnv::getProductionURI('/'.$object->getMonogram())); return $body; } protected function getMailTo(PhabricatorLiskDAO $object) { return array($object->getOwnerPHID()); } protected function getMailSubjectPrefix() { return 'Fund'; } protected function buildReplyHandler(PhabricatorLiskDAO $object) { return id(new FundInitiativeReplyHandler()) ->setMailReceiver($object); } protected function shouldPublishFeedStory( PhabricatorLiskDAO $object, array $xactions) { return true; } protected function supportsSearch() { return true; } }