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
=== modified file 'lib/lp/bugs/doc/bugnotification-email.txt'
--- lib/lp/bugs/doc/bugnotification-email.txt 2015-07-21 09:04:01 +0000
+++ lib/lp/bugs/doc/bugnotification-email.txt 2015-07-31 14:49:19 +0000
@@ -535,18 +535,19 @@
535535
536The build() method of a builder accepts a number of parameters and returns536The build() method of a builder accepts a number of parameters and returns
537an instance of email.mime.text.MIMEText. The most basic invocation of this537an instance of email.mime.text.MIMEText. The most basic invocation of this
538method requires a from address, a to address, a body, a subject and a538method requires a from address, a to person, a body, a subject and a sending
539sending date for the mail.539date for the mail.
540540
541 >>> from datetime import datetime541 >>> from datetime import datetime
542 >>> import pytz542 >>> import pytz
543543
544 >>> from_address = get_bugmail_from_address(lp_janitor, bug_four)544 >>> from_address = get_bugmail_from_address(lp_janitor, bug_four)
545 >>> to_person = getUtility(IPersonSet).getByEmail('foo.bar@canonical.com')
545 >>> sending_date = pytz.timezone('Europe/Prague').localize(546 >>> sending_date = pytz.timezone('Europe/Prague').localize(
546 ... datetime(2008, 5, 20, 11, 5, 47))547 ... datetime(2008, 5, 20, 11, 5, 47))
547548
548 >>> notification_email = bug_four_notification_builder.build(549 >>> notification_email = bug_four_notification_builder.build(
549 ... from_address, 'foo.bar@canonical.com',550 ... from_address, to_person,
550 ... "A test body.", "A test subject.", sending_date)551 ... "A test body.", "A test subject.", sending_date)
551552
552The fields of the generated notification email will be set according to553The fields of the generated notification email will be set according to
@@ -572,7 +573,7 @@
572references and message_id.573references and message_id.
573574
574 >>> notification_email = bug_four_notification_builder.build(575 >>> notification_email = bug_four_notification_builder.build(
575 ... from_address, 'foo.bar@canonical.com',576 ... from_address, to_person,
576 ... "A test body.", "A test subject.", sending_date,577 ... "A test body.", "A test subject.", sending_date,
577 ... rationale='Because-I-said-so',578 ... rationale='Because-I-said-so',
578 ... references=['<12345@launchpad.net>'],579 ... references=['<12345@launchpad.net>'],
@@ -598,7 +599,7 @@
598The message subject will always have [Bug <bug_id>] prepended to it.599The message subject will always have [Bug <bug_id>] prepended to it.
599600
600 >>> notification_email = bug_four_notification_builder.build(601 >>> notification_email = bug_four_notification_builder.build(
601 ... from_address, 'foo.bar@canonical.com',602 ... from_address, to_person,
602 ... "A test body.", "Yet another message", sending_date)603 ... "A test body.", "Yet another message", sending_date)
603604
604 >>> print notification_email['Subject']605 >>> print notification_email['Subject']
@@ -608,8 +609,7 @@
608<bug_id>].609<bug_id>].
609610
610 >>> notification_email = bug_four_notification_builder.build(611 >>> notification_email = bug_four_notification_builder.build(
611 ... from_address, 'foo.bar@canonical.com',612 ... from_address, to_person, "A test body.", None, sending_date)
612 ... "A test body.", None, sending_date)
613613
614 >>> print notification_email['Subject']614 >>> print notification_email['Subject']
615 [Bug 4]615 [Bug 4]
616616
=== modified file 'lib/lp/bugs/mail/bugnotificationbuilder.py'
--- lib/lp/bugs/mail/bugnotificationbuilder.py 2015-07-13 16:14:46 +0000
+++ lib/lp/bugs/mail/bugnotificationbuilder.py 2015-07-31 14:49:19 +0000
@@ -16,6 +16,7 @@
16import rfc82216import rfc822
1717
18from zope.component import getUtility18from zope.component import getUtility
19from zope.security.proxy import removeSecurityProxy
1920
20from lp.app.enums import (21from lp.app.enums import (
21 PRIVATE_INFORMATION_TYPES,22 PRIVATE_INFORMATION_TYPES,
@@ -25,7 +26,10 @@
25from lp.services.config import config26from lp.services.config import config
26from lp.services.helpers import shortlist27from lp.services.helpers import shortlist
27from lp.services.identity.interfaces.emailaddress import IEmailAddressSet28from lp.services.identity.interfaces.emailaddress import IEmailAddressSet
28from lp.services.mail.sendmail import format_address29from lp.services.mail.sendmail import (
30 append_footer,
31 format_address,
32 )
2933
3034
31def format_rfc2822_date(date):35def format_rfc2822_date(date):
@@ -161,12 +165,13 @@
161 self.common_headers.append(165 self.common_headers.append(
162 ('X-Launchpad-Bug-Duplicate', str(bug.duplicateof.id)))166 ('X-Launchpad-Bug-Duplicate', str(bug.duplicateof.id)))
163167
164 def build(self, from_address, to_address, body, subject, email_date,168 def build(self, from_address, to_person, body, subject, email_date,
165 rationale=None, references=None, message_id=None, filters=None):169 rationale=None, references=None, message_id=None, filters=None):
166 """Construct the notification.170 """Construct the notification.
167171
168 :param from_address: The From address of the notification.172 :param from_address: The From address of the notification.
169 :param to_address: The To address for the notification.173 :param to_person: The `IPerson` to use as the To address for the
174 notification.
170 :param body: The body text of the notification.175 :param body: The body text of the notification.
171 :type body: unicode176 :type body: unicode
172 :param subject: The Subject of the notification.177 :param subject: The Subject of the notification.
@@ -178,34 +183,47 @@
178183
179 :return: An `email.mime.text.MIMEText` object.184 :return: An `email.mime.text.MIMEText` object.
180 """185 """
181 message = MIMEText(body.encode('utf8'), 'plain', 'utf8')186 headers = [
182 message['Date'] = format_rfc2822_date(email_date)187 ('Date', format_rfc2822_date(email_date)),
183 message['From'] = from_address188 ('From', from_address),
184 message['To'] = to_address189 ('To', str(removeSecurityProxy(to_person).preferredemail.email)),
190 ]
185191
186 # Add the common headers.192 # Add the common headers.
187 for header in self.common_headers:193 headers.extend(self.common_headers)
188 message.add_header(*header)
189194
190 if references:195 if references:
191 message['References'] = ' '.join(references)196 headers.append(('References', ' '.join(references)))
192 if message_id is not None:197 if message_id is not None:
193 message['Message-Id'] = message_id198 headers.append(('Message-Id', message_id))
194199
195 subject_prefix = "[Bug %d]" % self.bug.id200 subject_prefix = "[Bug %d]" % self.bug.id
196 if subject is None:201 if subject is None:
197 message['Subject'] = subject_prefix202 headers.append(('Subject', subject_prefix))
198 elif subject_prefix in subject:203 elif subject_prefix in subject:
199 message['Subject'] = subject204 headers.append(('Subject', subject))
200 else:205 else:
201 message['Subject'] = "%s %s" % (subject_prefix, subject)206 headers.append(('Subject', "%s %s" % (subject_prefix, subject)))
202207
203 if rationale is not None:208 if rationale is not None:
204 message.add_header('X-Launchpad-Message-Rationale', rationale)209 headers.append(('X-Launchpad-Message-Rationale', rationale))
205210
206 if filters is not None:211 if filters is not None:
207 for filter in filters:212 for filter in filters:
208 message.add_header(213 headers.append(('X-Launchpad-Subscription', filter))
209 'X-Launchpad-Subscription', filter)214
210215 # XXX cjwatson 2015-07-31: This is cloned-and-hacked from
216 # BaseMailer; it would ultimately be better to convert bug
217 # notifications to that framework.
218 if to_person.expanded_notification_footers:
219 lines = []
220 for key, value in headers:
221 if key.startswith('X-Launchpad-'):
222 lines.append('%s: %s\n' % (key[2:], value))
223 if lines:
224 body = append_footer(body, ''.join(lines))
225
226 message = MIMEText(body.encode('utf8'), 'plain', 'utf8')
227 for header in headers:
228 message.add_header(*header)
211 return message229 return message
212230
=== modified file 'lib/lp/bugs/mail/tests/test_bugnotificationbuilder.py'
--- lib/lp/bugs/mail/tests/test_bugnotificationbuilder.py 2015-01-26 01:31:33 +0000
+++ lib/lp/bugs/mail/tests/test_bugnotificationbuilder.py 2015-07-31 14:49:19 +0000
@@ -1,4 +1,4 @@
1# Copyright 2011 Canonical Ltd. This software is licensed under the1# Copyright 2011-2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for BugNotificationBuilder email construction."""4"""Tests for BugNotificationBuilder email construction."""
@@ -27,7 +27,7 @@
27 def test_build_filters_empty(self):27 def test_build_filters_empty(self):
28 """Filters are added."""28 """Filters are added."""
29 utc_now = datetime.now(pytz.UTC)29 utc_now = datetime.now(pytz.UTC)
30 message = self.builder.build('from', 'to', 'body', 'subject',30 message = self.builder.build('from', self.bug.owner, 'body', 'subject',
31 utc_now, filters=[])31 utc_now, filters=[])
32 self.assertIs(None,32 self.assertIs(None,
33 message.get("X-Launchpad-Subscription", None))33 message.get("X-Launchpad-Subscription", None))
@@ -35,7 +35,7 @@
35 def test_build_filters_single(self):35 def test_build_filters_single(self):
36 """Filters are added."""36 """Filters are added."""
37 utc_now = datetime.now(pytz.UTC)37 utc_now = datetime.now(pytz.UTC)
38 message = self.builder.build('from', 'to', 'body', 'subject',38 message = self.builder.build('from', self.bug.owner, 'body', 'subject',
39 utc_now, filters=[u"Testing filter"])39 utc_now, filters=[u"Testing filter"])
40 self.assertContentEqual(40 self.assertContentEqual(
41 [u"Testing filter"],41 [u"Testing filter"],
@@ -45,7 +45,7 @@
45 """Filters are added."""45 """Filters are added."""
46 utc_now = datetime.now(pytz.UTC)46 utc_now = datetime.now(pytz.UTC)
47 message = self.builder.build(47 message = self.builder.build(
48 'from', 'to', 'body', 'subject', utc_now,48 'from', self.bug.owner, 'body', 'subject', utc_now,
49 filters=[u"Testing filter", u"Second testing filter"])49 filters=[u"Testing filter", u"Second testing filter"])
50 self.assertContentEqual(50 self.assertContentEqual(
51 [u"Testing filter", u"Second testing filter"],51 [u"Testing filter", u"Second testing filter"],
@@ -54,7 +54,26 @@
54 def test_mails_contain_notification_type_header(self):54 def test_mails_contain_notification_type_header(self):
55 utc_now = datetime.now(pytz.UTC)55 utc_now = datetime.now(pytz.UTC)
56 message = self.builder.build(56 message = self.builder.build(
57 'from', 'to', 'body', 'subject', utc_now, filters=[])57 'from', self.bug.owner, 'body', 'subject', utc_now, filters=[])
58 self.assertEqual(58 self.assertEqual(
59 "bug", message.get("X-Launchpad-Notification-Type", None))59 "bug", message.get("X-Launchpad-Notification-Type", None))
6060
61 def test_mails_no_expanded_footer(self):
62 # Recipients without expanded_notification_footers do not receive an
63 # expanded footer on messages.
64 utc_now = datetime.now(pytz.UTC)
65 message = self.builder.build(
66 'from', self.bug.owner, 'body', 'subject', utc_now, filters=[])
67 self.assertNotIn(
68 "Launchpad-Notification-Type", message.get_payload(decode=True))
69
70 def test_mails_append_expanded_footer(self):
71 # Recipients with expanded_notification_footers receive an expanded
72 # footer on messages.
73 utc_now = datetime.now(pytz.UTC)
74 self.bug.owner.expanded_notification_footers = True
75 message = self.builder.build(
76 'from', self.bug.owner, 'body', 'subject', utc_now, filters=[])
77 self.assertIn(
78 "\n-- \nLaunchpad-Notification-Type: bug\n",
79 message.get_payload(decode=True))
6180
=== modified file 'lib/lp/bugs/scripts/bugnotification.py'
--- lib/lp/bugs/scripts/bugnotification.py 2015-07-06 17:27:09 +0000
+++ lib/lp/bugs/scripts/bugnotification.py 2015-07-31 14:49:19 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Functions related to sending bug notifications."""4"""Functions related to sending bug notifications."""
@@ -182,7 +182,6 @@
182 recipients.items(), key=lambda t: t[0].preferredemail.email)182 recipients.items(), key=lambda t: t[0].preferredemail.email)
183183
184 for email_person, data in sorted_recipients:184 for email_person, data in sorted_recipients:
185 address = str(email_person.preferredemail.email)
186 # Choosing the first source is a bit arbitrary, but it185 # Choosing the first source is a bit arbitrary, but it
187 # is simple for the user to understand. We may want to reconsider186 # is simple for the user to understand. We may want to reconsider
188 # this in the future.187 # this in the future.
@@ -240,7 +239,7 @@
240 body_template = get_email_template(email_template, 'bugs')239 body_template = get_email_template(email_template, 'bugs')
241 body = (body_template % body_data).strip()240 body = (body_template % body_data).strip()
242 msg = bug_notification_builder.build(241 msg = bug_notification_builder.build(
243 from_address, address, body, subject, email_date,242 from_address, email_person, body, subject, email_date,
244 rationale, references, msgid, filters=data['filter descriptions'])243 rationale, references, msgid, filters=data['filter descriptions'])
245 messages.append(msg)244 messages.append(msg)
246245
247246
=== modified file 'lib/lp/bugs/subscribers/bug.py'
--- lib/lp/bugs/subscribers/bug.py 2013-11-29 14:12:13 +0000
+++ lib/lp/bugs/subscribers/bug.py 2015-07-31 14:49:19 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type4__metaclass__ = type
@@ -28,9 +28,9 @@
28from lp.bugs.mail.newbug import generate_bug_add_email28from lp.bugs.mail.newbug import generate_bug_add_email
29from lp.bugs.model.bug import get_also_notified_subscribers29from lp.bugs.model.bug import get_also_notified_subscribers
30from lp.registry.interfaces.person import IPerson30from lp.registry.interfaces.person import IPerson
31from lp.registry.model.person import get_recipients
31from lp.services.config import config32from lp.services.config import config
32from lp.services.database.sqlbase import block_implicit_flushes33from lp.services.database.sqlbase import block_implicit_flushes
33from lp.services.mail.helpers import get_contact_email_addresses
34from lp.services.mail.sendmail import (34from lp.services.mail.sendmail import (
35 format_address,35 format_address,
36 sendmail,36 sendmail,
@@ -203,11 +203,11 @@
203 and not event_creator.selfgenerated_bugnotifications):203 and not event_creator.selfgenerated_bugnotifications):
204 new_subs.discard(event_creator)204 new_subs.discard(event_creator)
205205
206 to_addrs = set()206 to_persons = set()
207 for new_sub in new_subs:207 for new_sub in new_subs:
208 to_addrs.update(get_contact_email_addresses(new_sub))208 to_persons.update(get_recipients(new_sub))
209209
210 if not to_addrs:210 if not to_persons:
211 return False211 return False
212212
213 from_addr = format_address(213 from_addr = format_address(
@@ -225,13 +225,13 @@
225 recipients = bug.getBugNotificationRecipients()225 recipients = bug.getBugNotificationRecipients()
226226
227 bug_notification_builder = BugNotificationBuilder(bug, event_creator)227 bug_notification_builder = BugNotificationBuilder(bug, event_creator)
228 for to_addr in sorted(to_addrs):228 for to_person in sorted(to_persons):
229 reason, rationale = recipients.getReason(to_addr)229 reason, rationale = recipients.getReason(to_person)
230 subject, contents = generate_bug_add_email(230 subject, contents = generate_bug_add_email(
231 bug, new_recipients=True, subscribed_by=subscribed_by,231 bug, new_recipients=True, subscribed_by=subscribed_by,
232 reason=reason, event_creator=event_creator)232 reason=reason, event_creator=event_creator)
233 msg = bug_notification_builder.build(233 msg = bug_notification_builder.build(
234 from_addr, to_addr, contents, subject, email_date,234 from_addr, to_person, contents, subject, email_date,
235 rationale=rationale, references=references)235 rationale=rationale, references=references)
236 sendmail(msg)236 sendmail(msg)
237237