Skip to content

Commit 67d4f1e

Browse files
author
Jason Ge
committedMay 14, 2012
Detect package change and send out email
Summary: For package creation and deletion, send email to all the owners For package modification, detect important fields such as owners and paths, and then send out emails to all owners (including deleted owners and current owners) Also start using transaction for package creation/deletion/modification. Test Plan: - tested mail creation and deletion - tested modification to auditing enabled, primary owners, owners, paths Reviewers: epriestley, nh, vrana Reviewed By: epriestley CC: prithvi, aran, Koolvin Differential Revision: https://secure.phabricator.com/D2470
1 parent bb96115 commit 67d4f1e

17 files changed

+680
-2
lines changed
 

‎conf/default.conf.php

+8
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,14 @@
356356
// affects Diffusion.
357357
'metamta.diffusion.reply-handler' => 'PhabricatorAuditReplyHandler',
358358

359+
// Prefix prepended to mail sent by Package.
360+
'metamta.package.subject-prefix' => '[Package]',
361+
362+
// See 'metamta.maniphest.reply-handler'. This does similar thing for package
363+
// except that it only supports sending out mail and doesn't handle incoming
364+
// email.
365+
'metamta.package.reply-handler' => 'OwnersPackageReplyHandler',
366+
359367
// By default, Phabricator generates unique reply-to addresses and sends a
360368
// separate email to each recipient when you enable reply handling. This is
361369
// more secure than using "From" to establish user identity, but can mean

‎src/__phutil_library_map__.php

+9
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,11 @@
516516
'ManiphestView' => 'applications/maniphest/view/base',
517517
'MetaMTAConstants' => 'applications/metamta/constants/base',
518518
'MetaMTANotificationType' => 'applications/metamta/constants/notificationtype',
519+
'OwnersPackageReplyHandler' => 'applications/owners/replyhandler',
520+
'PackageCreateMail' => 'applications/owners/mail/create',
521+
'PackageDeleteMail' => 'applications/owners/mail/delete',
522+
'PackageMail' => 'applications/owners/mail/base',
523+
'PackageModifyMail' => 'applications/owners/mail/modify',
519524
'Phabricator404Controller' => 'applications/base/controller/404',
520525
'PhabricatorAccessLog' => 'infrastructure/accesslog',
521526
'PhabricatorAphlictTestPageController' => 'applications/notifications/controller/test',
@@ -1470,6 +1475,10 @@
14701475
'ManiphestTransactionType' => 'ManiphestConstants',
14711476
'ManiphestView' => 'AphrontView',
14721477
'MetaMTANotificationType' => 'MetaMTAConstants',
1478+
'OwnersPackageReplyHandler' => 'PhabricatorMailReplyHandler',
1479+
'PackageCreateMail' => 'PackageMail',
1480+
'PackageDeleteMail' => 'PackageMail',
1481+
'PackageModifyMail' => 'PackageMail',
14731482
'Phabricator404Controller' => 'PhabricatorController',
14741483
'PhabricatorAphlictTestPageController' => 'PhabricatorNotificationsController',
14751484
'PhabricatorAuditAddCommentController' => 'PhabricatorAuditController',

‎src/applications/owners/controller/delete/PhabricatorOwnersDeleteController.php

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public function processRequest() {
3535
}
3636

3737
if ($request->isDialogFormPost()) {
38+
$package->attachActorPHID($user->getPHID());
3839
$package->delete();
3940
return id(new AphrontRedirectResponse())->setURI('/owners/');
4041
}

‎src/applications/owners/controller/edit/PhabricatorOwnersEditController.php

+5
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ public function processRequest() {
4747
if ($request->isFormPost()) {
4848
$package->setName($request->getStr('name'));
4949
$package->setDescription($request->getStr('description'));
50+
$old_auditing_enabled = $package->getAuditingEnabled();
5051
$package->setAuditingEnabled($request->getStr('auditing') === 'enabled');
5152

5253
$primary = $request->getArr('primary');
5354
$primary = reset($primary);
55+
$old_primary = $package->getPrimaryOwnerPHID();
5456
$package->setPrimaryOwnerPHID($primary);
5557

5658
$owners = $request->getArr('owners');
@@ -94,6 +96,9 @@ public function processRequest() {
9496
if (!$errors) {
9597
$package->attachUnsavedOwners($owners);
9698
$package->attachUnsavedPaths($path_refs);
99+
$package->attachOldAuditingEnabled($old_auditing_enabled);
100+
$package->attachOldPrimaryOwnerPHID($old_primary);
101+
$package->attachActorPHID($user->getPHID());
97102
try {
98103
$package->save();
99104
return id(new AphrontRedirectResponse())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
<?php
2+
3+
/*
4+
* Copyright 2012 Facebook, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
abstract class PackageMail {
20+
21+
protected $package;
22+
protected $handles;
23+
protected $owners;
24+
protected $paths;
25+
protected $mailTo;
26+
27+
public function __construct($package) {
28+
$this->package = $package;
29+
}
30+
31+
abstract protected function getVerb();
32+
33+
abstract protected function isNewThread();
34+
35+
final protected function getPackage() {
36+
return $this->package;
37+
}
38+
39+
final protected function getHandles() {
40+
return $this->handles;
41+
}
42+
43+
final protected function getOwners() {
44+
return $this->owners;
45+
}
46+
47+
final protected function getPaths() {
48+
return $this->paths;
49+
}
50+
51+
final protected function getMailTo() {
52+
return $this->mailTo;
53+
}
54+
55+
final protected function renderPackageTitle() {
56+
return $this->getPackage()->getName();
57+
}
58+
59+
final protected function renderRepoSubSection($repository_phid, $paths) {
60+
$handles = $this->getHandles();
61+
$section = array();
62+
$section[] = ' In repository '.$handles[$repository_phid]->getName().
63+
' - '. PhabricatorEnv::getProductionURI($handles[$repository_phid]
64+
->getURI());
65+
foreach ($paths as $path => $ignored) {
66+
$section[] = ' '.$path;
67+
}
68+
69+
return implode("\n", $section);
70+
}
71+
72+
protected function needSend() {
73+
return true;
74+
}
75+
76+
protected function loadData() {
77+
$package = $this->getPackage();
78+
$owners = $package->loadOwners();
79+
$this->owners = $owners;
80+
81+
$owner_phids = mpull($owners, 'getUserPHID');
82+
$primary_owner_phid = $package->getPrimaryOwnerPHID();
83+
$mail_to = $owner_phids;
84+
if (!in_array($primary_owner_phid, $owner_phids)) {
85+
$mail_to[] = $primary_owner_phid;
86+
}
87+
$this->mailTo = $mail_to;
88+
89+
$paths = $package->loadPaths();
90+
$this->paths = mgroup($paths, 'getRepositoryPHID', 'getPath');
91+
92+
$phids = array_mergev(array(
93+
$this->mailTo,
94+
array($package->getActorPHID()),
95+
array_keys($this->paths),
96+
));
97+
$this->handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
98+
}
99+
100+
final protected function renderSummarySection() {
101+
$package = $this->getPackage();
102+
$handles = $this->getHandles();
103+
$section = array();
104+
$section[] = $handles[$package->getActorPHID()]->getName().' '.
105+
strtolower($this->getVerb()).' '.$this->renderPackageTitle().'.';
106+
$section[] = '';
107+
108+
$section[] = 'PACKAGE DETAIL';
109+
$section[] = ' '.PhabricatorEnv::getProductionURI(
110+
'/owners/package/'.$package->getID().'/');
111+
112+
return implode("\n", $section);
113+
}
114+
115+
protected function renderDescriptionSection() {
116+
return "PACKAGE DESCRIPTION\n".
117+
' '.$this->getPackage()->getDescription();
118+
}
119+
120+
protected function renderPrimaryOwnerSection() {
121+
$handles = $this->getHandles();
122+
return "PRIMARY OWNER\n".
123+
' '.$handles[$this->getPackage()->getPrimaryOwnerPHID()]->getName();
124+
}
125+
126+
protected function renderOwnersSection() {
127+
$handles = $this->getHandles();
128+
$owners = $this->getOwners();
129+
if (!$owners) {
130+
return null;
131+
}
132+
133+
$owners = mpull($owners, 'getUserPHID');
134+
$owners = array_select_keys($handles, $owners);
135+
$owners = mpull($owners, 'getName');
136+
return "OWNERS\n".
137+
' '.implode(', ', $owners);
138+
}
139+
140+
protected function renderAuditingEnabledSection() {
141+
return "AUDITING ENABLED STATUS\n".
142+
' '.($this->getPackage()->getAuditingEnabled() ? 'Enabled' : 'Disabled');
143+
}
144+
145+
protected function renderPathsSection() {
146+
$section = array();
147+
$section[] = 'PATHS';
148+
foreach ($this->paths as $repository_phid => $paths) {
149+
$section[] = $this->renderRepoSubSection($repository_phid, $paths);
150+
}
151+
152+
return implode("\n", $section);
153+
}
154+
155+
final protected function renderBody() {
156+
$body = array();
157+
$body[] = $this->renderSummarySection();
158+
$body[] = $this->renderDescriptionSection();
159+
$body[] = $this->renderPrimaryOwnerSection();
160+
$body[] = $this->renderOwnersSection();
161+
$body[] = $this->renderAuditingEnabledSection();
162+
$body[] = $this->renderPathsSection();
163+
$body = array_filter($body);
164+
return implode("\n\n", $body);
165+
}
166+
167+
final public function send() {
168+
$mails = $this->prepareMails();
169+
170+
foreach ($mails as $mail) {
171+
$mail->saveAndSend();
172+
}
173+
}
174+
175+
final public function prepareMails() {
176+
if (!$this->needSend()) {
177+
return array();
178+
}
179+
180+
$this->loadData();
181+
182+
$package = $this->getPackage();
183+
$prefix = PhabricatorEnv::getEnvConfig('metamta.package.subject-prefix');
184+
$verb = $this->getVerb();
185+
$package_title = $this->renderPackageTitle();
186+
$subject = trim("{$prefix} {$package_title}");
187+
$vary_subject = trim("{$prefix} [{$verb}] {$package_title}");
188+
$threading = $this->getMailThreading();
189+
list($thread_id, $thread_topic) = $threading;
190+
191+
$template = id(new PhabricatorMetaMTAMail())
192+
->setSubject($subject)
193+
->setVarySubject($vary_subject)
194+
->setFrom($package->getActorPHID())
195+
->setThreadID($thread_id, $this->isNewThread())
196+
->addHeader('Thread-Topic', $thread_topic)
197+
->setRelatedPHID($package->getPHID())
198+
->setIsBulk(true)
199+
->setBody($this->renderBody());
200+
201+
$reply_handler = $this->newReplyHandler();
202+
$mails = $reply_handler->multiplexMail(
203+
$template,
204+
array_select_keys($this->getHandles(), $this->getMailTo()),
205+
array());
206+
return $mails;
207+
}
208+
209+
private function getMailThreading() {
210+
return array(
211+
'package-'.$this->getPackage()->getPHID(),
212+
'package '.$this->getPackage()->getPHID(),
213+
);
214+
}
215+
216+
private function newReplyHandler() {
217+
$reply_handler = PhabricatorEnv::newObjectFromConfig(
218+
'metamta.package.reply-handler');
219+
$reply_handler->setMailReceiver($this->getPackage());
220+
return $reply_handler;
221+
}
222+
223+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
/**
3+
* This file is automatically generated. Lint this module to rebuild it.
4+
* @generated
5+
*/
6+
7+
8+
9+
phutil_require_module('phabricator', 'applications/metamta/storage/mail');
10+
phutil_require_module('phabricator', 'applications/phid/handle/data');
11+
phutil_require_module('phabricator', 'infrastructure/env');
12+
13+
phutil_require_module('phutil', 'utils');
14+
15+
16+
phutil_require_source('PackageMail.php');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/*
4+
* Copyright 2012 Facebook, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
final class PackageCreateMail extends PackageMail {
20+
21+
protected function isNewThread() {
22+
return true;
23+
}
24+
25+
protected function getVerb() {
26+
return 'Created';
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
/**
3+
* This file is automatically generated. Lint this module to rebuild it.
4+
* @generated
5+
*/
6+
7+
8+
9+
phutil_require_module('phabricator', 'applications/owners/mail/base');
10+
11+
12+
phutil_require_source('PackageCreateMail.php');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* Copyright 2012 Facebook, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
final class PackageDeleteMail extends PackageMail {
20+
21+
protected function getVerb() {
22+
return "Deleted";
23+
}
24+
25+
protected function isNewThread() {
26+
return false;
27+
}
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
/**
3+
* This file is automatically generated. Lint this module to rebuild it.
4+
* @generated
5+
*/
6+
7+
8+
9+
phutil_require_module('phabricator', 'applications/owners/mail/base');
10+
11+
12+
phutil_require_source('PackageDeleteMail.php');

0 commit comments

Comments
 (0)
Failed to load comments.