mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-12 18:02:40 +01:00
Add basic email support to Pholio
Summary: These emails aren't yet useful, but thread/multiplex/etc correctly. Test Plan: Got some Pholio emails. Reviewers: btrahan, chad Reviewed By: btrahan CC: aran Maniphest Tasks: T2097 Differential Revision: https://secure.phabricator.com/D3842
This commit is contained in:
parent
029cfcfc19
commit
3ceaad1aa8
5 changed files with 168 additions and 13 deletions
|
@ -349,8 +349,9 @@ return array(
|
||||||
// class with an implementation of your own. This will allow you to do things
|
// class with an implementation of your own. This will allow you to do things
|
||||||
// like have a single public reply handler or change how private reply
|
// like have a single public reply handler or change how private reply
|
||||||
// handlers are generated and validated.
|
// handlers are generated and validated.
|
||||||
|
//
|
||||||
// This key should be set to a loadable subclass of
|
// This key should be set to a loadable subclass of
|
||||||
// PhabricatorMailReplyHandler (and possibly of ManiphestReplyHandler).
|
// PhabricatorMailReplyHandler.
|
||||||
'metamta.maniphest.reply-handler' => 'ManiphestReplyHandler',
|
'metamta.maniphest.reply-handler' => 'ManiphestReplyHandler',
|
||||||
|
|
||||||
// If you don't want phabricator to take up an entire domain
|
// If you don't want phabricator to take up an entire domain
|
||||||
|
@ -367,6 +368,13 @@ return array(
|
||||||
// distinguish between testing and development installs, for example.
|
// distinguish between testing and development installs, for example.
|
||||||
'metamta.maniphest.subject-prefix' => '[Maniphest]',
|
'metamta.maniphest.subject-prefix' => '[Maniphest]',
|
||||||
|
|
||||||
|
// See 'metamta.pholio.reply-handler-domain'. This does the same thing, but
|
||||||
|
// affects Pholio.
|
||||||
|
'metamta.pholio.reply-handler-domain' => null,
|
||||||
|
|
||||||
|
// Prefix prepended to mail sent by Pholio.
|
||||||
|
'metamta.pholio.subject-prefix' => '[Pholio]',
|
||||||
|
|
||||||
// See 'metamta.maniphest.reply-handler-domain'. This does the same thing,
|
// See 'metamta.maniphest.reply-handler-domain'. This does the same thing,
|
||||||
// but allows email replies via Differential.
|
// but allows email replies via Differential.
|
||||||
'metamta.differential.reply-handler-domain' => null,
|
'metamta.differential.reply-handler-domain' => null,
|
||||||
|
|
|
@ -1216,6 +1216,7 @@ phutil_register_library_map(array(
|
||||||
'PholioMockQuery' => 'applications/pholio/query/PholioMockQuery.php',
|
'PholioMockQuery' => 'applications/pholio/query/PholioMockQuery.php',
|
||||||
'PholioMockViewController' => 'applications/pholio/controller/PholioMockViewController.php',
|
'PholioMockViewController' => 'applications/pholio/controller/PholioMockViewController.php',
|
||||||
'PholioPixelComment' => 'applications/pholio/storage/PholioPixelComment.php',
|
'PholioPixelComment' => 'applications/pholio/storage/PholioPixelComment.php',
|
||||||
|
'PholioReplyHandler' => 'applications/pholio/mail/PholioReplyHandler.php',
|
||||||
'PholioTransaction' => 'applications/pholio/storage/PholioTransaction.php',
|
'PholioTransaction' => 'applications/pholio/storage/PholioTransaction.php',
|
||||||
'PholioTransactionQuery' => 'applications/pholio/query/PholioTransactionQuery.php',
|
'PholioTransactionQuery' => 'applications/pholio/query/PholioTransactionQuery.php',
|
||||||
'PholioTransactionType' => 'applications/pholio/constants/PholioTransactionType.php',
|
'PholioTransactionType' => 'applications/pholio/constants/PholioTransactionType.php',
|
||||||
|
@ -2427,6 +2428,7 @@ phutil_register_library_map(array(
|
||||||
0 => 'PholioDAO',
|
0 => 'PholioDAO',
|
||||||
1 => 'PhabricatorMarkupInterface',
|
1 => 'PhabricatorMarkupInterface',
|
||||||
),
|
),
|
||||||
|
'PholioReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||||
'PholioTransaction' =>
|
'PholioTransaction' =>
|
||||||
array(
|
array(
|
||||||
0 => 'PholioDAO',
|
0 => 'PholioDAO',
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
final class ManiphestTransactionEditor extends PhabricatorEditor {
|
final class ManiphestTransactionEditor extends PhabricatorEditor {
|
||||||
|
|
||||||
private $parentMessageID;
|
private $parentMessageID;
|
||||||
private $excludePHIDs = array();
|
|
||||||
private $auxiliaryFields = array();
|
private $auxiliaryFields = array();
|
||||||
|
|
||||||
public function setAuxiliaryFields(array $fields) {
|
public function setAuxiliaryFields(array $fields) {
|
||||||
|
@ -20,15 +19,6 @@ final class ManiphestTransactionEditor extends PhabricatorEditor {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setExcludePHIDs(array $exclude) {
|
|
||||||
$this->excludePHIDs = $exclude;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getExcludePHIDs() {
|
|
||||||
return $this->excludePHIDs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function applyTransactions(ManiphestTask $task, array $transactions) {
|
public function applyTransactions(ManiphestTask $task, array $transactions) {
|
||||||
assert_instances_of($transactions, 'ManiphestTransaction');
|
assert_instances_of($transactions, 'ManiphestTransaction');
|
||||||
|
|
||||||
|
@ -219,7 +209,6 @@ final class ManiphestTransactionEditor extends PhabricatorEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function sendEmail($task, $transactions, $email_to, $email_cc) {
|
private function sendEmail($task, $transactions, $email_to, $email_cc) {
|
||||||
$exclude = $this->getExcludePHIDs();
|
|
||||||
$email_to = array_filter(array_unique($email_to));
|
$email_to = array_filter(array_unique($email_to));
|
||||||
$email_cc = array_filter(array_unique($email_cc));
|
$email_cc = array_filter(array_unique($email_cc));
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,8 @@ final class PholioMockEditor extends PhabricatorEditor {
|
||||||
"Call setContentSource() before applyTransactions()!");
|
"Call setContentSource() before applyTransactions()!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$is_new = !$mock->getID();
|
||||||
|
|
||||||
$comments = array();
|
$comments = array();
|
||||||
foreach ($xactions as $xaction) {
|
foreach ($xactions as $xaction) {
|
||||||
if (strlen($xaction->getComment())) {
|
if (strlen($xaction->getComment())) {
|
||||||
|
@ -54,8 +56,12 @@ final class PholioMockEditor extends PhabricatorEditor {
|
||||||
|
|
||||||
$mentioned_phids = PhabricatorMarkupEngine::extractPHIDsFromMentions(
|
$mentioned_phids = PhabricatorMarkupEngine::extractPHIDsFromMentions(
|
||||||
$comments);
|
$comments);
|
||||||
|
$subscribe_phids = $mentioned_phids;
|
||||||
|
|
||||||
if ($mentioned_phids) {
|
// Attempt to subscribe the actor.
|
||||||
|
$subscribe_phids[] = $actor->getPHID();
|
||||||
|
|
||||||
|
if ($subscribe_phids) {
|
||||||
if ($mock->getID()) {
|
if ($mock->getID()) {
|
||||||
$old_subs = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
$old_subs = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
||||||
$mock->getPHID());
|
$mock->getPHID());
|
||||||
|
@ -111,11 +117,99 @@ final class PholioMockEditor extends PhabricatorEditor {
|
||||||
|
|
||||||
$mock->saveTransaction();
|
$mock->saveTransaction();
|
||||||
|
|
||||||
|
$this->sendMail($mock, $xactions, $is_new, $mentioned_phids);
|
||||||
|
|
||||||
PholioIndexer::indexMock($mock);
|
PholioIndexer::indexMock($mock);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sendMail(
|
||||||
|
PholioMock $mock,
|
||||||
|
array $xactions,
|
||||||
|
$is_new,
|
||||||
|
array $mentioned_phids) {
|
||||||
|
|
||||||
|
$subscribed_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
||||||
|
$mock->getPHID());
|
||||||
|
|
||||||
|
$email_to = array(
|
||||||
|
$mock->getAuthorPHID(),
|
||||||
|
$this->requireActor()->getPHID(),
|
||||||
|
);
|
||||||
|
$email_cc = $subscribed_phids;
|
||||||
|
|
||||||
|
$phids = array_merge($email_to, $email_cc);
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||||
|
->setViewer($this->requireActor())
|
||||||
|
->loadHandles();
|
||||||
|
|
||||||
|
$mock_id = $mock->getID();
|
||||||
|
$name = $mock->getName();
|
||||||
|
$original_name = $mock->getOriginalName();
|
||||||
|
|
||||||
|
$thread_id = 'pholio-mock-'.$mock->getPHID();
|
||||||
|
|
||||||
|
$mail_tags = $this->getMailTags($mock, $xactions);
|
||||||
|
|
||||||
|
$body = new PhabricatorMetaMTAMailBody();
|
||||||
|
$body->addRawSection('lorem ipsum');
|
||||||
|
|
||||||
|
$mock_uri = PhabricatorEnv::getProductionURI('/M'.$mock->getID());
|
||||||
|
|
||||||
|
$body->addTextSection(pht('MOCK DETAIL'), $mock_uri);
|
||||||
|
|
||||||
|
$reply_handler = $this->buildReplyHandler($mock);
|
||||||
|
|
||||||
|
$template = id(new PhabricatorMetaMTAMail())
|
||||||
|
->setSubject("M{$mock_id}: {$name}")
|
||||||
|
->setSubjectPrefix($this->getMailSubjectPrefix())
|
||||||
|
->setVarySubjectPrefix('[edit/create?]')
|
||||||
|
->setFrom($this->requireActor()->getPHID())
|
||||||
|
->addHeader('Thread-Topic', "M{$mock_id}: {$original_name}")
|
||||||
|
->setThreadID($thread_id, $is_new)
|
||||||
|
->setRelatedPHID($mock->getPHID())
|
||||||
|
->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs())
|
||||||
|
->setIsBulk(true)
|
||||||
|
->setMailTags($mail_tags)
|
||||||
|
->setBody($body->render());
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// ->setParentMessageID(...)
|
||||||
|
|
||||||
|
$mails = $reply_handler->multiplexMail(
|
||||||
|
$template,
|
||||||
|
array_select_keys($handles, $email_to),
|
||||||
|
array_select_keys($handles, $email_cc));
|
||||||
|
|
||||||
|
foreach ($mails as $mail) {
|
||||||
|
$mail->saveAndSend();
|
||||||
|
}
|
||||||
|
|
||||||
|
$template->addTos($email_to);
|
||||||
|
$template->addCCs($email_cc);
|
||||||
|
|
||||||
|
return $template;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMailTags(PholioMock $mock, array $xactions) {
|
||||||
|
assert_instances_of($xactions, 'PholioTransaction');
|
||||||
|
$tags = array();
|
||||||
|
|
||||||
|
return $tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildReplyHandler(PholioMock $mock) {
|
||||||
|
$handler_object = new PholioReplyHandler();
|
||||||
|
$handler_object->setMailReceiver($mock);
|
||||||
|
|
||||||
|
return $handler_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMailSubjectPrefix() {
|
||||||
|
return PhabricatorEnv::getEnvConfig('metamta.pholio.subject-prefix');
|
||||||
|
}
|
||||||
|
|
||||||
private function applyTransaction(
|
private function applyTransaction(
|
||||||
PholioMock $mock,
|
PholioMock $mock,
|
||||||
PholioTransaction $xaction) {
|
PholioTransaction $xaction) {
|
||||||
|
@ -155,6 +249,9 @@ final class PholioMockEditor extends PhabricatorEditor {
|
||||||
break;
|
break;
|
||||||
case PholioTransactionType::TYPE_NAME:
|
case PholioTransactionType::TYPE_NAME:
|
||||||
$mock->setName($xaction->getNewValue());
|
$mock->setName($xaction->getNewValue());
|
||||||
|
if ($mock->getOriginalName() === null) {
|
||||||
|
$mock->setOriginalName($xaction->getNewValue());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PholioTransactionType::TYPE_DESCRIPTION:
|
case PholioTransactionType::TYPE_DESCRIPTION:
|
||||||
$mock->setDescription($xaction->getNewValue());
|
$mock->setDescription($xaction->getNewValue());
|
||||||
|
|
59
src/applications/pholio/mail/PholioReplyHandler.php
Normal file
59
src/applications/pholio/mail/PholioReplyHandler.php
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group pholio
|
||||||
|
*/
|
||||||
|
final class PholioReplyHandler extends PhabricatorMailReplyHandler {
|
||||||
|
|
||||||
|
public function validateMailReceiver($mail_receiver) {
|
||||||
|
if (!($mail_receiver instanceof PholioMock)) {
|
||||||
|
throw new Exception("Mail receiver is not a PholioMock!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrivateReplyHandlerEmailAddress(
|
||||||
|
PhabricatorObjectHandle $handle) {
|
||||||
|
return $this->getDefaultPrivateReplyHandlerEmailAddress($handle, 'M');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPublicReplyHandlerEmailAddress() {
|
||||||
|
return $this->getDefaultPublicReplyHandlerEmailAddress('M');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReplyHandlerDomain() {
|
||||||
|
return PhabricatorEnv::getEnvConfig(
|
||||||
|
'metamta.pholio.reply-handler-domain');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReplyHandlerInstructions() {
|
||||||
|
if ($this->supportsReplies()) {
|
||||||
|
// TODO: Implement.
|
||||||
|
return null;
|
||||||
|
return "Reply to comment.";
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) {
|
||||||
|
// TODO: Implement this.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue