mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-28 16:30:59 +01:00
Enable email for audits
Summary: When users submit an audit, send email to relevant parties informing them. Allow email to be replied to. Just basic support so far; no "!raise" stuff and no threading with the Herald commit notification. Test Plan: Made comments, got email. Replied to email, got comments. Reviewers: btrahan, jungejason Reviewed By: btrahan CC: aran, epriestley Maniphest Tasks: T904 Differential Revision: https://secure.phabricator.com/D1698
This commit is contained in:
parent
cfbec38fbe
commit
d7a7bca85c
10 changed files with 227 additions and 4 deletions
|
@ -235,6 +235,17 @@ return array(
|
||||||
// adapter.
|
// adapter.
|
||||||
'metamta.differential.attach-patches' => false,
|
'metamta.differential.attach-patches' => false,
|
||||||
|
|
||||||
|
// Prefix prepended to mail sent by Diffusion.
|
||||||
|
'metamta.diffusion.subject-prefix' => '[Diffusion]',
|
||||||
|
|
||||||
|
// See 'metamta.maniphest.reply-handler-domain'. This does the same thing,
|
||||||
|
// but allows email replies via Diffusion.
|
||||||
|
'metamta.diffusion.reply-handler-domain' => null,
|
||||||
|
|
||||||
|
// See 'metamta.maniphest.reply-handler'. This does the same thing, but
|
||||||
|
// affects Diffusion.
|
||||||
|
'metamta.diffusion.reply-handler' => 'PhabricatorAuditReplyHandler',
|
||||||
|
|
||||||
// By default, Phabricator generates unique reply-to addresses and sends a
|
// By default, Phabricator generates unique reply-to addresses and sends a
|
||||||
// separate email to each recipient when you enable reply handling. This is
|
// separate email to each recipient when you enable reply handling. This is
|
||||||
// more secure than using "From" to establish user identity, but can mean
|
// more secure than using "From" to establish user identity, but can mean
|
||||||
|
|
|
@ -450,6 +450,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuditListController' => 'applications/audit/controller/list',
|
'PhabricatorAuditListController' => 'applications/audit/controller/list',
|
||||||
'PhabricatorAuditListView' => 'applications/audit/view/list',
|
'PhabricatorAuditListView' => 'applications/audit/view/list',
|
||||||
'PhabricatorAuditQuery' => 'applications/audit/query/audit',
|
'PhabricatorAuditQuery' => 'applications/audit/query/audit',
|
||||||
|
'PhabricatorAuditReplyHandler' => 'applications/audit/replyhandler',
|
||||||
'PhabricatorAuditStatusConstants' => 'applications/audit/constants/status',
|
'PhabricatorAuditStatusConstants' => 'applications/audit/constants/status',
|
||||||
'PhabricatorAuthController' => 'applications/auth/controller/base',
|
'PhabricatorAuthController' => 'applications/auth/controller/base',
|
||||||
'PhabricatorCalendarBrowseController' => 'applications/calendar/controller/browse',
|
'PhabricatorCalendarBrowseController' => 'applications/calendar/controller/browse',
|
||||||
|
@ -1240,6 +1241,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuditEditController' => 'PhabricatorAuditController',
|
'PhabricatorAuditEditController' => 'PhabricatorAuditController',
|
||||||
'PhabricatorAuditListController' => 'PhabricatorAuditController',
|
'PhabricatorAuditListController' => 'PhabricatorAuditController',
|
||||||
'PhabricatorAuditListView' => 'AphrontView',
|
'PhabricatorAuditListView' => 'AphrontView',
|
||||||
|
'PhabricatorAuditReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||||
'PhabricatorAuthController' => 'PhabricatorController',
|
'PhabricatorAuthController' => 'PhabricatorController',
|
||||||
'PhabricatorCalendarBrowseController' => 'PhabricatorCalendarController',
|
'PhabricatorCalendarBrowseController' => 'PhabricatorCalendarController',
|
||||||
'PhabricatorCalendarController' => 'PhabricatorController',
|
'PhabricatorCalendarController' => 'PhabricatorController',
|
||||||
|
|
|
@ -36,6 +36,10 @@ final class PhabricatorAuditCommentEditor {
|
||||||
$commit = $this->commit;
|
$commit = $this->commit;
|
||||||
$user = $this->user;
|
$user = $this->user;
|
||||||
|
|
||||||
|
$other_comments = id(new PhabricatorAuditComment())->loadAllWhere(
|
||||||
|
'targetPHID = %s',
|
||||||
|
$commit->getPHID());
|
||||||
|
|
||||||
$comment
|
$comment
|
||||||
->setActorPHID($user->getPHID())
|
->setActorPHID($user->getPHID())
|
||||||
->setTargetPHID($commit->getPHID())
|
->setTargetPHID($commit->getPHID())
|
||||||
|
@ -89,7 +93,7 @@ final class PhabricatorAuditCommentEditor {
|
||||||
|
|
||||||
$this->publishFeedStory($comment, array_keys($audit_phids));
|
$this->publishFeedStory($comment, array_keys($audit_phids));
|
||||||
PhabricatorSearchCommitIndexer::indexCommit($commit);
|
PhabricatorSearchCommitIndexer::indexCommit($commit);
|
||||||
// TODO: Email.
|
$this->sendMail($comment, $other_comments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -156,4 +160,102 @@ final class PhabricatorAuditCommentEditor {
|
||||||
->publish();
|
->publish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sendMail(
|
||||||
|
PhabricatorAuditComment $comment,
|
||||||
|
array $other_comments) {
|
||||||
|
$commit = $this->commit;
|
||||||
|
|
||||||
|
$data = $commit->loadCommitData();
|
||||||
|
$summary = $data->getSummary();
|
||||||
|
|
||||||
|
$commit_phid = $commit->getPHID();
|
||||||
|
$phids = array($commit_phid);
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
|
||||||
|
$handle = $handles[$commit_phid];
|
||||||
|
|
||||||
|
$name = $handle->getName();
|
||||||
|
|
||||||
|
$map = array(
|
||||||
|
PhabricatorAuditActionConstants::CONCERN => 'Raised Concern',
|
||||||
|
PhabricatorAuditActionConstants::ACCEPT => 'Accepted',
|
||||||
|
);
|
||||||
|
$verb = idx($map, $comment->getAction(), 'Commented On');
|
||||||
|
|
||||||
|
$prefix = PhabricatorEnv::getEnvConfig('metamta.diffusion.subject-prefix');
|
||||||
|
$subject = "{$prefix} [{$verb}] {$name}: {$summary}";
|
||||||
|
$thread_id = '<diffusion-audit-'.$commit->getPHID().'>';
|
||||||
|
$is_new = !count($other_comments);
|
||||||
|
$body = $this->renderMailBody(
|
||||||
|
$comment,
|
||||||
|
"{$name}: {$summary}",
|
||||||
|
$handle);
|
||||||
|
|
||||||
|
$email_to = array();
|
||||||
|
|
||||||
|
$author_phid = $data->getCommitDetail('authorPHID');
|
||||||
|
if ($author_phid) {
|
||||||
|
$email_to[] = $author_phid;
|
||||||
|
}
|
||||||
|
|
||||||
|
$email_cc = array();
|
||||||
|
foreach ($other_comments as $comment) {
|
||||||
|
$email_cc[] = $comment->getActorPHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
$phids = array_merge($email_to, $email_cc);
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
|
||||||
|
|
||||||
|
$template = id(new PhabricatorMetaMTAMail())
|
||||||
|
->setSubject($subject)
|
||||||
|
->setFrom($comment->getActorPHID())
|
||||||
|
->addHeader('Thread-Topic', 'Diffusion Audit '.$commit->getPHID())
|
||||||
|
->setThreadID($thread_id, $is_new)
|
||||||
|
->setRelatedPHID($commit->getPHID())
|
||||||
|
->setIsBulk(true)
|
||||||
|
->setBody($body);
|
||||||
|
|
||||||
|
$reply_handler = self::newReplyHandlerForCommit($commit);
|
||||||
|
|
||||||
|
$mails = $reply_handler->multiplexMail(
|
||||||
|
$template,
|
||||||
|
array_select_keys($handles, $email_to),
|
||||||
|
array_select_keys($handles, $email_cc));
|
||||||
|
|
||||||
|
foreach ($mails as $mail) {
|
||||||
|
$mail->saveAndSend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function newReplyHandlerForCommit($commit) {
|
||||||
|
$handler_class = PhabricatorEnv::getEnvConfig(
|
||||||
|
'metamta.diffusion.reply-handler');
|
||||||
|
$reply_handler = newv($handler_class, array());
|
||||||
|
$reply_handler->setMailReceiver($commit);
|
||||||
|
return $reply_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderMailBody(
|
||||||
|
PhabricatorAuditComment $comment,
|
||||||
|
$cname,
|
||||||
|
PhabricatorObjectHandle $handle) {
|
||||||
|
|
||||||
|
$commit = $this->commit;
|
||||||
|
$user = $this->user;
|
||||||
|
$name = $user->getUsername();
|
||||||
|
|
||||||
|
$verb = PhabricatorAuditActionConstants::getActionPastTenseVerb(
|
||||||
|
$comment->getAction());
|
||||||
|
|
||||||
|
$body = array();
|
||||||
|
$body[] = "{$name} {$verb} commit {$cname}.";
|
||||||
|
|
||||||
|
if ($comment->getContent()) {
|
||||||
|
$body[] = $comment->getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
$body[] = "COMMIT\n ".PhabricatorEnv::getProductionURI($handle->getURI());
|
||||||
|
|
||||||
|
return implode("\n\n", $body)."\n";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,15 @@ phutil_require_module('phabricator', 'applications/audit/constants/action');
|
||||||
phutil_require_module('phabricator', 'applications/feed/constants/story');
|
phutil_require_module('phabricator', 'applications/feed/constants/story');
|
||||||
phutil_require_module('phabricator', 'applications/feed/publisher');
|
phutil_require_module('phabricator', 'applications/feed/publisher');
|
||||||
phutil_require_module('phabricator', 'applications/audit/constants/status');
|
phutil_require_module('phabricator', 'applications/audit/constants/status');
|
||||||
|
phutil_require_module('phabricator', 'applications/audit/storage/auditcomment');
|
||||||
|
phutil_require_module('phabricator', 'applications/metamta/storage/mail');
|
||||||
phutil_require_module('phabricator', 'applications/owners/storage/owner');
|
phutil_require_module('phabricator', 'applications/owners/storage/owner');
|
||||||
phutil_require_module('phabricator', 'applications/owners/storage/package');
|
phutil_require_module('phabricator', 'applications/owners/storage/package');
|
||||||
phutil_require_module('phabricator', 'applications/owners/storage/packagecommitrelationship');
|
phutil_require_module('phabricator', 'applications/owners/storage/packagecommitrelationship');
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
phutil_require_module('phabricator', 'applications/project/query/project');
|
phutil_require_module('phabricator', 'applications/project/query/project');
|
||||||
phutil_require_module('phabricator', 'applications/search/index/indexer/repository');
|
phutil_require_module('phabricator', 'applications/search/index/indexer/repository');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?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 audit
|
||||||
|
*/
|
||||||
|
final class PhabricatorAuditReplyHandler extends PhabricatorMailReplyHandler {
|
||||||
|
|
||||||
|
public function validateMailReceiver($mail_receiver) {
|
||||||
|
if (!($mail_receiver instanceof PhabricatorRepositoryCommit)) {
|
||||||
|
throw new Exception("Mail receiver is not a commit!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrivateReplyHandlerEmailAddress(
|
||||||
|
PhabricatorObjectHandle $handle) {
|
||||||
|
return $this->getDefaultPrivateReplyHandlerEmailAddress($handle, 'C');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPublicReplyHandlerEmailAddress() {
|
||||||
|
return $this->getDefaultPublicReplyHandlerEmailAddress('C');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReplyHandlerDomain() {
|
||||||
|
return PhabricatorEnv::getEnvConfig(
|
||||||
|
'metamta.diffusion.reply-handler-domain');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReplyHandlerInstructions() {
|
||||||
|
if ($this->supportsReplies()) {
|
||||||
|
return "Reply to comment.";
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) {
|
||||||
|
$commit = $this->getMailReceiver();
|
||||||
|
$actor = $this->getActor();
|
||||||
|
|
||||||
|
// TODO: Support !raise, !accept, etc.
|
||||||
|
// TODO: Content sources.
|
||||||
|
|
||||||
|
$comment = id(new PhabricatorAuditComment())
|
||||||
|
->setAction(PhabricatorAuditActionConstants::COMMENT)
|
||||||
|
->setContent($mail->getCleanTextBody());
|
||||||
|
|
||||||
|
$editor = new PhabricatorAuditCommentEditor($commit);
|
||||||
|
$editor->setUser($actor);
|
||||||
|
$editor->addComment($comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
src/applications/audit/replyhandler/__init__.php
Normal file
18
src/applications/audit/replyhandler/__init__.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/audit/constants/action');
|
||||||
|
phutil_require_module('phabricator', 'applications/audit/editor/comment');
|
||||||
|
phutil_require_module('phabricator', 'applications/audit/storage/auditcomment');
|
||||||
|
phutil_require_module('phabricator', 'applications/metamta/replyhandler/base');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorAuditReplyHandler.php');
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2011 Facebook, Inc.
|
* Copyright 2012 Facebook, Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -56,6 +56,9 @@ abstract class PhabricatorMailReplyHandler {
|
||||||
if (!PhabricatorEnv::getEnvConfig('metamta.public-replies')) {
|
if (!PhabricatorEnv::getEnvConfig('metamta.public-replies')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!$this->getReplyHandlerDomain()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return (bool)$this->getPublicReplyHandlerEmailAddress();
|
return (bool)$this->getPublicReplyHandlerEmailAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2011 Facebook, Inc.
|
* Copyright 2012 Facebook, Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -116,7 +116,7 @@ class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
|
||||||
$prefixPattern = ($single_handle_prefix)
|
$prefixPattern = ($single_handle_prefix)
|
||||||
? preg_quote($single_handle_prefix, '/') . '\+'
|
? preg_quote($single_handle_prefix, '/') . '\+'
|
||||||
: '';
|
: '';
|
||||||
$pattern = "/^{$prefixPattern}((?:D|T)\d+)\+([\w]+)\+([a-f0-9]{16})@/U";
|
$pattern = "/^{$prefixPattern}((?:D|T|C)\d+)\+([\w]+)\+([a-f0-9]{16})@/U";
|
||||||
|
|
||||||
$ok = preg_match(
|
$ok = preg_match(
|
||||||
$pattern,
|
$pattern,
|
||||||
|
@ -189,6 +189,9 @@ class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
|
||||||
$handler = $editor->buildReplyHandler($receiver);
|
$handler = $editor->buildReplyHandler($receiver);
|
||||||
} else if ($receiver instanceof DifferentialRevision) {
|
} else if ($receiver instanceof DifferentialRevision) {
|
||||||
$handler = DifferentialMail::newReplyHandlerForRevision($receiver);
|
$handler = DifferentialMail::newReplyHandlerForRevision($receiver);
|
||||||
|
} else if ($receiver instanceof PhabricatorRepositoryCommit) {
|
||||||
|
$handler = PhabricatorAuditCommentEditor::newReplyHandlerForCommit(
|
||||||
|
$receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
$handler->setActor($user);
|
$handler->setActor($user);
|
||||||
|
@ -222,6 +225,9 @@ class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
|
||||||
case 'D':
|
case 'D':
|
||||||
$class_obj = newv('DifferentialRevision', array());
|
$class_obj = newv('DifferentialRevision', array());
|
||||||
break;
|
break;
|
||||||
|
case 'C':
|
||||||
|
$class_obj = newv('PhabricatorRepositoryCommit', array());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/audit/editor/comment');
|
||||||
phutil_require_module('phabricator', 'applications/differential/mail/base');
|
phutil_require_module('phabricator', 'applications/differential/mail/base');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/constants/priority');
|
phutil_require_module('phabricator', 'applications/maniphest/constants/priority');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/editor/transaction');
|
phutil_require_module('phabricator', 'applications/maniphest/editor/transaction');
|
||||||
|
|
|
@ -23,6 +23,9 @@ class PhabricatorRepositoryCommit extends PhabricatorRepositoryDAO {
|
||||||
protected $commitIdentifier;
|
protected $commitIdentifier;
|
||||||
protected $epoch;
|
protected $epoch;
|
||||||
|
|
||||||
|
// TODO: Add this!
|
||||||
|
// protected $mailKey;
|
||||||
|
|
||||||
private $commitData;
|
private $commitData;
|
||||||
|
|
||||||
public function getConfiguration() {
|
public function getConfiguration() {
|
||||||
|
@ -58,6 +61,11 @@ class PhabricatorRepositoryCommit extends PhabricatorRepositoryDAO {
|
||||||
return $this->commitData;
|
return $this->commitData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMailKey() {
|
||||||
|
// TODO: Fix properly!
|
||||||
|
return $this->phid;
|
||||||
|
}
|
||||||
|
|
||||||
public function delete() {
|
public function delete() {
|
||||||
$data = $this->loadCommitData();
|
$data = $this->loadCommitData();
|
||||||
$this->openTransaction();
|
$this->openTransaction();
|
||||||
|
|
Loading…
Reference in a new issue