diff --git a/resources/sql/autopatches/20141010.fundmailkey.sql b/resources/sql/autopatches/20141010.fundmailkey.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20141010.fundmailkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_fund.fund_initiative + ADD mailKey BINARY(20) NOT NULL; 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 @@ -688,6 +688,7 @@ 'FundInitiativePHIDType' => 'applications/fund/phid/FundInitiativePHIDType.php', 'FundInitiativeQuery' => 'applications/fund/query/FundInitiativeQuery.php', 'FundInitiativeRemarkupRule' => 'applications/fund/remarkup/FundInitiativeRemarkupRule.php', + 'FundInitiativeReplyHandler' => 'applications/fund/mail/FundInitiativeReplyHandler.php', 'FundInitiativeSearchEngine' => 'applications/fund/query/FundInitiativeSearchEngine.php', 'FundInitiativeTransaction' => 'applications/fund/storage/FundInitiativeTransaction.php', 'FundInitiativeTransactionQuery' => 'applications/fund/query/FundInitiativeTransactionQuery.php', @@ -3579,6 +3580,7 @@ 'FundInitiativePHIDType' => 'PhabricatorPHIDType', 'FundInitiativeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'FundInitiativeRemarkupRule' => 'PhabricatorObjectRemarkupRule', + 'FundInitiativeReplyHandler' => 'PhabricatorMailReplyHandler', 'FundInitiativeSearchEngine' => 'PhabricatorApplicationSearchEngine', 'FundInitiativeTransaction' => 'PhabricatorApplicationTransaction', 'FundInitiativeTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 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 @@ -231,5 +231,52 @@ 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 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; + } + + } diff --git a/src/applications/fund/mail/FundInitiativeReplyHandler.php b/src/applications/fund/mail/FundInitiativeReplyHandler.php new file mode 100644 --- /dev/null +++ b/src/applications/fund/mail/FundInitiativeReplyHandler.php @@ -0,0 +1,38 @@ +getDefaultPrivateReplyHandlerEmailAddress($handle, 'I'); + } + + public function getPublicReplyHandlerEmailAddress() { + return $this->getDefaultPublicReplyHandlerEmailAddress('I'); + } + + public function getReplyHandlerDomain() { + return PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain'); + } + + public function getReplyHandlerInstructions() { + if ($this->supportsReplies()) { + // TODO: Implement. + return null; + } else { + return null; + } + } + + protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) { + // TODO: Implement. + return null; + } + +} diff --git a/src/applications/fund/storage/FundInitiative.php b/src/applications/fund/storage/FundInitiative.php --- a/src/applications/fund/storage/FundInitiative.php +++ b/src/applications/fund/storage/FundInitiative.php @@ -20,6 +20,7 @@ protected $editPolicy; protected $status; protected $totalAsCurrency; + protected $mailKey; private $projectPHIDs = self::ATTACHABLE; @@ -59,6 +60,7 @@ 'status' => 'text32', 'merchantPHID' => 'phid?', 'totalAsCurrency' => 'text64', + 'mailKey' => 'bytes20', ), self::CONFIG_APPLICATION_SERIALIZERS => array( 'totalAsCurrency' => new PhortuneCurrencySerializer(), @@ -95,6 +97,13 @@ return ($this->getStatus() == self::STATUS_CLOSED); } + public function save() { + if (!$this->mailKey) { + $this->mailKey = Filesystem::readRandomCharacters(20); + } + return parent::save(); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 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 @@ -11,6 +11,10 @@ const TYPE_REFUND = 'fund:refund'; const TYPE_MERCHANT = 'fund:merchant'; + const MAILTAG_BACKER = 'fund.backer'; + const MAILTAG_STATUS = 'fund.status'; + const MAILTAG_OTHER = 'fund.other'; + const PROPERTY_AMOUNT = 'fund.amount'; const PROPERTY_BACKER = 'fund.backer'; @@ -172,15 +176,50 @@ } break; case FundInitiativeTransaction::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 FundInitiativeTransaction::TYPE_REFUND: + $amount = $this->getMetadataValue(self::PROPERTY_AMOUNT); + $amount = PhortuneCurrency::newFromString($amount); + + $backer_phid = $this->getMetadataValue(self::PROPERTY_BACKER); + return pht( - '%s backed %s.', + '%s refunded %s to %s for %s.', $this->renderHandleLink($author_phid), + $amount->formatForDisplay(), + $this->renderHandleLink($backer_phid), $this->renderHandleLink($object_phid)); } return parent::getTitleForFeed($story); } + public function getMailTags() { + $tags = parent::getMailTags(); + + switch ($this->getTransactionType()) { + case self::TYPE_STATUS: + $tags[] = self::MAILTAG_STATUS; + break; + case self::TYPE_BACKER: + case self::TYPE_REFUND: + $tags[] = self::MAILTAG_BACKER; + break; + default: + $tags[] = self::MAILTAG_OTHER; + break; + } + + return $tags; + } + + public function shouldHide() { $old = $this->getOldValue(); switch ($this->getTransactionType()) {