Merge lp:~cjwatson/launchpad/bug-footer into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 17667
Proposed branch: lp:~cjwatson/launchpad/bug-footer
Merge into: lp:launchpad
Diff against target: 301 lines (+77/-41)
5 files modified
lib/lp/bugs/doc/bugnotification-email.txt (+7/-7)
lib/lp/bugs/mail/bugnotificationbuilder.py (+36/-18)
lib/lp/bugs/mail/tests/test_bugnotificationbuilder.py (+24/-5)
lib/lp/bugs/scripts/bugnotification.py (+2/-3)
lib/lp/bugs/subscribers/bug.py (+8/-8)
To merge this branch: bzr merge lp:~cjwatson/launchpad/bug-footer
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+266577@code.launchpad.net

Commit message

Make bug notifications honour PersonSettings.expanded_notification_footers.

Description of the change

Make bug notifications honour PersonSettings.expanded_notification_footers.

I tried to convert bug notifications to BaseMailer, and that would still ultimately be better, but it was Hard Work. This only took me half an hour or so and the duplication isn't all that bad.

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/bugs/doc/bugnotification-email.txt'
2--- lib/lp/bugs/doc/bugnotification-email.txt 2015-07-21 09:04:01 +0000
3+++ lib/lp/bugs/doc/bugnotification-email.txt 2015-07-31 14:49:19 +0000
4@@ -535,18 +535,19 @@
5
6 The build() method of a builder accepts a number of parameters and returns
7 an instance of email.mime.text.MIMEText. The most basic invocation of this
8-method requires a from address, a to address, a body, a subject and a
9-sending date for the mail.
10+method requires a from address, a to person, a body, a subject and a sending
11+date for the mail.
12
13 >>> from datetime import datetime
14 >>> import pytz
15
16 >>> from_address = get_bugmail_from_address(lp_janitor, bug_four)
17+ >>> to_person = getUtility(IPersonSet).getByEmail('foo.bar@canonical.com')
18 >>> sending_date = pytz.timezone('Europe/Prague').localize(
19 ... datetime(2008, 5, 20, 11, 5, 47))
20
21 >>> notification_email = bug_four_notification_builder.build(
22- ... from_address, 'foo.bar@canonical.com',
23+ ... from_address, to_person,
24 ... "A test body.", "A test subject.", sending_date)
25
26 The fields of the generated notification email will be set according to
27@@ -572,7 +573,7 @@
28 references and message_id.
29
30 >>> notification_email = bug_four_notification_builder.build(
31- ... from_address, 'foo.bar@canonical.com',
32+ ... from_address, to_person,
33 ... "A test body.", "A test subject.", sending_date,
34 ... rationale='Because-I-said-so',
35 ... references=['<12345@launchpad.net>'],
36@@ -598,7 +599,7 @@
37 The message subject will always have [Bug <bug_id>] prepended to it.
38
39 >>> notification_email = bug_four_notification_builder.build(
40- ... from_address, 'foo.bar@canonical.com',
41+ ... from_address, to_person,
42 ... "A test body.", "Yet another message", sending_date)
43
44 >>> print notification_email['Subject']
45@@ -608,8 +609,7 @@
46 <bug_id>].
47
48 >>> notification_email = bug_four_notification_builder.build(
49- ... from_address, 'foo.bar@canonical.com',
50- ... "A test body.", None, sending_date)
51+ ... from_address, to_person, "A test body.", None, sending_date)
52
53 >>> print notification_email['Subject']
54 [Bug 4]
55
56=== modified file 'lib/lp/bugs/mail/bugnotificationbuilder.py'
57--- lib/lp/bugs/mail/bugnotificationbuilder.py 2015-07-13 16:14:46 +0000
58+++ lib/lp/bugs/mail/bugnotificationbuilder.py 2015-07-31 14:49:19 +0000
59@@ -16,6 +16,7 @@
60 import rfc822
61
62 from zope.component import getUtility
63+from zope.security.proxy import removeSecurityProxy
64
65 from lp.app.enums import (
66 PRIVATE_INFORMATION_TYPES,
67@@ -25,7 +26,10 @@
68 from lp.services.config import config
69 from lp.services.helpers import shortlist
70 from lp.services.identity.interfaces.emailaddress import IEmailAddressSet
71-from lp.services.mail.sendmail import format_address
72+from lp.services.mail.sendmail import (
73+ append_footer,
74+ format_address,
75+ )
76
77
78 def format_rfc2822_date(date):
79@@ -161,12 +165,13 @@
80 self.common_headers.append(
81 ('X-Launchpad-Bug-Duplicate', str(bug.duplicateof.id)))
82
83- def build(self, from_address, to_address, body, subject, email_date,
84+ def build(self, from_address, to_person, body, subject, email_date,
85 rationale=None, references=None, message_id=None, filters=None):
86 """Construct the notification.
87
88 :param from_address: The From address of the notification.
89- :param to_address: The To address for the notification.
90+ :param to_person: The `IPerson` to use as the To address for the
91+ notification.
92 :param body: The body text of the notification.
93 :type body: unicode
94 :param subject: The Subject of the notification.
95@@ -178,34 +183,47 @@
96
97 :return: An `email.mime.text.MIMEText` object.
98 """
99- message = MIMEText(body.encode('utf8'), 'plain', 'utf8')
100- message['Date'] = format_rfc2822_date(email_date)
101- message['From'] = from_address
102- message['To'] = to_address
103+ headers = [
104+ ('Date', format_rfc2822_date(email_date)),
105+ ('From', from_address),
106+ ('To', str(removeSecurityProxy(to_person).preferredemail.email)),
107+ ]
108
109 # Add the common headers.
110- for header in self.common_headers:
111- message.add_header(*header)
112+ headers.extend(self.common_headers)
113
114 if references:
115- message['References'] = ' '.join(references)
116+ headers.append(('References', ' '.join(references)))
117 if message_id is not None:
118- message['Message-Id'] = message_id
119+ headers.append(('Message-Id', message_id))
120
121 subject_prefix = "[Bug %d]" % self.bug.id
122 if subject is None:
123- message['Subject'] = subject_prefix
124+ headers.append(('Subject', subject_prefix))
125 elif subject_prefix in subject:
126- message['Subject'] = subject
127+ headers.append(('Subject', subject))
128 else:
129- message['Subject'] = "%s %s" % (subject_prefix, subject)
130+ headers.append(('Subject', "%s %s" % (subject_prefix, subject)))
131
132 if rationale is not None:
133- message.add_header('X-Launchpad-Message-Rationale', rationale)
134+ headers.append(('X-Launchpad-Message-Rationale', rationale))
135
136 if filters is not None:
137 for filter in filters:
138- message.add_header(
139- 'X-Launchpad-Subscription', filter)
140-
141+ headers.append(('X-Launchpad-Subscription', filter))
142+
143+ # XXX cjwatson 2015-07-31: This is cloned-and-hacked from
144+ # BaseMailer; it would ultimately be better to convert bug
145+ # notifications to that framework.
146+ if to_person.expanded_notification_footers:
147+ lines = []
148+ for key, value in headers:
149+ if key.startswith('X-Launchpad-'):
150+ lines.append('%s: %s\n' % (key[2:], value))
151+ if lines:
152+ body = append_footer(body, ''.join(lines))
153+
154+ message = MIMEText(body.encode('utf8'), 'plain', 'utf8')
155+ for header in headers:
156+ message.add_header(*header)
157 return message
158
159=== modified file 'lib/lp/bugs/mail/tests/test_bugnotificationbuilder.py'
160--- lib/lp/bugs/mail/tests/test_bugnotificationbuilder.py 2015-01-26 01:31:33 +0000
161+++ lib/lp/bugs/mail/tests/test_bugnotificationbuilder.py 2015-07-31 14:49:19 +0000
162@@ -1,4 +1,4 @@
163-# Copyright 2011 Canonical Ltd. This software is licensed under the
164+# Copyright 2011-2015 Canonical Ltd. This software is licensed under the
165 # GNU Affero General Public License version 3 (see the file LICENSE).
166
167 """Tests for BugNotificationBuilder email construction."""
168@@ -27,7 +27,7 @@
169 def test_build_filters_empty(self):
170 """Filters are added."""
171 utc_now = datetime.now(pytz.UTC)
172- message = self.builder.build('from', 'to', 'body', 'subject',
173+ message = self.builder.build('from', self.bug.owner, 'body', 'subject',
174 utc_now, filters=[])
175 self.assertIs(None,
176 message.get("X-Launchpad-Subscription", None))
177@@ -35,7 +35,7 @@
178 def test_build_filters_single(self):
179 """Filters are added."""
180 utc_now = datetime.now(pytz.UTC)
181- message = self.builder.build('from', 'to', 'body', 'subject',
182+ message = self.builder.build('from', self.bug.owner, 'body', 'subject',
183 utc_now, filters=[u"Testing filter"])
184 self.assertContentEqual(
185 [u"Testing filter"],
186@@ -45,7 +45,7 @@
187 """Filters are added."""
188 utc_now = datetime.now(pytz.UTC)
189 message = self.builder.build(
190- 'from', 'to', 'body', 'subject', utc_now,
191+ 'from', self.bug.owner, 'body', 'subject', utc_now,
192 filters=[u"Testing filter", u"Second testing filter"])
193 self.assertContentEqual(
194 [u"Testing filter", u"Second testing filter"],
195@@ -54,7 +54,26 @@
196 def test_mails_contain_notification_type_header(self):
197 utc_now = datetime.now(pytz.UTC)
198 message = self.builder.build(
199- 'from', 'to', 'body', 'subject', utc_now, filters=[])
200+ 'from', self.bug.owner, 'body', 'subject', utc_now, filters=[])
201 self.assertEqual(
202 "bug", message.get("X-Launchpad-Notification-Type", None))
203
204+ def test_mails_no_expanded_footer(self):
205+ # Recipients without expanded_notification_footers do not receive an
206+ # expanded footer on messages.
207+ utc_now = datetime.now(pytz.UTC)
208+ message = self.builder.build(
209+ 'from', self.bug.owner, 'body', 'subject', utc_now, filters=[])
210+ self.assertNotIn(
211+ "Launchpad-Notification-Type", message.get_payload(decode=True))
212+
213+ def test_mails_append_expanded_footer(self):
214+ # Recipients with expanded_notification_footers receive an expanded
215+ # footer on messages.
216+ utc_now = datetime.now(pytz.UTC)
217+ self.bug.owner.expanded_notification_footers = True
218+ message = self.builder.build(
219+ 'from', self.bug.owner, 'body', 'subject', utc_now, filters=[])
220+ self.assertIn(
221+ "\n-- \nLaunchpad-Notification-Type: bug\n",
222+ message.get_payload(decode=True))
223
224=== modified file 'lib/lp/bugs/scripts/bugnotification.py'
225--- lib/lp/bugs/scripts/bugnotification.py 2015-07-06 17:27:09 +0000
226+++ lib/lp/bugs/scripts/bugnotification.py 2015-07-31 14:49:19 +0000
227@@ -1,4 +1,4 @@
228-# Copyright 2009 Canonical Ltd. This software is licensed under the
229+# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
230 # GNU Affero General Public License version 3 (see the file LICENSE).
231
232 """Functions related to sending bug notifications."""
233@@ -182,7 +182,6 @@
234 recipients.items(), key=lambda t: t[0].preferredemail.email)
235
236 for email_person, data in sorted_recipients:
237- address = str(email_person.preferredemail.email)
238 # Choosing the first source is a bit arbitrary, but it
239 # is simple for the user to understand. We may want to reconsider
240 # this in the future.
241@@ -240,7 +239,7 @@
242 body_template = get_email_template(email_template, 'bugs')
243 body = (body_template % body_data).strip()
244 msg = bug_notification_builder.build(
245- from_address, address, body, subject, email_date,
246+ from_address, email_person, body, subject, email_date,
247 rationale, references, msgid, filters=data['filter descriptions'])
248 messages.append(msg)
249
250
251=== modified file 'lib/lp/bugs/subscribers/bug.py'
252--- lib/lp/bugs/subscribers/bug.py 2013-11-29 14:12:13 +0000
253+++ lib/lp/bugs/subscribers/bug.py 2015-07-31 14:49:19 +0000
254@@ -1,4 +1,4 @@
255-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
256+# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
257 # GNU Affero General Public License version 3 (see the file LICENSE).
258
259 __metaclass__ = type
260@@ -28,9 +28,9 @@
261 from lp.bugs.mail.newbug import generate_bug_add_email
262 from lp.bugs.model.bug import get_also_notified_subscribers
263 from lp.registry.interfaces.person import IPerson
264+from lp.registry.model.person import get_recipients
265 from lp.services.config import config
266 from lp.services.database.sqlbase import block_implicit_flushes
267-from lp.services.mail.helpers import get_contact_email_addresses
268 from lp.services.mail.sendmail import (
269 format_address,
270 sendmail,
271@@ -203,11 +203,11 @@
272 and not event_creator.selfgenerated_bugnotifications):
273 new_subs.discard(event_creator)
274
275- to_addrs = set()
276+ to_persons = set()
277 for new_sub in new_subs:
278- to_addrs.update(get_contact_email_addresses(new_sub))
279+ to_persons.update(get_recipients(new_sub))
280
281- if not to_addrs:
282+ if not to_persons:
283 return False
284
285 from_addr = format_address(
286@@ -225,13 +225,13 @@
287 recipients = bug.getBugNotificationRecipients()
288
289 bug_notification_builder = BugNotificationBuilder(bug, event_creator)
290- for to_addr in sorted(to_addrs):
291- reason, rationale = recipients.getReason(to_addr)
292+ for to_person in sorted(to_persons):
293+ reason, rationale = recipients.getReason(to_person)
294 subject, contents = generate_bug_add_email(
295 bug, new_recipients=True, subscribed_by=subscribed_by,
296 reason=reason, event_creator=event_creator)
297 msg = bug_notification_builder.build(
298- from_addr, to_addr, contents, subject, email_date,
299+ from_addr, to_person, contents, subject, email_date,
300 rationale=rationale, references=references)
301 sendmail(msg)
302