mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 23:02:42 +01:00
Legalpad V0.2 - add mail integration
Summary: Supports !unsubscribe and commenting on replies. Subscribers get mailed something reasonable. Fixes T3480. Sneaks in /LX/ support. In the near future I want to have that /LX/ be a clean "signature" page sans all the edit actions and other fluff... Will resolve this as part of T3481. Test Plan: used the metamta console to add comments and unsubscribe. added a phlog() inside mail code to verify mail bodies looked okay. Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Maniphest Tasks: T3480 Differential Revision: https://secure.phabricator.com/D6369
This commit is contained in:
parent
ffbad23994
commit
2c03cd931b
12 changed files with 272 additions and 2 deletions
25
resources/sql/patches/legalpad-mailkey-populate.php
Normal file
25
resources/sql/patches/legalpad-mailkey-populate.php
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo "Populating Legalpad Documents with mail keys...\n";
|
||||||
|
$table = new LegalpadDocument();
|
||||||
|
$table->openTransaction();
|
||||||
|
|
||||||
|
foreach (new LiskMigrationIterator($table) as $document) {
|
||||||
|
$id = $document->getID();
|
||||||
|
|
||||||
|
echo "Document {$id}: ";
|
||||||
|
if (!$document->getMailKey()) {
|
||||||
|
queryfx(
|
||||||
|
$document->establishConnection('w'),
|
||||||
|
'UPDATE %T SET mailKey = %s WHERE id = %d',
|
||||||
|
$document->getTableName(),
|
||||||
|
Filesystem::readRandomCharacters(20),
|
||||||
|
$id);
|
||||||
|
echo "Generated Key\n";
|
||||||
|
} else {
|
||||||
|
echo "-\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$table->saveTransaction();
|
||||||
|
echo "Done.\n";
|
2
resources/sql/patches/legalpad-mailkey.sql
Normal file
2
resources/sql/patches/legalpad-mailkey.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE `{$NAMESPACE}_legalpad`.legalpad_document
|
||||||
|
ADD mailKey VARCHAR(20) NOT NULL COLLATE utf8_bin;
|
|
@ -644,6 +644,8 @@ phutil_register_library_map(array(
|
||||||
'LegalpadDocumentSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSearchEngine.php',
|
'LegalpadDocumentSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSearchEngine.php',
|
||||||
'LegalpadDocumentSignature' => 'applications/legalpad/storage/LegalpadDocumentSignature.php',
|
'LegalpadDocumentSignature' => 'applications/legalpad/storage/LegalpadDocumentSignature.php',
|
||||||
'LegalpadDocumentViewController' => 'applications/legalpad/controller/LegalpadDocumentViewController.php',
|
'LegalpadDocumentViewController' => 'applications/legalpad/controller/LegalpadDocumentViewController.php',
|
||||||
|
'LegalpadMockMailReceiver' => 'applications/legalpad/mail/LegalpadMockMailReceiver.php',
|
||||||
|
'LegalpadReplyHandler' => 'applications/legalpad/mail/LegalpadReplyHandler.php',
|
||||||
'LegalpadTransaction' => 'applications/legalpad/storage/LegalpadTransaction.php',
|
'LegalpadTransaction' => 'applications/legalpad/storage/LegalpadTransaction.php',
|
||||||
'LegalpadTransactionComment' => 'applications/legalpad/storage/LegalpadTransactionComment.php',
|
'LegalpadTransactionComment' => 'applications/legalpad/storage/LegalpadTransactionComment.php',
|
||||||
'LegalpadTransactionQuery' => 'applications/legalpad/query/LegalpadTransactionQuery.php',
|
'LegalpadTransactionQuery' => 'applications/legalpad/query/LegalpadTransactionQuery.php',
|
||||||
|
@ -1169,6 +1171,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/PhabricatorJavelinLinter.php',
|
'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/PhabricatorJavelinLinter.php',
|
||||||
'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php',
|
'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php',
|
||||||
'PhabricatorKeyValueDatabaseCache' => 'applications/cache/PhabricatorKeyValueDatabaseCache.php',
|
'PhabricatorKeyValueDatabaseCache' => 'applications/cache/PhabricatorKeyValueDatabaseCache.php',
|
||||||
|
'PhabricatorLegalpadConfigOptions' => 'applications/legalpad/config/PhabricatorLegalpadConfigOptions.php',
|
||||||
'PhabricatorLintEngine' => 'infrastructure/lint/PhabricatorLintEngine.php',
|
'PhabricatorLintEngine' => 'infrastructure/lint/PhabricatorLintEngine.php',
|
||||||
'PhabricatorLipsumArtist' => 'applications/lipsum/image/PhabricatorLipsumArtist.php',
|
'PhabricatorLipsumArtist' => 'applications/lipsum/image/PhabricatorLipsumArtist.php',
|
||||||
'PhabricatorLipsumGenerateWorkflow' => 'applications/lipsum/management/PhabricatorLipsumGenerateWorkflow.php',
|
'PhabricatorLipsumGenerateWorkflow' => 'applications/lipsum/management/PhabricatorLipsumGenerateWorkflow.php',
|
||||||
|
@ -2572,6 +2575,8 @@ phutil_register_library_map(array(
|
||||||
'LegalpadDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
'LegalpadDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
'LegalpadDocumentSignature' => 'LegalpadDAO',
|
'LegalpadDocumentSignature' => 'LegalpadDAO',
|
||||||
'LegalpadDocumentViewController' => 'LegalpadController',
|
'LegalpadDocumentViewController' => 'LegalpadController',
|
||||||
|
'LegalpadMockMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||||
|
'LegalpadReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||||
'LegalpadTransaction' => 'PhabricatorApplicationTransaction',
|
'LegalpadTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
'LegalpadTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
'LegalpadTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||||
'LegalpadTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
'LegalpadTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
|
@ -3120,6 +3125,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorInlineSummaryView' => 'AphrontView',
|
'PhabricatorInlineSummaryView' => 'AphrontView',
|
||||||
'PhabricatorJavelinLinter' => 'ArcanistLinter',
|
'PhabricatorJavelinLinter' => 'ArcanistLinter',
|
||||||
'PhabricatorKeyValueDatabaseCache' => 'PhutilKeyValueCache',
|
'PhabricatorKeyValueDatabaseCache' => 'PhutilKeyValueCache',
|
||||||
|
'PhabricatorLegalpadConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
'PhabricatorLintEngine' => 'PhutilLintEngine',
|
'PhabricatorLintEngine' => 'PhutilLintEngine',
|
||||||
'PhabricatorLipsumGenerateWorkflow' => 'PhabricatorLipsumManagementWorkflow',
|
'PhabricatorLipsumGenerateWorkflow' => 'PhabricatorLipsumManagementWorkflow',
|
||||||
'PhabricatorLipsumManagementWorkflow' => 'PhutilArgumentWorkflow',
|
'PhabricatorLipsumManagementWorkflow' => 'PhutilArgumentWorkflow',
|
||||||
|
|
|
@ -40,6 +40,7 @@ final class PhabricatorApplicationLegalpad extends PhabricatorApplication {
|
||||||
|
|
||||||
public function getRoutes() {
|
public function getRoutes() {
|
||||||
return array(
|
return array(
|
||||||
|
'/L(?P<id>\d+)/' => 'LegalpadDocumentViewController',
|
||||||
'/legalpad/' => array(
|
'/legalpad/' => array(
|
||||||
'' => 'LegalpadDocumentListController',
|
'' => 'LegalpadDocumentListController',
|
||||||
'(query/(?P<queryKey>[^/]+)/)?' => 'LegalpadDocumentListController',
|
'(query/(?P<queryKey>[^/]+)/)?' => 'LegalpadDocumentListController',
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group legalpad
|
||||||
|
*/
|
||||||
|
final class PhabricatorLegalpadConfigOptions
|
||||||
|
extends PhabricatorApplicationConfigOptions {
|
||||||
|
|
||||||
|
public function getName() {
|
||||||
|
return pht('Legalpad');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription() {
|
||||||
|
return pht('Configure Legalpad.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOptions() {
|
||||||
|
return array(
|
||||||
|
$this->newOption(
|
||||||
|
'metamta.legalpad.subject-prefix',
|
||||||
|
'string',
|
||||||
|
'[Legalpad]')
|
||||||
|
->setDescription(pht('Subject prefix for Legalpad email.'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ final class LegalpadDocumentCommentController extends LegalpadController {
|
||||||
$document = id(new LegalpadDocumentQuery())
|
$document = id(new LegalpadDocumentQuery())
|
||||||
->setViewer($user)
|
->setViewer($user)
|
||||||
->withIDs(array($this->id))
|
->withIDs(array($this->id))
|
||||||
|
->needDocumentBodies(true)
|
||||||
->executeOne();
|
->executeOne();
|
||||||
|
|
||||||
if (!$document) {
|
if (!$document) {
|
||||||
|
|
|
@ -111,10 +111,65 @@ final class LegalpadDocumentEditor
|
||||||
return parent::mergeTransactions($u, $v);
|
return parent::mergeTransactions($u, $v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -( Sending Mail )------------------------------------------------------- */
|
||||||
|
|
||||||
protected function supportsMail() {
|
protected function supportsMail() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
|
||||||
|
return id(new LegalpadReplyHandler())
|
||||||
|
->setMailReceiver($object);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||||
|
$id = $object->getID();
|
||||||
|
$phid = $object->getPHID();
|
||||||
|
$title = $object->getDocumentBody()->getTitle();
|
||||||
|
|
||||||
|
return id(new PhabricatorMetaMTAMail())
|
||||||
|
->setSubject("L{$id}: {$title}")
|
||||||
|
->addHeader('Thread-Topic', "L{$id}: {$phid}");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||||
|
return array(
|
||||||
|
$object->getCreatorPHID(),
|
||||||
|
$this->requireActor()->getPHID(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function shouldImplyCC(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
|
switch ($xaction->getTransactionType()) {
|
||||||
|
case LegalpadTransactionType::TYPE_TEXT:
|
||||||
|
case LegalpadTransactionType::TYPE_TITLE:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::shouldImplyCC($object, $xaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildMailBody(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
array $xactions) {
|
||||||
|
|
||||||
|
$body = parent::buildMailBody($object, $xactions);
|
||||||
|
|
||||||
|
$body->addTextSection(
|
||||||
|
pht('DOCUMENT DETAIL'),
|
||||||
|
PhabricatorEnv::getProductionURI('/L'.$object->getID()));
|
||||||
|
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getMailSubjectPrefix() {
|
||||||
|
return PhabricatorEnv::getEnvConfig('metamta.legalpad.subject-prefix');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function supportsFeed() {
|
protected function supportsFeed() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
38
src/applications/legalpad/mail/LegalpadMockMailReceiver.php
Normal file
38
src/applications/legalpad/mail/LegalpadMockMailReceiver.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class LegalpadMockMailReceiver extends PhabricatorObjectMailReceiver {
|
||||||
|
|
||||||
|
public function isEnabled() {
|
||||||
|
$app_class = 'PhabricatorApplicationLegalpad';
|
||||||
|
return PhabricatorApplication::isClassInstalled($app_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getObjectPattern() {
|
||||||
|
return 'L[1-9]\d*';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadObject($pattern, PhabricatorUser $viewer) {
|
||||||
|
$id = (int)trim($pattern, 'L');
|
||||||
|
|
||||||
|
return id(new LegalpadDocumentQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->needDocumentBodies(true)
|
||||||
|
->executeOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processReceivedObjectMail(
|
||||||
|
PhabricatorMetaMTAReceivedMail $mail,
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
PhabricatorUser $sender) {
|
||||||
|
|
||||||
|
$handler = id(new LegalpadReplyHandler())
|
||||||
|
->setMailReceiver($object)
|
||||||
|
->setActor($sender)
|
||||||
|
->setExcludeMailRecipientPHIDs(
|
||||||
|
$mail->loadExcludeMailRecipientPHIDs());
|
||||||
|
|
||||||
|
return $handler->processEmail($mail);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
99
src/applications/legalpad/mail/LegalpadReplyHandler.php
Normal file
99
src/applications/legalpad/mail/LegalpadReplyHandler.php
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group legalpad
|
||||||
|
*/
|
||||||
|
final class LegalpadReplyHandler extends PhabricatorMailReplyHandler {
|
||||||
|
|
||||||
|
public function validateMailReceiver($mail_receiver) {
|
||||||
|
if (!($mail_receiver instanceof LegalpadDocument)) {
|
||||||
|
throw new Exception("Mail receiver is not a LegalpadDocument!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrivateReplyHandlerEmailAddress(
|
||||||
|
PhabricatorObjectHandle $handle) {
|
||||||
|
return $this->getDefaultPrivateReplyHandlerEmailAddress($handle, 'L');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPublicReplyHandlerEmailAddress() {
|
||||||
|
return $this->getDefaultPublicReplyHandlerEmailAddress('L');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReplyHandlerDomain() {
|
||||||
|
return PhabricatorEnv::getEnvConfig(
|
||||||
|
'metamta.reply-handler-domain');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReplyHandlerInstructions() {
|
||||||
|
if ($this->supportsReplies()) {
|
||||||
|
return 'Reply to comment or !unsubscribe.';
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) {
|
||||||
|
$actor = $this->getActor();
|
||||||
|
$document = $this->getMailReceiver();
|
||||||
|
|
||||||
|
$body = $mail->getCleanTextBody();
|
||||||
|
$body = trim($body);
|
||||||
|
$body = $this->enhanceBodyWithAttachments($body, $mail->getAttachments());
|
||||||
|
|
||||||
|
$content_source = PhabricatorContentSource::newForSource(
|
||||||
|
PhabricatorContentSource::SOURCE_EMAIL,
|
||||||
|
array(
|
||||||
|
'id' => $mail->getID(),
|
||||||
|
));
|
||||||
|
|
||||||
|
$lines = explode("\n", trim($body));
|
||||||
|
$first_line = head($lines);
|
||||||
|
|
||||||
|
$xactions = array();
|
||||||
|
$command = null;
|
||||||
|
$matches = null;
|
||||||
|
if (preg_match('/^!(\w+)/', $first_line, $matches)) {
|
||||||
|
$lines = array_slice($lines, 1);
|
||||||
|
$body = implode("\n", $lines);
|
||||||
|
$body = trim($body);
|
||||||
|
|
||||||
|
$command = $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($command) {
|
||||||
|
case 'unsubscribe':
|
||||||
|
$xaction = id(new LegalpadTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
|
||||||
|
->setNewValue(array('-' => array($actor->getPHID())));
|
||||||
|
$xactions[] = $xaction;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$xactions[] = id(new LegalpadTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
||||||
|
->attachComment(
|
||||||
|
id(new LegalpadTransactionComment())
|
||||||
|
->setDocumentID($document->getID())
|
||||||
|
->setLineNumber(0)
|
||||||
|
->setLineLength(0)
|
||||||
|
->setContent($body));
|
||||||
|
|
||||||
|
$editor = id(new LegalpadDocumentEditor())
|
||||||
|
->setActor($actor)
|
||||||
|
->setContentSource($content_source)
|
||||||
|
->setContinueOnNoEffect(true)
|
||||||
|
->setIsPreview(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$xactions = $editor->applyTransactions($document, $xactions);
|
||||||
|
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
|
||||||
|
// just do nothing, though unclear why you're sending a blank email
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$head_xaction = head($xactions);
|
||||||
|
return $head_xaction->getID();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ final class LegalpadDocument extends LegalpadDAO
|
||||||
protected $documentBodyPHID;
|
protected $documentBodyPHID;
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
protected $editPolicy;
|
protected $editPolicy;
|
||||||
|
protected $mailKey;
|
||||||
|
|
||||||
private $documentBody;
|
private $documentBody;
|
||||||
private $contributors;
|
private $contributors;
|
||||||
|
@ -58,6 +59,13 @@ final class LegalpadDocument extends LegalpadDAO
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function save() {
|
||||||
|
if (!$this->getMailKey()) {
|
||||||
|
$this->setMailKey(Filesystem::readRandomCharacters(20));
|
||||||
|
}
|
||||||
|
return parent::save();
|
||||||
|
}
|
||||||
|
|
||||||
/* -( PhabricatorSubscribableInterface Implementation )-------------------- */
|
/* -( PhabricatorSubscribableInterface Implementation )-------------------- */
|
||||||
|
|
||||||
public function isAutomaticallySubscribed($phid) {
|
public function isAutomaticallySubscribed($phid) {
|
||||||
|
|
|
@ -213,7 +213,7 @@ final class PhabricatorObjectHandleData {
|
||||||
|
|
||||||
case PhabricatorPHIDConstants::PHID_TYPE_LEGD:
|
case PhabricatorPHIDConstants::PHID_TYPE_LEGD:
|
||||||
$legds = id(new LegalpadDocumentQuery())
|
$legds = id(new LegalpadDocumentQuery())
|
||||||
->needDocumentBody(true)
|
->needDocumentBodies(true)
|
||||||
->withPHIDs($phids)
|
->withPHIDs($phids)
|
||||||
->setViewer($this->viewer)
|
->setViewer($this->viewer)
|
||||||
->execute();
|
->execute();
|
||||||
|
|
|
@ -1414,6 +1414,14 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
||||||
'type' => 'sql',
|
'type' => 'sql',
|
||||||
'name' => $this->getPatchPath('20130701.conduitlog.sql'),
|
'name' => $this->getPatchPath('20130701.conduitlog.sql'),
|
||||||
),
|
),
|
||||||
|
'legalpad-mailkey.sql' => array(
|
||||||
|
'type' => 'sql',
|
||||||
|
'name' => $this->getPatchPath('legalpad-mailkey.sql'),
|
||||||
|
),
|
||||||
|
'legalpad-mailkey-populate.php' => array(
|
||||||
|
'type' => 'php',
|
||||||
|
'name' => $this->getPatchPath('legalpad-mailkey-populate.php'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue