mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-27 07:50:57 +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.
|
||||
'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
|
||||
// separate email to each recipient when you enable reply handling. This is
|
||||
// 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',
|
||||
'PhabricatorAuditListView' => 'applications/audit/view/list',
|
||||
'PhabricatorAuditQuery' => 'applications/audit/query/audit',
|
||||
'PhabricatorAuditReplyHandler' => 'applications/audit/replyhandler',
|
||||
'PhabricatorAuditStatusConstants' => 'applications/audit/constants/status',
|
||||
'PhabricatorAuthController' => 'applications/auth/controller/base',
|
||||
'PhabricatorCalendarBrowseController' => 'applications/calendar/controller/browse',
|
||||
|
@ -1240,6 +1241,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuditEditController' => 'PhabricatorAuditController',
|
||||
'PhabricatorAuditListController' => 'PhabricatorAuditController',
|
||||
'PhabricatorAuditListView' => 'AphrontView',
|
||||
'PhabricatorAuditReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||
'PhabricatorAuthController' => 'PhabricatorController',
|
||||
'PhabricatorCalendarBrowseController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarController' => 'PhabricatorController',
|
||||
|
|
|
@ -36,6 +36,10 @@ final class PhabricatorAuditCommentEditor {
|
|||
$commit = $this->commit;
|
||||
$user = $this->user;
|
||||
|
||||
$other_comments = id(new PhabricatorAuditComment())->loadAllWhere(
|
||||
'targetPHID = %s',
|
||||
$commit->getPHID());
|
||||
|
||||
$comment
|
||||
->setActorPHID($user->getPHID())
|
||||
->setTargetPHID($commit->getPHID())
|
||||
|
@ -89,7 +93,7 @@ final class PhabricatorAuditCommentEditor {
|
|||
|
||||
$this->publishFeedStory($comment, array_keys($audit_phids));
|
||||
PhabricatorSearchCommitIndexer::indexCommit($commit);
|
||||
// TODO: Email.
|
||||
$this->sendMail($comment, $other_comments);
|
||||
}
|
||||
|
||||
|
||||
|
@ -156,4 +160,102 @@ final class PhabricatorAuditCommentEditor {
|
|||
->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/publisher');
|
||||
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/package');
|
||||
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/search/index/indexer/repository');
|
||||
phutil_require_module('phabricator', 'infrastructure/env');
|
||||
|
||||
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
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
* 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.
|
||||
|
@ -56,6 +56,9 @@ abstract class PhabricatorMailReplyHandler {
|
|||
if (!PhabricatorEnv::getEnvConfig('metamta.public-replies')) {
|
||||
return false;
|
||||
}
|
||||
if (!$this->getReplyHandlerDomain()) {
|
||||
return false;
|
||||
}
|
||||
return (bool)$this->getPublicReplyHandlerEmailAddress();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
* 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.
|
||||
|
@ -116,7 +116,7 @@ class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
|
|||
$prefixPattern = ($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(
|
||||
$pattern,
|
||||
|
@ -189,6 +189,9 @@ class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
|
|||
$handler = $editor->buildReplyHandler($receiver);
|
||||
} else if ($receiver instanceof DifferentialRevision) {
|
||||
$handler = DifferentialMail::newReplyHandlerForRevision($receiver);
|
||||
} else if ($receiver instanceof PhabricatorRepositoryCommit) {
|
||||
$handler = PhabricatorAuditCommentEditor::newReplyHandlerForCommit(
|
||||
$receiver);
|
||||
}
|
||||
|
||||
$handler->setActor($user);
|
||||
|
@ -222,6 +225,9 @@ class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
|
|||
case 'D':
|
||||
$class_obj = newv('DifferentialRevision', array());
|
||||
break;
|
||||
case 'C':
|
||||
$class_obj = newv('PhabricatorRepositoryCommit', array());
|
||||
break;
|
||||
default:
|
||||
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/maniphest/constants/priority');
|
||||
phutil_require_module('phabricator', 'applications/maniphest/editor/transaction');
|
||||
|
|
|
@ -23,6 +23,9 @@ class PhabricatorRepositoryCommit extends PhabricatorRepositoryDAO {
|
|||
protected $commitIdentifier;
|
||||
protected $epoch;
|
||||
|
||||
// TODO: Add this!
|
||||
// protected $mailKey;
|
||||
|
||||
private $commitData;
|
||||
|
||||
public function getConfiguration() {
|
||||
|
@ -58,6 +61,11 @@ class PhabricatorRepositoryCommit extends PhabricatorRepositoryDAO {
|
|||
return $this->commitData;
|
||||
}
|
||||
|
||||
public function getMailKey() {
|
||||
// TODO: Fix properly!
|
||||
return $this->phid;
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
$data = $this->loadCommitData();
|
||||
$this->openTransaction();
|
||||
|
|
Loading…
Reference in a new issue