Merge lp:~cjwatson/launchpad/message-for-header into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 17730
Proposed branch: lp:~cjwatson/launchpad/message-for-header
Merge into: lp:launchpad
Diff against target: 895 lines (+141/-40)
28 files modified
lib/lp/answers/model/questionjob.py (+1/-0)
lib/lp/bugs/doc/bugnotification-email.txt (+6/-0)
lib/lp/bugs/doc/bugnotification-sending.txt (+30/-4)
lib/lp/bugs/doc/initial-bug-contacts.txt (+2/-0)
lib/lp/bugs/mail/bugnotificationbuilder.py (+11/-0)
lib/lp/bugs/stories/bugs/bug-add-subscriber.txt (+1/-0)
lib/lp/code/doc/branch-merge-proposal-notifications.txt (+12/-7)
lib/lp/code/doc/branch-notifications.txt (+2/-0)
lib/lp/code/doc/codeimport.txt (+2/-0)
lib/lp/code/mail/codeimport.py (+5/-1)
lib/lp/code/mail/tests/test_branch.py (+2/-0)
lib/lp/code/mail/tests/test_branchmergeproposal.py (+1/-0)
lib/lp/code/mail/tests/test_codereviewcomment.py (+2/-0)
lib/lp/code/mail/tests/test_sourcepackagerecipebuild.py (+4/-0)
lib/lp/registry/browser/person.py (+1/-1)
lib/lp/registry/doc/teammembership-email-notification.txt (+2/-1)
lib/lp/registry/mail/notification.py (+7/-4)
lib/lp/registry/model/productjob.py (+2/-1)
lib/lp/registry/tests/test_notification.py (+10/-6)
lib/lp/registry/tests/test_productjob.py (+3/-1)
lib/lp/services/mail/basemailer.py (+2/-0)
lib/lp/services/mail/notificationrecipientset.py (+1/-0)
lib/lp/services/mail/tests/test_basemailer.py (+3/-1)
lib/lp/snappy/tests/test_snapbuild.py (+1/-0)
lib/lp/soyuz/mail/tests/test_packageupload.py (+14/-7)
lib/lp/soyuz/tests/test_build_notify.py (+3/-0)
lib/lp/soyuz/tests/test_livefsbuild.py (+2/-1)
lib/lp/testing/mail_helpers.py (+9/-5)
To merge this branch: bzr merge lp:~cjwatson/launchpad/message-for-header
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+270811@code.launchpad.net

Commit message

Add an X-Launchpad-Message-For header with just the name of the person subscribed to the notification.

Description of the change

Add an X-Launchpad-Message-For header with just the name of the person subscribed to the notification.

There are several times more implementations of this than there ought to be, because not everything goes through BaseMailer yet; and the implementation for bug notifications is horrible because BugNotificationRecipient doesn't (I think) provide enough information.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/answers/model/questionjob.py'
2--- lib/lp/answers/model/questionjob.py 2015-08-25 16:24:06 +0000
3+++ lib/lp/answers/model/questionjob.py 2015-09-11 12:44:20 +0000
4@@ -236,6 +236,7 @@
5 for email in recipients.getEmails():
6 reason, header = recipients.getReason(email)
7 headers['X-Launchpad-Message-Rationale'] = header
8+ headers['X-Launchpad-Message-For'] = reason.subscriber.name
9 formatted_body = self.buildBody(reason.getReason())
10 simple_sendmail(
11 self.from_address, email, self.subject, formatted_body,
12
13=== modified file 'lib/lp/bugs/doc/bugnotification-email.txt'
14--- lib/lp/bugs/doc/bugnotification-email.txt 2015-07-31 14:46:31 +0000
15+++ lib/lp/bugs/doc/bugnotification-email.txt 2015-09-11 12:44:20 +0000
16@@ -585,6 +585,12 @@
17 >>> print notification_email['X-Launchpad-Message-Rationale']
18 Because-I-said-so
19
20+The X-Launchpad-Message-For header is set from the to_person (since this
21+notification is not for a team).
22+
23+ >>> print notification_email['X-Launchpad-Message-For']
24+ name16
25+
26 The references parameter sets the References header of the email.
27
28 >>> print notification_email['References']
29
30=== modified file 'lib/lp/bugs/doc/bugnotification-sending.txt'
31--- lib/lp/bugs/doc/bugnotification-sending.txt 2015-07-13 16:14:46 +0000
32+++ lib/lp/bugs/doc/bugnotification-sending.txt 2015-09-11 12:44:20 +0000
33@@ -22,6 +22,7 @@
34 >>> def print_notification_headers(email_notification, extra_headers=[]):
35 ... for header in ['To', 'From', 'Subject',
36 ... 'X-Launchpad-Message-Rationale',
37+ ... 'X-Launchpad-Message-For',
38 ... 'X-Launchpad-Subscription'] + extra_headers:
39 ... if email_notification[header]:
40 ... print "%s: %s" % (header, email_notification[header])
41@@ -73,6 +74,7 @@
42 From: Sample Person <1@bugs.launchpad.net>
43 Subject: [Bug 1] subject
44 X-Launchpad-Message-Rationale: Subscriber (mozilla-firefox in Ubuntu)
45+ X-Launchpad-Message-For: name16
46 <BLANKLINE>
47 a comment.
48 <BLANKLINE>
49@@ -82,6 +84,7 @@
50 From: Sample Person <1@bugs.launchpad.net>
51 Subject: [Bug 1] subject
52 X-Launchpad-Message-Rationale: Assignee
53+ X-Launchpad-Message-For: mark
54 <BLANKLINE>
55 a comment.
56 <BLANKLINE>
57@@ -91,6 +94,7 @@
58 From: Sample Person <1@bugs.launchpad.net>
59 Subject: [Bug 1] subject
60 X-Launchpad-Message-Rationale: Subscriber
61+ X-Launchpad-Message-For: name12
62 <BLANKLINE>
63 a comment.
64 <BLANKLINE>
65@@ -154,6 +158,7 @@
66 From: Sample Person <1@bugs.launchpad.net>
67 Subject: Re: [Bug 1] subject
68 X-Launchpad-Message-Rationale: Assignee
69+ X-Launchpad-Message-For: mark
70 <BLANKLINE>
71 a new comment.
72 <BLANKLINE>
73@@ -193,6 +198,7 @@
74 From: Sample Person <1@bugs.launchpad.net>
75 Subject: [Bug 1] Re: Firefox does not support SVG
76 X-Launchpad-Message-Rationale: Assignee
77+ X-Launchpad-Message-For: mark
78 <BLANKLINE>
79 ** Summary changed:
80 - Old summary
81@@ -239,6 +245,7 @@
82 From: Sample Person <1@bugs.launchpad.net>
83 Subject: [Bug 1] Re: Firefox does not support SVG
84 X-Launchpad-Message-Rationale: Assignee
85+ X-Launchpad-Message-For: mark
86 <BLANKLINE>
87 a new comment.
88 <BLANKLINE>
89@@ -366,6 +373,7 @@
90 From: Sample Person <16@bugs.launchpad.net>
91 Subject: [Bug 16] [NEW] new bug
92 X-Launchpad-Message-Rationale: Subscriber
93+ X-Launchpad-Message-For: name12
94 <BLANKLINE>
95 Public bug reported:
96 ...
97@@ -396,6 +404,7 @@
98 From: Sample Person <16@bugs.launchpad.net>
99 Subject: [Bug 16] subject
100 X-Launchpad-Message-Rationale: Subscriber
101+ X-Launchpad-Message-For: name12
102 X-Launchpad-Bug-Duplicate: 1
103 <BLANKLINE>
104 *** This bug is a duplicate of bug 1 ***
105@@ -432,6 +441,7 @@
106 From: Sample Person <...@bugs.launchpad.net>
107 Subject: [Bug ...] [NEW] Zero-day on Frobulator
108 X-Launchpad-Message-Rationale: Subscriber
109+ X-Launchpad-Message-For: name12
110 <BLANKLINE>
111 *** This bug is a security vulnerability ***
112 <BLANKLINE>
113@@ -456,6 +466,7 @@
114 From: Sample Person <...@bugs.launchpad.net>
115 Subject: [Bug ...] subject
116 X-Launchpad-Message-Rationale: Subscriber
117+ X-Launchpad-Message-For: name12
118 <BLANKLINE>
119 a comment.
120 <BLANKLINE>
121@@ -538,6 +549,7 @@
122 References: foo@example.com-332342--1231
123 ...
124 X-Launchpad-Message-Rationale: Assignee
125+ X-Launchpad-Message-For: name12
126 ...
127 INFO Notifying foo.bar@canonical.com about bug 1.
128 ...
129@@ -548,6 +560,7 @@
130 References: sdsdfsfd
131 ...
132 X-Launchpad-Message-Rationale: Subscriber (mozilla-firefox in Ubuntu)
133+ X-Launchpad-Message-For: name16
134 ...
135 INFO Notifying mark@example.com about bug 1.
136 ...
137@@ -564,6 +577,7 @@
138 References: sdsdfsfd
139 ...
140 X-Launchpad-Message-Rationale: Subscriber (mozilla-firefox in Ubuntu)
141+ X-Launchpad-Message-For: name16
142 Errors-To: bounces@canonical.com
143 Return-Path: bounces@canonical.com
144 Precedence: bulk
145@@ -840,12 +854,14 @@
146 ... "will be automatically wrapped by the BugNotification "
147 ... "machinery. Ain't technology great?")
148 ... verbose_person = factory.makePerson(
149- ... displayname='Verbose Person', email='verbose@example.com',
150+ ... name='verbose-person', displayname='Verbose Person',
151+ ... email='verbose@example.com',
152 ... selfgenerated_bugnotifications=True)
153 ... verbose_person.verbose_bugnotifications = True
154 ... ignored = bug.subscribe(verbose_person, verbose_person)
155 ... concise_person = factory.makePerson(
156- ... displayname='Concise Person', email='concise@example.com')
157+ ... name='concise-person', displayname='Concise Person',
158+ ... email='concise@example.com')
159 ... concise_person.verbose_bugnotifications = False
160 ... ignored = bug.subscribe(concise_person, concise_person)
161
162@@ -858,7 +874,7 @@
163 ... name='conciseteam', displayname='Concise Team')
164 ... concise_team.verbose_bugnotifications = False
165 ... concise_team_person = factory.makePerson(
166- ... displayname='Concise Team Person',
167+ ... name='conciseteam-person', displayname='Concise Team Person',
168 ... email='conciseteam@example.com')
169 ... concise_team_person.verbose_bugnotifications = True
170 ... ignored = concise_team.addMember(
171@@ -873,7 +889,7 @@
172 ... name='verboseteam', displayname='Verbose Team')
173 ... verbose_team.verbose_bugnotifications = True
174 ... verbose_team_person = factory.makePerson(
175- ... displayname='Verbose Team Person',
176+ ... name='verboseteam-person', displayname='Verbose Team Person',
177 ... email='verboseteam@example.com')
178 ... verbose_team_person.verbose_bugnotifications = False
179 ... ignored = verbose_team.addMember(
180@@ -934,6 +950,7 @@
181 From: Verbose Person <verbose@example.com>
182 Subject: [Bug ...] subject
183 X-Launchpad-Message-Rationale: Subscriber
184+ X-Launchpad-Message-For: concise-person
185 <BLANKLINE>
186 a really simple comment.
187 <BLANKLINE>
188@@ -961,6 +978,7 @@
189 From: Verbose Person <verbose@example.com>
190 Subject: [Bug ...] subject
191 X-Launchpad-Message-Rationale: Subscriber @verboseteam
192+ X-Launchpad-Message-For: verboseteam
193 <BLANKLINE>
194 a really simple comment.
195 <BLANKLINE>
196@@ -980,6 +998,7 @@
197 From: Verbose Person <verbose@example.com>
198 Subject: [Bug ...] subject
199 X-Launchpad-Message-Rationale: Subscriber
200+ X-Launchpad-Message-For: verbose-person
201 <BLANKLINE>
202 a really simple comment.
203 <BLANKLINE>
204@@ -1010,6 +1029,7 @@
205 From: Verbose Person <verbose@example.com>
206 Subject: [Bug ...] subject
207 X-Launchpad-Message-Rationale: Subscriber @conciseteam
208+ X-Launchpad-Message-For: conciseteam
209 <BLANKLINE>
210 a really simple comment.
211 <BLANKLINE>
212@@ -1134,6 +1154,7 @@
213 From: Sample Person <...@bugs.launchpad.net>
214 Subject: [Bug 1] subject
215 X-Launchpad-Message-Rationale: Subscriber (Mozilla Firefox)
216+ X-Launchpad-Message-For: no-priv
217 <BLANKLINE>
218 another comment.
219 <BLANKLINE>
220@@ -1190,6 +1211,7 @@
221 From: Sample Person <...@bugs.launchpad.net>
222 Subject: [Bug 1] subject
223 X-Launchpad-Message-Rationale: Subscriber (Mozilla Firefox)
224+ X-Launchpad-Message-For: no-priv
225 X-Launchpad-Subscription: Allow-comments filter
226 <BLANKLINE>
227 another comment.
228@@ -1245,6 +1267,7 @@
229 From: Sample Person <...@bugs.launchpad.net>
230 Subject: [Bug 1] subject
231 X-Launchpad-Message-Rationale: Subscriber @addressless
232+ X-Launchpad-Message-For: addressless
233 <BLANKLINE>
234 no comment for no-priv.
235 <BLANKLINE>
236@@ -1291,6 +1314,7 @@
237 From: Sample Person <...@bugs.launchpad.net>
238 Subject: [Bug 1] subject
239 X-Launchpad-Message-Rationale: Subscriber (Mozilla Firefox)
240+ X-Launchpad-Message-For: no-priv
241 X-Launchpad-Subscription: Allow-comments filter
242 <BLANKLINE>
243 no comment for no-priv.
244@@ -1350,6 +1374,7 @@
245 From: Sample Person <...@bugs.launchpad.net>
246 Subject: [Bug 1] Re: Firefox does not support SVG
247 X-Launchpad-Message-Rationale: Subscriber @addressless
248+ X-Launchpad-Message-For: addressless
249 <BLANKLINE>
250 ** Summary changed:
251 - Whatever
252@@ -1405,6 +1430,7 @@
253 From: Sample Person <...@bugs.launchpad.net>
254 Subject: [Bug 1] Re: Firefox does not support SVG
255 X-Launchpad-Message-Rationale: Subscriber (Mozilla Firefox)
256+ X-Launchpad-Message-For: no-priv
257 <BLANKLINE>
258 ** Summary changed:
259 - I'm losing my
260
261=== modified file 'lib/lp/bugs/doc/initial-bug-contacts.txt'
262--- lib/lp/bugs/doc/initial-bug-contacts.txt 2012-08-16 07:02:41 +0000
263+++ lib/lp/bugs/doc/initial-bug-contacts.txt 2015-09-11 12:44:20 +0000
264@@ -191,6 +191,8 @@
265
266 >>> msg['X-Launchpad-Message-Rationale']
267 'Subscriber (pmount in Ubuntu)'
268+ >>> msg['X-Launchpad-Message-For']
269+ 'daf'
270
271 >>> msg['Subject']
272 '[Bug 1] [NEW] Firefox does not support SVG'
273
274=== modified file 'lib/lp/bugs/mail/bugnotificationbuilder.py'
275--- lib/lp/bugs/mail/bugnotificationbuilder.py 2015-08-28 06:44:52 +0000
276+++ lib/lp/bugs/mail/bugnotificationbuilder.py 2015-09-11 12:44:20 +0000
277@@ -13,6 +13,7 @@
278
279 from email.mime.text import MIMEText
280 from email.utils import formatdate
281+import re
282 import rfc822
283
284 from zope.component import getUtility
285@@ -207,6 +208,16 @@
286
287 if rationale is not None:
288 headers.append(('X-Launchpad-Message-Rationale', rationale))
289+ # XXX cjwatson 2015-09-11: The ridiculously complicated way that
290+ # bug notifications are built means that we no longer have
291+ # direct access to the subscriber name at this point. As a
292+ # stopgap, parse it out of the rationale.
293+ match = re.search(r'@([^ ]*)', rationale)
294+ if match is not None:
295+ message_for = match.group(1)
296+ else:
297+ message_for = removeSecurityProxy(to_person).name
298+ headers.append(('X-Launchpad-Message-For', message_for))
299
300 if filters is not None:
301 for filter in filters:
302
303=== modified file 'lib/lp/bugs/stories/bugs/bug-add-subscriber.txt'
304--- lib/lp/bugs/stories/bugs/bug-add-subscriber.txt 2012-07-27 01:15:04 +0000
305+++ lib/lp/bugs/stories/bugs/bug-add-subscriber.txt 2015-09-11 12:44:20 +0000
306@@ -82,6 +82,7 @@
307 Reply-To: Bug ... <...@bugs.launchpad.net>
308 ...
309 X-Launchpad-Message-Rationale: Subscriber
310+ X-Launchpad-Message-For: ddaa
311 ...
312 You have been subscribed to a public bug by No Privileges Person (no-priv):
313 ...
314
315=== modified file 'lib/lp/code/doc/branch-merge-proposal-notifications.txt'
316--- lib/lp/code/doc/branch-merge-proposal-notifications.txt 2015-09-02 16:54:24 +0000
317+++ lib/lp/code/doc/branch-merge-proposal-notifications.txt 2015-09-11 12:44:20 +0000
318@@ -29,13 +29,15 @@
319 >>> previewdiff = factory.makePreviewDiff(merge_proposal=bmp)
320 >>> transaction.commit()
321 >>> source_subscriber = factory.makePerson(
322- ... email='source@example.com', displayname='Source Subscriber')
323+ ... email='source@example.com', name='source-subscriber',
324+ ... displayname='Source Subscriber')
325 >>> _unused = bmp.source_branch.subscribe(source_subscriber,
326 ... BranchSubscriptionNotificationLevel.NOEMAIL,
327 ... BranchSubscriptionDiffSize.NODIFF,
328 ... CodeReviewNotificationLevel.STATUS, source_subscriber)
329 >>> target_subscriber = factory.makePerson(
330- ... email='target@example.com', displayname='Target Subscriber')
331+ ... email='target@example.com', name='target-subscriber',
332+ ... displayname='Target Subscriber')
333 >>> target_subscription = bmp.target_branch.subscribe(target_subscriber,
334 ... BranchSubscriptionNotificationLevel.NOEMAIL,
335 ... BranchSubscriptionDiffSize.NODIFF,
336@@ -136,6 +138,8 @@
337 ~person-name...
338 >>> print notification['X-Launchpad-Message-Rationale']
339 Subscriber
340+ >>> print notification['X-Launchpad-Message-For']
341+ source-subscriber
342 >>> print notification.get_payload(decode=True)
343 Eric has proposed merging
344 lp://dev/~person-name...into lp://dev/~person-name...
345@@ -176,11 +180,12 @@
346 >>> for notification in notifications:
347 ... print "%s, %s" % (
348 ... notification['X-Envelope-To'],
349- ... notification['X-Launchpad-Message-Rationale'])
350- bob@example.com, Reviewer
351- mary@example.com, Reviewer
352- source@example.com, Subscriber
353- target@example.com, Subscriber
354+ ... notification['X-Launchpad-Message-Rationale'],
355+ ... notification['X-Launchpad-Message-For'])
356+ bob@example.com, Reviewer, bob
357+ mary@example.com, Reviewer, mary
358+ source@example.com, Subscriber, source-subscriber
359+ target@example.com, Subscriber, target-subscriber
360 >>> notification = notifications[0]
361 >>> print notification.get_payload()[0].get_payload(decode=True)
362 Eric has proposed merging
363
364=== modified file 'lib/lp/code/doc/branch-notifications.txt'
365--- lib/lp/code/doc/branch-notifications.txt 2015-09-02 16:54:24 +0000
366+++ lib/lp/code/doc/branch-notifications.txt 2015-09-11 12:44:20 +0000
367@@ -60,6 +60,8 @@
368 ~name12/firefox/main
369 >>> print branch_notification['X-Launchpad-Message-Rationale']
370 Subscriber
371+ >>> print branch_notification['X-Launchpad-Message-For']
372+ name12
373 >>> notification_body = branch_notification.get_payload(decode=True)
374 >>> print notification_body #doctest: -NORMALIZE_WHITESPACE
375 The contents.
376
377=== modified file 'lib/lp/code/doc/codeimport.txt'
378--- lib/lp/code/doc/codeimport.txt 2014-02-24 07:19:52 +0000
379+++ lib/lp/code/doc/codeimport.txt 2015-09-11 12:44:20 +0000
380@@ -100,6 +100,8 @@
381 New code import: widget/trunk-cvs
382 >>> print message['X-Launchpad-Message-Rationale']
383 Operator @vcs-imports
384+ >>> print message['X-Launchpad-Message-For']
385+ vcs-imports
386 >>> print message.get_payload(decode=True)
387 A new CVS code import has been requested by Code Import Person:
388 http://code.launchpad.dev/~import-person/widget/trunk-cvs
389
390=== modified file 'lib/lp/code/mail/codeimport.py'
391--- lib/lp/code/mail/codeimport.py 2014-02-24 07:19:52 +0000
392+++ lib/lp/code/mail/codeimport.py 2015-09-11 12:44:20 +0000
393@@ -1,4 +1,4 @@
394-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
395+# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
396 # GNU Affero General Public License version 3 (see the file LICENSE).
397
398 """Email notifications related to code imports."""
399@@ -65,6 +65,7 @@
400 headers = {'X-Launchpad-Branch': code_import.branch.unique_name,
401 'X-Launchpad-Message-Rationale':
402 'Operator @%s' % vcs_imports.name,
403+ 'X-Launchpad-Message-For': vcs_imports.name,
404 'X-Launchpad-Notification-Type': 'code-import',
405 }
406 for address in get_contact_email_addresses(vcs_imports):
407@@ -185,6 +186,7 @@
408 else:
409 template_params['rationale'] = rationale
410 template_params['unsubscribe'] = ''
411+ for_person = vcs_imports
412 else:
413 if subscription.notification_level in interested_levels:
414 template_params['rationale'] = (
415@@ -197,10 +199,12 @@
416 "%s/+edit-subscription." % canonical_url(branch))
417 else:
418 template_params['unsubscribe'] = ''
419+ for_person = subscription.person
420 else:
421 # Don't send email to this subscriber.
422 continue
423
424 headers['X-Launchpad-Message-Rationale'] = rationale
425+ headers['X-Launchpad-Message-For'] = for_person.name
426 body = email_template % template_params
427 simple_sendmail(from_address, email_address, subject, body, headers)
428
429=== modified file 'lib/lp/code/mail/tests/test_branch.py'
430--- lib/lp/code/mail/tests/test_branch.py 2015-09-02 16:54:24 +0000
431+++ lib/lp/code/mail/tests/test_branch.py 2015-09-11 12:44:20 +0000
432@@ -227,6 +227,7 @@
433 self.assertEqual(
434 {'X-Launchpad-Branch': branch.unique_name,
435 'X-Launchpad-Message-Rationale': 'Subscriber',
436+ 'X-Launchpad-Message-For': bob.name,
437 'X-Launchpad-Notification-Type': 'branch-updated',
438 'X-Launchpad-Project': self.getBranchProjectName(branch),
439 'Message-Id': '<foobar-example-com>'},
440@@ -247,6 +248,7 @@
441 self.assertEqual(
442 {'X-Launchpad-Branch': branch.unique_name,
443 'X-Launchpad-Message-Rationale': 'Subscriber',
444+ 'X-Launchpad-Message-For': bob.name,
445 'X-Launchpad-Notification-Type': 'branch-revision',
446 'X-Launchpad-Branch-Revision-Number': '1',
447 'X-Launchpad-Project': self.getBranchProjectName(branch),
448
449=== modified file 'lib/lp/code/mail/tests/test_branchmergeproposal.py'
450--- lib/lp/code/mail/tests/test_branchmergeproposal.py 2015-09-02 16:54:24 +0000
451+++ lib/lp/code/mail/tests/test_branchmergeproposal.py 2015-09-11 12:44:20 +0000
452@@ -135,6 +135,7 @@
453 self.assertEqual(
454 {'X-Launchpad-Branch': bmp.source_branch.unique_name,
455 'X-Launchpad-Message-Rationale': 'Subscriber',
456+ 'X-Launchpad-Message-For': subscriber.name,
457 'X-Launchpad-Notification-Type': 'code-review',
458 'X-Launchpad-Project': bmp.source_branch.product.name,
459 'Reply-To': bmp.address,
460
461=== modified file 'lib/lp/code/mail/tests/test_codereviewcomment.py'
462--- lib/lp/code/mail/tests/test_codereviewcomment.py 2015-09-08 11:56:33 +0000
463+++ lib/lp/code/mail/tests/test_codereviewcomment.py 2015-09-11 12:44:20 +0000
464@@ -164,6 +164,7 @@
465 rationale = mailer._recipients.getReason('subscriber@example.com')[1]
466 expected = {'X-Launchpad-Branch': source_branch.unique_name,
467 'X-Launchpad-Message-Rationale': rationale,
468+ 'X-Launchpad-Message-For': subscriber.name,
469 'X-Launchpad-Notification-Type': 'code-review',
470 'X-Launchpad-Project': source_branch.product.name,
471 'Message-Id': message.rfc822msgid,
472@@ -221,6 +222,7 @@
473 'You are subscribed to branch %s.' % source_branch.bzr_identity,
474 '',
475 'Launchpad-Message-Rationale: %s' % rationale,
476+ 'Launchpad-Message-For: %s' % subscriber.name,
477 'Launchpad-Notification-Type: code-review',
478 'Launchpad-Branch: %s' % source_branch.unique_name,
479 'Launchpad-Project: %s' % source_branch.product.name,
480
481=== modified file 'lib/lp/code/mail/tests/test_sourcepackagerecipebuild.py'
482--- lib/lp/code/mail/tests/test_sourcepackagerecipebuild.py 2015-09-02 16:54:24 +0000
483+++ lib/lp/code/mail/tests/test_sourcepackagerecipebuild.py 2015-09-11 12:44:20 +0000
484@@ -84,6 +84,8 @@
485 self.assertEqual(
486 'Requester', ctrl.headers['X-Launchpad-Message-Rationale'])
487 self.assertEqual(
488+ build.requester.name, ctrl.headers['X-Launchpad-Message-For'])
489+ self.assertEqual(
490 'recipe-build-status',
491 ctrl.headers['X-Launchpad-Notification-Type'])
492 self.assertEqual(
493@@ -119,6 +121,8 @@
494 self.assertEqual(
495 'Requester', ctrl.headers['X-Launchpad-Message-Rationale'])
496 self.assertEqual(
497+ build.requester.name, ctrl.headers['X-Launchpad-Message-For'])
498+ self.assertEqual(
499 'recipe-build-status',
500 ctrl.headers['X-Launchpad-Notification-Type'])
501 self.assertEqual(
502
503=== modified file 'lib/lp/registry/browser/person.py'
504--- lib/lp/registry/browser/person.py 2015-08-06 16:48:48 +0000
505+++ lib/lp/registry/browser/person.py 2015-09-11 12:44:20 +0000
506@@ -4327,7 +4327,7 @@
507 return
508 try:
509 send_direct_contact_email(
510- sender_email, self.recipients, subject, message)
511+ sender_email, self.recipients, self.context, subject, message)
512 except QuotaReachedError as error:
513 fmt_date = DateTimeFormatterAPI(self.next_try)
514 self.request.response.addErrorNotification(
515
516=== modified file 'lib/lp/registry/doc/teammembership-email-notification.txt'
517--- lib/lp/registry/doc/teammembership-email-notification.txt 2015-09-02 02:46:18 +0000
518+++ lib/lp/registry/doc/teammembership-email-notification.txt 2015-09-11 12:44:20 +0000
519@@ -1067,10 +1067,11 @@
520 ... name='team-two', email='team-two@example.com', owner=owner)
521 >>> ignored = team_one.addMember(team_two, owner, force_team_add=True)
522 >>> run_mail_jobs()
523- >>> print_distinct_emails()
524+ >>> print_distinct_emails(include_for=True)
525 From: Team One ...
526 To: Team Two <team-two...>
527 X-Launchpad-Message-Rationale: Member (team-one) @team-two
528+ X-Launchpad-Message-For: team-two
529 X-Launchpad-Notification-Type: team-membership-new
530 Subject: team-two joined team-one
531 <BLANKLINE>
532
533=== modified file 'lib/lp/registry/mail/notification.py'
534--- lib/lp/registry/mail/notification.py 2015-09-02 02:46:18 +0000
535+++ lib/lp/registry/mail/notification.py 2015-09-11 12:44:20 +0000
536@@ -157,13 +157,15 @@
537
538
539 def send_direct_contact_email(
540- sender_email, recipients_set, subject, body):
541+ sender_email, recipients_set, person_or_team, subject, body):
542 """Send a direct user-to-user email.
543
544 :param sender_email: The email address of the sender.
545 :type sender_email: string
546 :param recipients_set: The recipients.
547- :type recipients_set:' A ContactViaWebNotificationSet
548+ :type recipients_set: `ContactViaWebNotificationSet`
549+ :param person_or_team: The party that is the context of the email.
550+ :type person_or_team: `IPerson`
551 :param subject: The Subject header.
552 :type subject: unicode
553 :param body: The message body.
554@@ -209,7 +211,7 @@
555 message = None
556 for recipient_email, recipient in recipients_set.getRecipientPersons():
557 recipient_name = str(encode(recipient.displayname))
558- reason, rational_header = recipients_set.getReason(recipient_email)
559+ reason, rationale_header = recipients_set.getReason(recipient_email)
560 reason = str(encode(reason)).replace('\n ', '\n')
561 formatted_body = mailwrapper.format(body, force_wrap=True)
562 formatted_body += additions % reason
563@@ -219,7 +221,8 @@
564 message['To'] = formataddr((recipient_name, recipient_email))
565 message['Subject'] = subject_header
566 message['Message-ID'] = make_msgid('launchpad')
567- message['X-Launchpad-Message-Rationale'] = rational_header
568+ message['X-Launchpad-Message-Rationale'] = rationale_header
569+ message['X-Launchpad-Message-For'] = person_or_team.name
570 # Send the message.
571 sendmail(message, bulk=False)
572 # Use the information from the last message sent to record the action
573
574=== modified file 'lib/lp/registry/model/productjob.py'
575--- lib/lp/registry/model/productjob.py 2015-07-09 20:06:17 +0000
576+++ lib/lp/registry/model/productjob.py 2015-09-11 12:44:20 +0000
577@@ -1,4 +1,4 @@
578-# Copyright 2012 Canonical Ltd. This software is licensed under the
579+# Copyright 2012-2015 Canonical Ltd. This software is licensed under the
580 # GNU Affero General Public License version 3 (see the file LICENSE).
581
582 """Jobs classes to update products and send notifications."""
583@@ -321,6 +321,7 @@
584 'X-Launchpad-Project':
585 '%(product_displayname)s (%(product_name)s)' % message_data,
586 'X-Launchpad-Message-Rationale': rationale,
587+ 'X-Launchpad-Message-For': self.product.owner.name,
588 }
589 if reply_to is not None:
590 headers['Reply-To'] = reply_to
591
592=== modified file 'lib/lp/registry/tests/test_notification.py'
593--- lib/lp/registry/tests/test_notification.py 2012-12-26 01:04:05 +0000
594+++ lib/lp/registry/tests/test_notification.py 2015-09-11 12:44:20 +0000
595@@ -1,4 +1,4 @@
596-# Copyright 2012 Canonical Ltd. This software is licensed under the
597+# Copyright 2012-2015 Canonical Ltd. This software is licensed under the
598 # GNU Affero General Public License version 3 (see the file LICENSE).
599
600 """Test notification classes and functions."""
601@@ -28,7 +28,8 @@
602 recipients_set = NotificationRecipientSet()
603 recipients_set.add(user, 'test reason', 'test rationale')
604 pop_notifications()
605- send_direct_contact_email('me@eg.dom', recipients_set, subject, body)
606+ send_direct_contact_email(
607+ 'me@eg.dom', recipients_set, user, subject, body)
608 notifications = pop_notifications()
609 notification = notifications[0]
610 self.assertEqual(1, len(notifications))
611@@ -37,6 +38,7 @@
612 self.assertEqual(subject, notification['Subject'])
613 self.assertEqual(
614 'test rationale', notification['X-Launchpad-Message-Rationale'])
615+ self.assertEqual(user.name, notification['X-Launchpad-Message-For'])
616 self.assertIs(None, notification['Precedence'])
617 self.assertTrue('launchpad' in notification['Message-ID'])
618 self.assertEqual(
619@@ -61,7 +63,7 @@
620 authorization.record(old_message)
621 self.assertRaises(
622 QuotaReachedError, send_direct_contact_email,
623- 'me@eg.dom', recipients_set, 'subject', 'body')
624+ 'me@eg.dom', recipients_set, user, 'subject', 'body')
625
626 def test_empty_recipient_set(self):
627 # The recipient set can be empty. No messages are sent and the
628@@ -75,7 +77,7 @@
629 authorization.record(old_message)
630 pop_notifications()
631 send_direct_contact_email(
632- 'me@eg.dom', recipients_set, 'subject', 'body')
633+ 'me@eg.dom', recipients_set, user, 'subject', 'body')
634 notifications = pop_notifications()
635 self.assertEqual(0, len(notifications))
636 self.assertTrue(authorization.is_allowed)
637@@ -87,7 +89,8 @@
638 recipients_set.add(user, 'test reason', 'test rationale')
639 pop_notifications()
640 body = 'Can you help me? ' * 8
641- send_direct_contact_email('me@eg.dom', recipients_set, 'subject', body)
642+ send_direct_contact_email(
643+ 'me@eg.dom', recipients_set, user, 'subject', body)
644 notifications = pop_notifications()
645 body, footer = notifications[0].get_payload().split('-- ')
646 self.assertEqual(
647@@ -105,7 +108,8 @@
648 recipients_set = NotificationRecipientSet()
649 recipients_set.add(user, 'test reason', 'test rationale')
650 pop_notifications()
651- send_direct_contact_email('me@eg.dom', recipients_set, 'test', 'test')
652+ send_direct_contact_email(
653+ 'me@eg.dom', recipients_set, user, 'test', 'test')
654 notifications = pop_notifications()
655 notification = notifications[0]
656 self.assertEqual(
657
658=== modified file 'lib/lp/registry/tests/test_productjob.py'
659--- lib/lp/registry/tests/test_productjob.py 2015-07-08 16:05:11 +0000
660+++ lib/lp/registry/tests/test_productjob.py 2015-09-11 12:44:20 +0000
661@@ -1,4 +1,4 @@
662-# Copyright 2010-2012 Canonical Ltd. This software is licensed under the
663+# Copyright 2010-2015 Canonical Ltd. This software is licensed under the
664 # GNU Affero General Public License version 3 (see the file LICENSE).
665
666 """Tests for ProductJobs."""
667@@ -442,6 +442,7 @@
668 ('X-Launchpad-Project', '%s (%s)' %
669 (product.displayname, product.name)),
670 ('X-Launchpad-Message-Rationale', 'Maintainer'),
671+ ('X-Launchpad-Message-For', product.owner.name),
672 ('Reply-To', reply_to),
673 ]
674 self.assertContentEqual(expected_headers, headers.items())
675@@ -458,6 +459,7 @@
676 ('X-Launchpad-Project', '%s (%s)' %
677 (product.displayname, product.name)),
678 ('X-Launchpad-Message-Rationale', 'Maintainer'),
679+ ('X-Launchpad-Message-For', product.owner.name),
680 ]
681 self.assertContentEqual(expected_headers, headers.items())
682
683
684=== modified file 'lib/lp/services/mail/basemailer.py'
685--- lib/lp/services/mail/basemailer.py 2015-08-27 14:34:21 +0000
686+++ lib/lp/services/mail/basemailer.py 2015-09-11 12:44:20 +0000
687@@ -123,6 +123,8 @@
688 reason, rationale = self._recipients.getReason(email)
689 headers = OrderedDict()
690 headers['X-Launchpad-Message-Rationale'] = reason.mail_header
691+ if reason.subscriber.name is not None:
692+ headers['X-Launchpad-Message-For'] = reason.subscriber.name
693 if self.notification_type is not None:
694 headers['X-Launchpad-Notification-Type'] = self.notification_type
695 reply_to = self._getReplyToAddress(email, recipient)
696
697=== modified file 'lib/lp/services/mail/notificationrecipientset.py'
698--- lib/lp/services/mail/notificationrecipientset.py 2015-08-26 13:41:21 +0000
699+++ lib/lp/services/mail/notificationrecipientset.py 2015-09-11 12:44:20 +0000
700@@ -29,6 +29,7 @@
701 correspond to a real Person.
702 """
703
704+ name = None
705 displayname = None
706 is_team = False
707 expanded_notification_footers = False
708
709=== modified file 'lib/lp/services/mail/tests/test_basemailer.py'
710--- lib/lp/services/mail/tests/test_basemailer.py 2015-08-23 22:53:55 +0000
711+++ lib/lp/services/mail/tests/test_basemailer.py 2015-09-11 12:44:20 +0000
712@@ -168,7 +168,8 @@
713 def test_generateEmail_append_expanded_footer(self):
714 # Recipients with expanded_notification_footers receive an expanded
715 # footer on messages.
716- fake_to = self.factory.makePerson(email='to@example.com')
717+ fake_to = self.factory.makePerson(
718+ name='to-person', email='to@example.com')
719 fake_to.expanded_notification_footers = True
720 recipients = {fake_to: FakeSubscription()}
721 mailer = BaseMailerSubclass(
722@@ -179,6 +180,7 @@
723 ctrl.body, EndsWith(
724 '\n-- \n'
725 'Launchpad-Message-Rationale: pete\n'
726+ 'Launchpad-Message-For: to-person\n'
727 'Launchpad-Notification-Type: test\n'))
728
729 def test_sendall_single_failure_doesnt_kill_all(self):
730
731=== modified file 'lib/lp/snappy/tests/test_snapbuild.py'
732--- lib/lp/snappy/tests/test_snapbuild.py 2015-08-03 13:20:45 +0000
733+++ lib/lp/snappy/tests/test_snapbuild.py 2015-09-11 12:44:20 +0000
734@@ -255,6 +255,7 @@
735 "unstable" % build.id, subject)
736 self.assertEqual(
737 "Requester", notification["X-Launchpad-Message-Rationale"])
738+ self.assertEqual(person.name, notification["X-Launchpad-Message-For"])
739 self.assertEqual(
740 "snap-build-status",
741 notification["X-Launchpad-Notification-Type"])
742
743=== modified file 'lib/lp/soyuz/mail/tests/test_packageupload.py'
744--- lib/lp/soyuz/mail/tests/test_packageupload.py 2015-08-25 23:27:09 +0000
745+++ lib/lp/soyuz/mail/tests/test_packageupload.py 2015-09-11 12:44:20 +0000
746@@ -132,6 +132,7 @@
747 "X-Launchpad-Message-Rationale": Equals("Announcement"),
748 "X-Launchpad-Notification-Type": Equals("package-upload"),
749 }))
750+ self.assertNotIn("X-Launchpad-Message-For", dict(notifications[1]))
751
752 def test_forAction_announce_from_person_override_with_unicode_names(self):
753 # PackageUploadMailer.forAction() takes an optional
754@@ -164,6 +165,7 @@
755 "X-Launchpad-Message-Rationale": Equals("Announcement"),
756 "X-Launchpad-Notification-Type": Equals("package-upload"),
757 }))
758+ self.assertNotIn("X-Launchpad-Message-For", dict(notifications[1]))
759
760 def test_forAction_bcc_to_derivatives_list(self):
761 # PackageUploadMailer.forAction() will BCC the announcement email to
762@@ -186,6 +188,7 @@
763 "X-Launchpad-Message-Rationale": Equals("Announcement"),
764 "X-Launchpad-Notification-Type": Equals("package-upload"),
765 }))
766+ self.assertNotIn("X-Launchpad-Message-For", dict(notifications[1]))
767
768 def test_fetch_information_spr_multiple_changelogs(self):
769 # If previous_version is passed the "changelog" entry in the
770@@ -454,15 +457,17 @@
771 "accepted", blamer, spr, [], [], archive, distroseries,
772 PackagePublishingPocket.RELEASE, changes=changes)
773 recipients = dict(mailer._recipients.getRecipientPersons())
774- for email, rationale in (
775- (blamer.preferredemail.email, "Requester"),
776- ("maintainer@example.com", "Maintainer"),
777- ("changer@example.com", "Changed-By")):
778+ for person, rationale in (
779+ (blamer, "Requester"),
780+ (maintainer, "Maintainer"),
781+ (changer, "Changed-By")):
782+ email = person.preferredemail.email
783 headers = mailer._getHeaders(email, recipients[email])
784 self.assertThat(
785 headers,
786 ContainsDict({
787 "X-Launchpad-Message-Rationale": Equals(rationale),
788+ "X-Launchpad-Message-For": Equals(person.name),
789 "X-Launchpad-Notification-Type": Equals("package-upload"),
790 "X-Katie": Equals("Launchpad actually"),
791 "X-Launchpad-Archive": Equals(archive.reference),
792@@ -497,14 +502,16 @@
793 "accepted", blamer, spr, [], [], archive, distroseries,
794 PackagePublishingPocket.RELEASE, changes=changes)
795 recipients = dict(mailer._recipients.getRecipientPersons())
796- for email, rationale in (
797- (blamer.preferredemail.email, "Requester"),
798- (uploader.preferredemail.email, "PPA Uploader")):
799+ for person, rationale in (
800+ (blamer, "Requester"),
801+ (uploader, "PPA Uploader")):
802+ email = person.preferredemail.email
803 headers = mailer._getHeaders(email, recipients[email])
804 self.assertThat(
805 headers,
806 ContainsDict({
807 "X-Launchpad-Message-Rationale": Equals(rationale),
808+ "X-Launchpad-Message-For": Equals(person.name),
809 "X-Launchpad-Notification-Type": Equals("package-upload"),
810 "X-Katie": Equals("Launchpad actually"),
811 "X-Launchpad-Archive": Equals(archive.reference),
812
813=== modified file 'lib/lp/soyuz/tests/test_build_notify.py'
814--- lib/lp/soyuz/tests/test_build_notify.py 2015-09-04 12:19:07 +0000
815+++ lib/lp/soyuz/tests/test_build_notify.py 2015-09-11 12:44:20 +0000
816@@ -105,10 +105,13 @@
817 format_address_for_person(recipient), notification['To'])
818 if reason == "buildd-admin":
819 rationale = "Buildd-Admin @launchpad-buildd-admins"
820+ expected_for = "launchpad-buildd-admins"
821 else:
822 rationale = reason.title()
823+ expected_for = recipient.name
824 self.assertEqual(
825 rationale, notification['X-Launchpad-Message-Rationale'])
826+ self.assertEqual(expected_for, notification['X-Launchpad-Message-For'])
827 self.assertEqual(
828 'package-build-status',
829 notification['X-Launchpad-Notification-Type'])
830
831=== modified file 'lib/lp/soyuz/tests/test_livefsbuild.py'
832--- lib/lp/soyuz/tests/test_livefsbuild.py 2015-08-03 12:59:18 +0000
833+++ lib/lp/soyuz/tests/test_livefsbuild.py 2015-09-11 12:44:20 +0000
834@@ -1,4 +1,4 @@
835-# Copyright 2014 Canonical Ltd. This software is licensed under the
836+# Copyright 2014-2015 Canonical Ltd. This software is licensed under the
837 # GNU Affero General Public License version 3 (see the file LICENSE).
838
839 """Test live filesystem build features."""
840@@ -257,6 +257,7 @@
841 "unstable" % build.id, subject)
842 self.assertEqual(
843 "Requester", notification["X-Launchpad-Message-Rationale"])
844+ self.assertEqual(person.name, notification["X-Launchpad-Message-For"])
845 self.assertEqual(
846 "livefs-build-status",
847 notification["X-Launchpad-Notification-Type"])
848
849=== modified file 'lib/lp/testing/mail_helpers.py'
850--- lib/lp/testing/mail_helpers.py 2015-09-02 02:46:18 +0000
851+++ lib/lp/testing/mail_helpers.py 2015-09-11 12:44:20 +0000
852@@ -60,8 +60,8 @@
853
854
855 def print_emails(include_reply_to=False, group_similar=False,
856- include_rationale=False, notifications=None,
857- include_notification_type=False):
858+ include_rationale=False, include_for=False,
859+ notifications=None, include_notification_type=False):
860 """Pop all messages from stub.test_emails and print them with
861 their recipients.
862
863@@ -76,6 +76,7 @@
864 :param group_similar: Group messages sent to multiple recipients if True.
865 :param include_rationale: Include the X-Launchpad-Message-Rationale
866 header.
867+ :param include_for: Include the X-Launchpad-Message-For header.
868 :param notifications: Use the provided list of notifications instead of
869 the stack.
870 :param include_notification_type: Include the
871@@ -106,8 +107,10 @@
872 print 'Reply-To:', message['Reply-To']
873 rationale_header = 'X-Launchpad-Message-Rationale'
874 if include_rationale and rationale_header in message:
875- print (
876- '%s: %s' % (rationale_header, message[rationale_header]))
877+ print '%s: %s' % (rationale_header, message[rationale_header])
878+ for_header = 'X-Launchpad-Message-For'
879+ if include_for and for_header in message:
880+ print '%s: %s' % (for_header, message[for_header])
881 notification_type_header = 'X-Launchpad-Notification-Type'
882 if include_notification_type and notification_type_header in message:
883 print '%s: %s' % (
884@@ -118,11 +121,12 @@
885
886
887 def print_distinct_emails(include_reply_to=False, include_rationale=True,
888- include_notification_type=True):
889+ include_for=False, include_notification_type=True):
890 """A convenient shortcut for `print_emails`(group_similar=True)."""
891 return print_emails(group_similar=True,
892 include_reply_to=include_reply_to,
893 include_rationale=include_rationale,
894+ include_for=include_for,
895 include_notification_type=include_notification_type)
896
897