mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 00:32:42 +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',
|
||||
'PasteCreateMailReceiver' => 'applications/paste/mail/PasteCreateMailReceiver.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',
|
||||
'PhabricatorAWSConfigOptions' => 'applications/config/option/PhabricatorAWSConfigOptions.php',
|
||||
'PhabricatorAccessLog' => 'infrastructure/PhabricatorAccessLog.php',
|
||||
|
@ -2773,6 +2775,8 @@ phutil_register_library_map(array(
|
|||
'PackageModifyMail' => 'PackageMail',
|
||||
'PasteCreateMailReceiver' => 'PhabricatorMailReceiver',
|
||||
'PasteEmbedView' => 'AphrontView',
|
||||
'PasteMockMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
'PasteReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||
'Phabricator404Controller' => 'PhabricatorController',
|
||||
'PhabricatorAWSConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorAccessLogConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
|
@ -3417,8 +3421,9 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPaste' =>
|
||||
array(
|
||||
0 => 'PhabricatorPasteDAO',
|
||||
1 => 'PhabricatorTokenReceiverInterface',
|
||||
2 => 'PhabricatorPolicyInterface',
|
||||
1 => 'PhabricatorSubscribableInterface',
|
||||
2 => 'PhabricatorTokenReceiverInterface',
|
||||
3 => 'PhabricatorPolicyInterface',
|
||||
),
|
||||
'PhabricatorPasteCommentController' => 'PhabricatorPasteController',
|
||||
'PhabricatorPasteConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
|
|
|
@ -20,7 +20,12 @@ final class PhabricatorPasteConfigOptions
|
|||
'metamta.paste.public-create-email',
|
||||
'string',
|
||||
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() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getMailSubjectPrefix() {
|
||||
return PhabricatorEnv::getEnvConfig('metamta.paste.subject-prefix');
|
||||
}
|
||||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
|
@ -111,8 +115,18 @@ final class PhabricatorPasteEditor
|
|||
);
|
||||
}
|
||||
|
||||
protected function getMailCC(PhabricatorLiskDAO $object) {
|
||||
return array();
|
||||
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
|
||||
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() {
|
||||
|
|
|
@ -66,6 +66,8 @@ final class PasteCreateMailReceiver
|
|||
|
||||
$mail->setRelatedPHID($paste->getPHID());
|
||||
|
||||
$subject_prefix =
|
||||
PhabricatorEnv::getEnvConfig('metamta.paste.subject-prefix');
|
||||
$subject = pht('You successfully created a paste.');
|
||||
$paste_uri = PhabricatorEnv::getProductionURI($paste->getURI());
|
||||
$body = new PhabricatorMetaMTAMailBody();
|
||||
|
@ -74,7 +76,8 @@ final class PasteCreateMailReceiver
|
|||
|
||||
id(new PhabricatorMetaMTAMail())
|
||||
->addTos(array($sender->getPHID()))
|
||||
->setSubject('[Paste] '.$subject)
|
||||
->setSubject($subject)
|
||||
->setSubjectPrefix($subject_prefix)
|
||||
->setFrom($sender->getPHID())
|
||||
->setRelatedPHID($paste->getPHID())
|
||||
->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
|
||||
*/
|
||||
final class PhabricatorPaste extends PhabricatorPasteDAO
|
||||
implements PhabricatorTokenReceiverInterface, PhabricatorPolicyInterface {
|
||||
implements
|
||||
PhabricatorSubscribableInterface,
|
||||
PhabricatorTokenReceiverInterface,
|
||||
PhabricatorPolicyInterface {
|
||||
|
||||
protected $phid;
|
||||
protected $title;
|
||||
|
@ -13,6 +16,7 @@ final class PhabricatorPaste extends PhabricatorPasteDAO
|
|||
protected $language;
|
||||
protected $parentPHID;
|
||||
protected $viewPolicy;
|
||||
protected $mailKey;
|
||||
|
||||
private $content;
|
||||
private $rawContent;
|
||||
|
@ -32,6 +36,13 @@ final class PhabricatorPaste extends PhabricatorPasteDAO
|
|||
PhabricatorPastePHIDTypePaste::TYPECONST);
|
||||
}
|
||||
|
||||
public function save() {
|
||||
if (!$this->getMailKey()) {
|
||||
$this->setMailKey(Filesystem::readRandomCharacters(20));
|
||||
}
|
||||
return parent::save();
|
||||
}
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
|
@ -82,6 +93,14 @@ final class PhabricatorPaste extends PhabricatorPasteDAO
|
|||
return $this;
|
||||
}
|
||||
|
||||
/* -( PhabricatorSubscribableInterface Implementation )-------------------- */
|
||||
|
||||
|
||||
public function isAutomaticallySubscribed($phid) {
|
||||
return ($this->authorPHID == $phid);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
|
||||
|
||||
public function getUsersToNotifyOfTokenGiven() {
|
||||
|
|
|
@ -1507,6 +1507,18 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
|||
'type' => '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