mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 18:22:41 +01:00
Paste - add support for email replies and subscribers
Summary: Email replies and subscribers seem to go hand in hand so deploy both at once. Test Plan: played around with bin/mail. Verified replies posted comments on the paste. Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Maniphest Tasks: T3650 Differential Revision: https://secure.phabricator.com/D6682
This commit is contained in:
parent
42af0d66d9
commit
0e6b5073cd
11 changed files with 242 additions and 8 deletions
15
resources/sql/patches/20130805.pasteedges.sql
Normal file
15
resources/sql/patches/20130805.pasteedges.sql
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_pastebin.edge (
|
||||||
|
src VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||||
|
type VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||||
|
dst VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||||
|
dateCreated INT UNSIGNED NOT NULL,
|
||||||
|
seq INT UNSIGNED NOT NULL,
|
||||||
|
dataID INT UNSIGNED,
|
||||||
|
PRIMARY KEY (src, type, dst),
|
||||||
|
KEY (src, type, dateCreated, seq)
|
||||||
|
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
CREATE TABLE {$NAMESPACE}_pastebin.edgedata (
|
||||||
|
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
data LONGTEXT NOT NULL COLLATE utf8_bin
|
||||||
|
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
2
resources/sql/patches/20130805.pastemailkey.sql
Normal file
2
resources/sql/patches/20130805.pastemailkey.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_pastebin.pastebin_paste
|
||||||
|
ADD COLUMN `mailKey` varchar(20) NOT NULL;
|
27
resources/sql/patches/20130805.pastemailkeypop.php
Normal file
27
resources/sql/patches/20130805.pastemailkeypop.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo "Populating pastes with mail keys...\n";
|
||||||
|
|
||||||
|
$table = new PhabricatorPaste();
|
||||||
|
$table->openTransaction();
|
||||||
|
$conn_w = $table->establishConnection('w');
|
||||||
|
|
||||||
|
foreach (new LiskMigrationIterator($table) as $paste) {
|
||||||
|
$id = $paste->getID();
|
||||||
|
|
||||||
|
echo "P{$id}: ";
|
||||||
|
if (!$paste->getMailKey()) {
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'UPDATE %T SET mailKey = %s WHERE id = %d',
|
||||||
|
$paste->getTableName(),
|
||||||
|
Filesystem::readRandomCharacters(20),
|
||||||
|
$id);
|
||||||
|
echo("Generated Key\n");
|
||||||
|
} else {
|
||||||
|
echo "-\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$table->saveTransaction();
|
||||||
|
echo "Done.\n";
|
|
@ -759,6 +759,8 @@ phutil_register_library_map(array(
|
||||||
'PackageModifyMail' => 'applications/owners/mail/PackageModifyMail.php',
|
'PackageModifyMail' => 'applications/owners/mail/PackageModifyMail.php',
|
||||||
'PasteCreateMailReceiver' => 'applications/paste/mail/PasteCreateMailReceiver.php',
|
'PasteCreateMailReceiver' => 'applications/paste/mail/PasteCreateMailReceiver.php',
|
||||||
'PasteEmbedView' => 'applications/paste/view/PasteEmbedView.php',
|
'PasteEmbedView' => 'applications/paste/view/PasteEmbedView.php',
|
||||||
|
'PasteMockMailReceiver' => 'applications/paste/mail/PasteMockMailReceiver.php',
|
||||||
|
'PasteReplyHandler' => 'applications/paste/mail/PasteReplyHandler.php',
|
||||||
'Phabricator404Controller' => 'applications/base/controller/Phabricator404Controller.php',
|
'Phabricator404Controller' => 'applications/base/controller/Phabricator404Controller.php',
|
||||||
'PhabricatorAWSConfigOptions' => 'applications/config/option/PhabricatorAWSConfigOptions.php',
|
'PhabricatorAWSConfigOptions' => 'applications/config/option/PhabricatorAWSConfigOptions.php',
|
||||||
'PhabricatorAccessLog' => 'infrastructure/PhabricatorAccessLog.php',
|
'PhabricatorAccessLog' => 'infrastructure/PhabricatorAccessLog.php',
|
||||||
|
@ -2773,6 +2775,8 @@ phutil_register_library_map(array(
|
||||||
'PackageModifyMail' => 'PackageMail',
|
'PackageModifyMail' => 'PackageMail',
|
||||||
'PasteCreateMailReceiver' => 'PhabricatorMailReceiver',
|
'PasteCreateMailReceiver' => 'PhabricatorMailReceiver',
|
||||||
'PasteEmbedView' => 'AphrontView',
|
'PasteEmbedView' => 'AphrontView',
|
||||||
|
'PasteMockMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||||
|
'PasteReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||||
'Phabricator404Controller' => 'PhabricatorController',
|
'Phabricator404Controller' => 'PhabricatorController',
|
||||||
'PhabricatorAWSConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorAWSConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
'PhabricatorAccessLogConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorAccessLogConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
|
@ -3417,8 +3421,9 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPaste' =>
|
'PhabricatorPaste' =>
|
||||||
array(
|
array(
|
||||||
0 => 'PhabricatorPasteDAO',
|
0 => 'PhabricatorPasteDAO',
|
||||||
1 => 'PhabricatorTokenReceiverInterface',
|
1 => 'PhabricatorSubscribableInterface',
|
||||||
2 => 'PhabricatorPolicyInterface',
|
2 => 'PhabricatorTokenReceiverInterface',
|
||||||
|
3 => 'PhabricatorPolicyInterface',
|
||||||
),
|
),
|
||||||
'PhabricatorPasteCommentController' => 'PhabricatorPasteController',
|
'PhabricatorPasteCommentController' => 'PhabricatorPasteController',
|
||||||
'PhabricatorPasteConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorPasteConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
|
|
|
@ -20,7 +20,12 @@ final class PhabricatorPasteConfigOptions
|
||||||
'metamta.paste.public-create-email',
|
'metamta.paste.public-create-email',
|
||||||
'string',
|
'string',
|
||||||
null)
|
null)
|
||||||
->setDescription(pht('Allow creating pastes via email.'))
|
->setDescription(pht('Allow creating pastes via email.')),
|
||||||
|
$this->newOption(
|
||||||
|
'metamta.paste.subject-prefix',
|
||||||
|
'string',
|
||||||
|
'[Paste]')
|
||||||
|
->setDescription(pht('Subject prefix for paste email.'))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,11 @@ final class PhabricatorPasteEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supportsMail() {
|
protected function supportsMail() {
|
||||||
return false;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getMailSubjectPrefix() {
|
||||||
|
return PhabricatorEnv::getEnvConfig('metamta.paste.subject-prefix');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||||
|
@ -111,8 +115,18 @@ final class PhabricatorPasteEditor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getMailCC(PhabricatorLiskDAO $object) {
|
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
|
||||||
return array();
|
return id(new PasteReplyHandler())
|
||||||
|
->setMailReceiver($object);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||||
|
$id = $object->getID();
|
||||||
|
$name = $object->getTitle();
|
||||||
|
|
||||||
|
return id(new PhabricatorMetaMTAMail())
|
||||||
|
->setSubject("P{$id}: {$name}")
|
||||||
|
->addHeader('Thread-Topic', "P{$id}");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supportsFeed() {
|
protected function supportsFeed() {
|
||||||
|
|
|
@ -66,6 +66,8 @@ final class PasteCreateMailReceiver
|
||||||
|
|
||||||
$mail->setRelatedPHID($paste->getPHID());
|
$mail->setRelatedPHID($paste->getPHID());
|
||||||
|
|
||||||
|
$subject_prefix =
|
||||||
|
PhabricatorEnv::getEnvConfig('metamta.paste.subject-prefix');
|
||||||
$subject = pht('You successfully created a paste.');
|
$subject = pht('You successfully created a paste.');
|
||||||
$paste_uri = PhabricatorEnv::getProductionURI($paste->getURI());
|
$paste_uri = PhabricatorEnv::getProductionURI($paste->getURI());
|
||||||
$body = new PhabricatorMetaMTAMailBody();
|
$body = new PhabricatorMetaMTAMailBody();
|
||||||
|
@ -74,7 +76,8 @@ final class PasteCreateMailReceiver
|
||||||
|
|
||||||
id(new PhabricatorMetaMTAMail())
|
id(new PhabricatorMetaMTAMail())
|
||||||
->addTos(array($sender->getPHID()))
|
->addTos(array($sender->getPHID()))
|
||||||
->setSubject('[Paste] '.$subject)
|
->setSubject($subject)
|
||||||
|
->setSubjectPrefix($subject_prefix)
|
||||||
->setFrom($sender->getPHID())
|
->setFrom($sender->getPHID())
|
||||||
->setRelatedPHID($paste->getPHID())
|
->setRelatedPHID($paste->getPHID())
|
||||||
->setBody($body->render())
|
->setBody($body->render())
|
||||||
|
|
40
src/applications/paste/mail/PasteMockMailReceiver.php
Normal file
40
src/applications/paste/mail/PasteMockMailReceiver.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group paste
|
||||||
|
*/
|
||||||
|
final class PasteMockMailReceiver extends PhabricatorObjectMailReceiver {
|
||||||
|
|
||||||
|
public function isEnabled() {
|
||||||
|
$app_class = 'PhabricatorApplicationPaste';
|
||||||
|
return PhabricatorApplication::isClassInstalled($app_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getObjectPattern() {
|
||||||
|
return 'P[1-9]\d*';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadObject($pattern, PhabricatorUser $viewer) {
|
||||||
|
$id = (int)trim($pattern, 'P');
|
||||||
|
|
||||||
|
return id(new PhabricatorPasteQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->executeOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processReceivedObjectMail(
|
||||||
|
PhabricatorMetaMTAReceivedMail $mail,
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
PhabricatorUser $sender) {
|
||||||
|
|
||||||
|
$handler = id(new PasteReplyHandler())
|
||||||
|
->setMailReceiver($object);
|
||||||
|
|
||||||
|
$handler->setActor($sender);
|
||||||
|
$handler->setExcludeMailRecipientPHIDs(
|
||||||
|
$mail->loadExcludeMailRecipientPHIDs());
|
||||||
|
$handler->processEmail($mail);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
92
src/applications/paste/mail/PasteReplyHandler.php
Normal file
92
src/applications/paste/mail/PasteReplyHandler.php
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group paste
|
||||||
|
*/
|
||||||
|
final class PasteReplyHandler extends PhabricatorMailReplyHandler {
|
||||||
|
|
||||||
|
public function validateMailReceiver($mail_receiver) {
|
||||||
|
if (!($mail_receiver instanceof PhabricatorPaste)) {
|
||||||
|
throw new Exception('Mail receiver is not a PhabricatorPaste.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrivateReplyHandlerEmailAddress(
|
||||||
|
PhabricatorObjectHandle $handle) {
|
||||||
|
return $this->getDefaultPrivateReplyHandlerEmailAddress($handle, 'P');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPublicReplyHandlerEmailAddress() {
|
||||||
|
return $this->getDefaultPublicReplyHandlerEmailAddress('P');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReplyHandlerInstructions() {
|
||||||
|
if ($this->supportsReplies()) {
|
||||||
|
return pht('Reply to comment or !unsubscribe.');
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) {
|
||||||
|
$actor = $this->getActor();
|
||||||
|
$paste = $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 PhabricatorPasteTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
|
||||||
|
->setNewValue(array('-' => array($actor->getPHID())));
|
||||||
|
$xactions[] = $xaction;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$xactions[] = id(new PhabricatorPasteTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
||||||
|
->attachComment(
|
||||||
|
id(new PhabricatorPasteTransactionComment())
|
||||||
|
->setContent($body));
|
||||||
|
|
||||||
|
$editor = id(new PhabricatorPasteEditor())
|
||||||
|
->setActor($actor)
|
||||||
|
->setContentSource($content_source)
|
||||||
|
->setContinueOnNoEffect(true)
|
||||||
|
->setIsPreview(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$xactions = $editor->applyTransactions($paste, $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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,7 +4,10 @@
|
||||||
* @group paste
|
* @group paste
|
||||||
*/
|
*/
|
||||||
final class PhabricatorPaste extends PhabricatorPasteDAO
|
final class PhabricatorPaste extends PhabricatorPasteDAO
|
||||||
implements PhabricatorTokenReceiverInterface, PhabricatorPolicyInterface {
|
implements
|
||||||
|
PhabricatorSubscribableInterface,
|
||||||
|
PhabricatorTokenReceiverInterface,
|
||||||
|
PhabricatorPolicyInterface {
|
||||||
|
|
||||||
protected $phid;
|
protected $phid;
|
||||||
protected $title;
|
protected $title;
|
||||||
|
@ -13,6 +16,7 @@ final class PhabricatorPaste extends PhabricatorPasteDAO
|
||||||
protected $language;
|
protected $language;
|
||||||
protected $parentPHID;
|
protected $parentPHID;
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
|
protected $mailKey;
|
||||||
|
|
||||||
private $content;
|
private $content;
|
||||||
private $rawContent;
|
private $rawContent;
|
||||||
|
@ -32,6 +36,13 @@ final class PhabricatorPaste extends PhabricatorPasteDAO
|
||||||
PhabricatorPastePHIDTypePaste::TYPECONST);
|
PhabricatorPastePHIDTypePaste::TYPECONST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function save() {
|
||||||
|
if (!$this->getMailKey()) {
|
||||||
|
$this->setMailKey(Filesystem::readRandomCharacters(20));
|
||||||
|
}
|
||||||
|
return parent::save();
|
||||||
|
}
|
||||||
|
|
||||||
public function getCapabilities() {
|
public function getCapabilities() {
|
||||||
return array(
|
return array(
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
@ -82,6 +93,14 @@ final class PhabricatorPaste extends PhabricatorPasteDAO
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -( PhabricatorSubscribableInterface Implementation )-------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function isAutomaticallySubscribed($phid) {
|
||||||
|
return ($this->authorPHID == $phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
|
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
|
||||||
|
|
||||||
public function getUsersToNotifyOfTokenGiven() {
|
public function getUsersToNotifyOfTokenGiven() {
|
||||||
|
|
|
@ -1507,6 +1507,18 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
||||||
'type' => 'php',
|
'type' => 'php',
|
||||||
'name' => $this->getPatchPath('20130801.pastexactions.php'),
|
'name' => $this->getPatchPath('20130801.pastexactions.php'),
|
||||||
),
|
),
|
||||||
|
'20130805.pastemailkey.sql' => array(
|
||||||
|
'type' => 'sql',
|
||||||
|
'name' => $this->getPatchPath('20130805.pastemailkey.sql'),
|
||||||
|
),
|
||||||
|
'20130805.pasteedges.sql' => array(
|
||||||
|
'type' => 'sql',
|
||||||
|
'name' => $this->getPatchPath('20130805.pasteedges.sql'),
|
||||||
|
),
|
||||||
|
'20130805.pastemailkeypop.php' => array(
|
||||||
|
'type' => 'php',
|
||||||
|
'name' => $this->getPatchPath('20130805.pastemailkeypop.php'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue