Merge lp:~allenap/launchpad/refactor-mailnotification into lp:launchpad
- refactor-mailnotification
- Merge into devel
Proposed by
Gavin Panella
Status: | Merged |
---|---|
Approved by: | Aaron Bentley |
Approved revision: | no longer in the source branch. |
Merged at revision: | 11417 |
Proposed branch: | lp:~allenap/launchpad/refactor-mailnotification |
Merge into: | lp:launchpad |
Prerequisite: | lp:~allenap/launchpad/refactor-get-email-notifications |
Diff against target: |
1160 lines (+465/-471) 11 files modified
lib/canonical/launchpad/emailtemplates/notify-unhandled-email.txt (+0/-7) lib/canonical/launchpad/mailnotification.py (+4/-434) lib/canonical/launchpad/subscribers/karma.py (+3/-1) lib/lp/bugs/configure.zcml (+6/-9) lib/lp/bugs/doc/bugnotification-email.txt (+3/-6) lib/lp/bugs/doc/bugsubscription.txt (+2/-4) lib/lp/bugs/mail/newbug.py (+95/-0) lib/lp/bugs/scripts/bugnotification.py (+2/-4) lib/lp/bugs/subscribers/bug.py (+268/-5) lib/lp/bugs/subscribers/bugcreation.py (+3/-1) lib/lp/bugs/subscribers/bugtask.py (+79/-0) |
To merge this branch: | bzr merge lp:~allenap/launchpad/refactor-mailnotification |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Aaron Bentley (community) | Approve | ||
Review via email: mp+32923@code.launchpad.net |
Commit message
Move most of the remaining bugs related code out of c.l.mailnotific
Description of the change
Moves almost all of the remaining bug related code out of c/l/mailnotific
To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote : | # |
Revision history for this message
Aaron Bentley (abentley) wrote : | # |
Please fix the copyright date on newbug.py. Otherwise, this looks fine.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed file 'lib/canonical/launchpad/emailtemplates/notify-unhandled-email.txt' | |||
2 | --- lib/canonical/launchpad/emailtemplates/notify-unhandled-email.txt 2005-10-31 18:29:12 +0000 | |||
3 | +++ lib/canonical/launchpad/emailtemplates/notify-unhandled-email.txt 1970-01-01 00:00:00 +0000 | |||
4 | @@ -1,7 +0,0 @@ | |||
5 | 1 | The following email was unhandled: | ||
6 | 2 | |||
7 | 3 | %(url)s | ||
8 | 4 | |||
9 | 5 | Error message: | ||
10 | 6 | |||
11 | 7 | %(error_msg)s | ||
12 | 8 | 0 | ||
13 | === modified file 'lib/canonical/launchpad/mailnotification.py' | |||
14 | --- lib/canonical/launchpad/mailnotification.py 2010-08-20 20:31:18 +0000 | |||
15 | +++ lib/canonical/launchpad/mailnotification.py 2010-08-23 20:18:06 +0000 | |||
16 | @@ -8,7 +8,6 @@ | |||
17 | 8 | 8 | ||
18 | 9 | __metaclass__ = type | 9 | __metaclass__ = type |
19 | 10 | 10 | ||
20 | 11 | import datetime | ||
21 | 12 | from difflib import unified_diff | 11 | from difflib import unified_diff |
22 | 13 | from email.Header import Header | 12 | from email.Header import Header |
23 | 14 | from email.MIMEMessage import MIMEMessage | 13 | from email.MIMEMessage import MIMEMessage |
24 | @@ -18,7 +17,6 @@ | |||
25 | 18 | formataddr, | 17 | formataddr, |
26 | 19 | make_msgid, | 18 | make_msgid, |
27 | 20 | ) | 19 | ) |
28 | 21 | import operator | ||
29 | 22 | import re | 20 | import re |
30 | 23 | 21 | ||
31 | 24 | from zope.component import ( | 22 | from zope.component import ( |
32 | @@ -37,9 +35,7 @@ | |||
33 | 37 | IPerson, | 35 | IPerson, |
34 | 38 | IPersonSet, | 36 | IPersonSet, |
35 | 39 | ISpecification, | 37 | ISpecification, |
36 | 40 | IStructuralSubscriptionTarget, | ||
37 | 41 | ITeamMembershipSet, | 38 | ITeamMembershipSet, |
38 | 42 | IUpstreamBugTask, | ||
39 | 43 | TeamMembershipStatus, | 39 | TeamMembershipStatus, |
40 | 44 | ) | 40 | ) |
41 | 45 | from canonical.launchpad.interfaces.launchpad import ILaunchpadRoot | 41 | from canonical.launchpad.interfaces.launchpad import ILaunchpadRoot |
42 | @@ -55,102 +51,20 @@ | |||
43 | 55 | ) | 51 | ) |
44 | 56 | from canonical.launchpad.webapp.publisher import canonical_url | 52 | from canonical.launchpad.webapp.publisher import canonical_url |
45 | 57 | from canonical.launchpad.webapp.url import urlappend | 53 | from canonical.launchpad.webapp.url import urlappend |
59 | 58 | from lp.bugs.adapters.bugchange import ( | 54 | from lp.bugs.mail.bugnotificationbuilder import get_bugmail_error_address |
47 | 59 | BugDuplicateChange, | ||
48 | 60 | BugTaskAssigneeChange, | ||
49 | 61 | get_bug_changes, | ||
50 | 62 | ) | ||
51 | 63 | from lp.bugs.adapters.bugdelta import BugDelta | ||
52 | 64 | from lp.bugs.interfaces.bugchange import IBugChange | ||
53 | 65 | from lp.bugs.mail.bugnotificationbuilder import ( | ||
54 | 66 | BugNotificationBuilder, | ||
55 | 67 | get_bugmail_error_address, | ||
56 | 68 | ) | ||
57 | 69 | from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients | ||
58 | 70 | from lp.registry.enum import BugNotificationLevel | ||
60 | 71 | from lp.services.mail.mailwrapper import MailWrapper | 55 | from lp.services.mail.mailwrapper import MailWrapper |
61 | 72 | # XXX 2010-06-16 gmb bug=594985 | 56 | # XXX 2010-06-16 gmb bug=594985 |
62 | 73 | # This shouldn't be here, but if we take it out lots of things cry, | 57 | # This shouldn't be here, but if we take it out lots of things cry, |
63 | 74 | # which is sad. | 58 | # which is sad. |
64 | 75 | from lp.services.mail.notificationrecipientset import NotificationRecipientSet | 59 | from lp.services.mail.notificationrecipientset import NotificationRecipientSet |
65 | 76 | 60 | ||
66 | 61 | # Silence lint warnings. | ||
67 | 62 | NotificationRecipientSet | ||
68 | 63 | |||
69 | 77 | 64 | ||
70 | 78 | CC = "CC" | 65 | CC = "CC" |
71 | 79 | 66 | ||
72 | 80 | 67 | ||
73 | 81 | def _send_bug_details_to_new_bug_subscribers( | ||
74 | 82 | bug, previous_subscribers, current_subscribers, subscribed_by=None, | ||
75 | 83 | event_creator=None): | ||
76 | 84 | """Send an email containing full bug details to new bug subscribers. | ||
77 | 85 | |||
78 | 86 | This function is designed to handle situations where bugtasks get | ||
79 | 87 | reassigned to new products or sourcepackages, and the new bug subscribers | ||
80 | 88 | need to be notified of the bug. | ||
81 | 89 | """ | ||
82 | 90 | prev_subs_set = set(previous_subscribers) | ||
83 | 91 | cur_subs_set = set(current_subscribers) | ||
84 | 92 | new_subs = cur_subs_set.difference(prev_subs_set) | ||
85 | 93 | |||
86 | 94 | to_addrs = set() | ||
87 | 95 | for new_sub in new_subs: | ||
88 | 96 | to_addrs.update(get_contact_email_addresses(new_sub)) | ||
89 | 97 | |||
90 | 98 | if not to_addrs: | ||
91 | 99 | return | ||
92 | 100 | |||
93 | 101 | from_addr = format_address( | ||
94 | 102 | 'Launchpad Bug Tracker', | ||
95 | 103 | "%s@%s" % (bug.id, config.launchpad.bugs_domain)) | ||
96 | 104 | # Now's a good a time as any for this email; don't use the original | ||
97 | 105 | # reported date for the bug as it will just confuse mailer and | ||
98 | 106 | # recipient. | ||
99 | 107 | email_date = datetime.datetime.now() | ||
100 | 108 | |||
101 | 109 | # The new subscriber email is effectively the initial message regarding | ||
102 | 110 | # a new bug. The bug's initial message is used in the References | ||
103 | 111 | # header to establish the message's context in the email client. | ||
104 | 112 | references = [bug.initial_message.rfc822msgid] | ||
105 | 113 | recipients = bug.getBugNotificationRecipients() | ||
106 | 114 | |||
107 | 115 | bug_notification_builder = BugNotificationBuilder(bug, event_creator) | ||
108 | 116 | for to_addr in sorted(to_addrs): | ||
109 | 117 | reason, rationale = recipients.getReason(to_addr) | ||
110 | 118 | subject, contents = generate_bug_add_email( | ||
111 | 119 | bug, new_recipients=True, subscribed_by=subscribed_by, | ||
112 | 120 | reason=reason, event_creator=event_creator) | ||
113 | 121 | msg = bug_notification_builder.build( | ||
114 | 122 | from_addr, to_addr, contents, subject, email_date, | ||
115 | 123 | rationale=rationale, references=references) | ||
116 | 124 | sendmail(msg) | ||
117 | 125 | |||
118 | 126 | |||
119 | 127 | @block_implicit_flushes | ||
120 | 128 | def update_security_contact_subscriptions(modified_bugtask, event): | ||
121 | 129 | """Subscribe the new security contact when a bugtask's product changes. | ||
122 | 130 | |||
123 | 131 | Only subscribes the new security contact if the bug was marked a | ||
124 | 132 | security issue originally. | ||
125 | 133 | |||
126 | 134 | No change is made for private bugs. | ||
127 | 135 | """ | ||
128 | 136 | if event.object.bug.private: | ||
129 | 137 | return | ||
130 | 138 | |||
131 | 139 | if not IUpstreamBugTask.providedBy(event.object): | ||
132 | 140 | return | ||
133 | 141 | |||
134 | 142 | bugtask_before_modification = event.object_before_modification | ||
135 | 143 | bugtask_after_modification = event.object | ||
136 | 144 | |||
137 | 145 | if (bugtask_before_modification.product != | ||
138 | 146 | bugtask_after_modification.product): | ||
139 | 147 | new_product = bugtask_after_modification.product | ||
140 | 148 | if (bugtask_before_modification.bug.security_related and | ||
141 | 149 | new_product.security_contact): | ||
142 | 150 | bugtask_after_modification.bug.subscribe( | ||
143 | 151 | new_product.security_contact, IPerson(event.user)) | ||
144 | 152 | |||
145 | 153 | |||
146 | 154 | def send_process_error_notification(to_address, subject, error_msg, | 68 | def send_process_error_notification(to_address, subject, error_msg, |
147 | 155 | original_msg, failing_command=None): | 69 | original_msg, failing_command=None): |
148 | 156 | """Send a mail about an error occurring while using the email interface. | 70 | """Send a mail about an error occurring while using the email interface. |
149 | @@ -193,102 +107,6 @@ | |||
150 | 193 | sendmail(msg) | 107 | sendmail(msg) |
151 | 194 | 108 | ||
152 | 195 | 109 | ||
153 | 196 | def notify_errors_list(message, file_alias_url): | ||
154 | 197 | """Sends an error to the Launchpad errors list.""" | ||
155 | 198 | template = get_email_template('notify-unhandled-email.txt') | ||
156 | 199 | # We add the error message in as a header too | ||
157 | 200 | # (X-Launchpad-Unhandled-Email) so we can create filters in the | ||
158 | 201 | # Launchpad-Error-Reports Mailman mailing list. | ||
159 | 202 | simple_sendmail( | ||
160 | 203 | get_bugmail_error_address(), [config.launchpad.errors_address], | ||
161 | 204 | 'Unhandled Email: %s' % file_alias_url, | ||
162 | 205 | template % {'url': file_alias_url, 'error_msg': message}, | ||
163 | 206 | headers={'X-Launchpad-Unhandled-Email': message}) | ||
164 | 207 | |||
165 | 208 | |||
166 | 209 | def generate_bug_add_email(bug, new_recipients=False, reason=None, | ||
167 | 210 | subscribed_by=None, event_creator=None): | ||
168 | 211 | """Generate a new bug notification from the given IBug. | ||
169 | 212 | |||
170 | 213 | If new_recipients is supplied we generate a notification explaining | ||
171 | 214 | that the new recipients have been subscribed to the bug. Otherwise | ||
172 | 215 | it's just a notification of a new bug report. | ||
173 | 216 | """ | ||
174 | 217 | subject = u"[Bug %d] [NEW] %s" % (bug.id, bug.title) | ||
175 | 218 | contents = '' | ||
176 | 219 | |||
177 | 220 | if bug.private: | ||
178 | 221 | # This is a confidential bug. | ||
179 | 222 | visibility = u"Private" | ||
180 | 223 | else: | ||
181 | 224 | # This is a public bug. | ||
182 | 225 | visibility = u"Public" | ||
183 | 226 | |||
184 | 227 | if bug.security_related: | ||
185 | 228 | visibility += ' security' | ||
186 | 229 | contents += '*** This bug is a security vulnerability ***\n\n' | ||
187 | 230 | |||
188 | 231 | bug_info = [] | ||
189 | 232 | # Add information about the affected upstreams and packages. | ||
190 | 233 | for bugtask in bug.bugtasks: | ||
191 | 234 | bug_info.append(u"** Affects: %s" % bugtask.bugtargetname) | ||
192 | 235 | bug_info.append(u" Importance: %s" % bugtask.importance.title) | ||
193 | 236 | |||
194 | 237 | if bugtask.assignee: | ||
195 | 238 | # There's a person assigned to fix this task, so show that | ||
196 | 239 | # information too. | ||
197 | 240 | bug_info.append( | ||
198 | 241 | u" Assignee: %s" % bugtask.assignee.unique_displayname) | ||
199 | 242 | bug_info.append(u" Status: %s\n" % bugtask.status.title) | ||
200 | 243 | |||
201 | 244 | if bug.tags: | ||
202 | 245 | bug_info.append('\n** Tags: %s' % ' '.join(bug.tags)) | ||
203 | 246 | |||
204 | 247 | mailwrapper = MailWrapper(width=72) | ||
205 | 248 | content_substitutions = { | ||
206 | 249 | 'visibility': visibility, | ||
207 | 250 | 'bug_url': canonical_url(bug), | ||
208 | 251 | 'bug_info': "\n".join(bug_info), | ||
209 | 252 | 'bug_title': bug.title, | ||
210 | 253 | 'description': mailwrapper.format(bug.description), | ||
211 | 254 | 'notification_rationale': reason, | ||
212 | 255 | } | ||
213 | 256 | |||
214 | 257 | if new_recipients: | ||
215 | 258 | if "assignee" in reason: | ||
216 | 259 | contents += ( | ||
217 | 260 | "You have been assigned a bug task for a %(visibility)s bug") | ||
218 | 261 | if event_creator is not None: | ||
219 | 262 | contents += " by %(assigner)s" | ||
220 | 263 | content_substitutions['assigner'] = ( | ||
221 | 264 | event_creator.unique_displayname) | ||
222 | 265 | else: | ||
223 | 266 | contents += "You have been subscribed to a %(visibility)s bug" | ||
224 | 267 | if subscribed_by is not None: | ||
225 | 268 | contents += " by %(subscribed_by)s" | ||
226 | 269 | content_substitutions['subscribed_by'] = ( | ||
227 | 270 | subscribed_by.unique_displayname) | ||
228 | 271 | contents += (":\n\n" | ||
229 | 272 | "%(description)s\n\n%(bug_info)s") | ||
230 | 273 | # The visibility appears mid-phrase so.. hack hack. | ||
231 | 274 | content_substitutions['visibility'] = visibility.lower() | ||
232 | 275 | # XXX: kiko, 2007-03-21: | ||
233 | 276 | # We should really have a centralized way of adding this | ||
234 | 277 | # footer, but right now we lack a INotificationRecipientSet | ||
235 | 278 | # for this particular situation. | ||
236 | 279 | contents += ( | ||
237 | 280 | "\n-- \n%(bug_title)s\n%(bug_url)s\n%(notification_rationale)s") | ||
238 | 281 | else: | ||
239 | 282 | contents += ("%(visibility)s bug reported:\n\n" | ||
240 | 283 | "%(description)s\n\n%(bug_info)s") | ||
241 | 284 | |||
242 | 285 | contents = contents % content_substitutions | ||
243 | 286 | |||
244 | 287 | contents = contents.rstrip() | ||
245 | 288 | |||
246 | 289 | return (subject, contents) | ||
247 | 290 | |||
248 | 291 | |||
249 | 292 | def get_unified_diff(old_text, new_text, text_width): | 110 | def get_unified_diff(old_text, new_text, text_width): |
250 | 293 | r"""Return a unified diff of the two texts. | 111 | r"""Return a unified diff of the two texts. |
251 | 294 | 112 | ||
252 | @@ -329,254 +147,6 @@ | |||
253 | 329 | return text_diff | 147 | return text_diff |
254 | 330 | 148 | ||
255 | 331 | 149 | ||
256 | 332 | def _get_task_change_row(label, oldval_display, newval_display): | ||
257 | 333 | """Return a row formatted for display in task change info.""" | ||
258 | 334 | return u"%(label)13s: %(oldval)s => %(newval)s\n" % { | ||
259 | 335 | 'label': label.capitalize(), | ||
260 | 336 | 'oldval': oldval_display, | ||
261 | 337 | 'newval': newval_display} | ||
262 | 338 | |||
263 | 339 | |||
264 | 340 | def _get_task_change_values(task_change, displayattrname): | ||
265 | 341 | """Return the old value and the new value for a task field change.""" | ||
266 | 342 | oldval = task_change.get('old') | ||
267 | 343 | newval = task_change.get('new') | ||
268 | 344 | |||
269 | 345 | oldval_display = None | ||
270 | 346 | newval_display = None | ||
271 | 347 | |||
272 | 348 | if oldval: | ||
273 | 349 | oldval_display = getattr(oldval, displayattrname) | ||
274 | 350 | if newval: | ||
275 | 351 | newval_display = getattr(newval, displayattrname) | ||
276 | 352 | |||
277 | 353 | return (oldval_display, newval_display) | ||
278 | 354 | |||
279 | 355 | |||
280 | 356 | def get_bug_delta(old_bug, new_bug, user): | ||
281 | 357 | """Compute the delta from old_bug to new_bug. | ||
282 | 358 | |||
283 | 359 | old_bug and new_bug are IBug's. user is an IPerson. Returns an | ||
284 | 360 | IBugDelta if there are changes, or None if there were no changes. | ||
285 | 361 | """ | ||
286 | 362 | changes = {} | ||
287 | 363 | |||
288 | 364 | for field_name in ("title", "description", "name", "private", | ||
289 | 365 | "security_related", "duplicateof", "tags"): | ||
290 | 366 | # fields for which we show old => new when their values change | ||
291 | 367 | old_val = getattr(old_bug, field_name) | ||
292 | 368 | new_val = getattr(new_bug, field_name) | ||
293 | 369 | if old_val != new_val: | ||
294 | 370 | changes[field_name] = {} | ||
295 | 371 | changes[field_name]["old"] = old_val | ||
296 | 372 | changes[field_name]["new"] = new_val | ||
297 | 373 | |||
298 | 374 | if changes: | ||
299 | 375 | changes["bug"] = new_bug | ||
300 | 376 | changes["bug_before_modification"] = old_bug | ||
301 | 377 | changes["bugurl"] = canonical_url(new_bug) | ||
302 | 378 | changes["user"] = user | ||
303 | 379 | |||
304 | 380 | return BugDelta(**changes) | ||
305 | 381 | else: | ||
306 | 382 | return None | ||
307 | 383 | |||
308 | 384 | |||
309 | 385 | @block_implicit_flushes | ||
310 | 386 | def notify_bug_added(bug, event): | ||
311 | 387 | """Send an email notification that a bug was added. | ||
312 | 388 | |||
313 | 389 | Event must be an IObjectCreatedEvent. | ||
314 | 390 | """ | ||
315 | 391 | |||
316 | 392 | bug.addCommentNotification(bug.initial_message) | ||
317 | 393 | |||
318 | 394 | |||
319 | 395 | @block_implicit_flushes | ||
320 | 396 | def notify_bug_modified(modified_bug, event): | ||
321 | 397 | """Notify the Cc'd list that this bug has been modified. | ||
322 | 398 | |||
323 | 399 | modified_bug bug must be an IBug. event must be an | ||
324 | 400 | IObjectModifiedEvent. | ||
325 | 401 | """ | ||
326 | 402 | bug_delta = get_bug_delta( | ||
327 | 403 | old_bug=event.object_before_modification, | ||
328 | 404 | new_bug=event.object, user=IPerson(event.user)) | ||
329 | 405 | |||
330 | 406 | if bug_delta is not None: | ||
331 | 407 | add_bug_change_notifications(bug_delta) | ||
332 | 408 | |||
333 | 409 | |||
334 | 410 | def get_bugtask_indirect_subscribers(bugtask, recipients=None, level=None): | ||
335 | 411 | """Return the indirect subscribers for a bug task. | ||
336 | 412 | |||
337 | 413 | Return the list of people who should get notifications about | ||
338 | 414 | changes to the task because of having an indirect subscription | ||
339 | 415 | relationship with it (by subscribing to its target, being an | ||
340 | 416 | assignee or owner, etc...) | ||
341 | 417 | |||
342 | 418 | If `recipients` is present, add the subscribers to the set of | ||
343 | 419 | bug notification recipients. | ||
344 | 420 | """ | ||
345 | 421 | if bugtask.bug.private: | ||
346 | 422 | return set() | ||
347 | 423 | |||
348 | 424 | also_notified_subscribers = set() | ||
349 | 425 | |||
350 | 426 | # Assignees are indirect subscribers. | ||
351 | 427 | if bugtask.assignee: | ||
352 | 428 | also_notified_subscribers.add(bugtask.assignee) | ||
353 | 429 | if recipients is not None: | ||
354 | 430 | recipients.addAssignee(bugtask.assignee) | ||
355 | 431 | |||
356 | 432 | if IStructuralSubscriptionTarget.providedBy(bugtask.target): | ||
357 | 433 | also_notified_subscribers.update( | ||
358 | 434 | bugtask.target.getBugNotificationsRecipients( | ||
359 | 435 | recipients, level=level)) | ||
360 | 436 | |||
361 | 437 | if bugtask.milestone is not None: | ||
362 | 438 | also_notified_subscribers.update( | ||
363 | 439 | bugtask.milestone.getBugNotificationsRecipients( | ||
364 | 440 | recipients, level=level)) | ||
365 | 441 | |||
366 | 442 | # If the target's bug supervisor isn't set, | ||
367 | 443 | # we add the owner as a subscriber. | ||
368 | 444 | pillar = bugtask.pillar | ||
369 | 445 | if pillar.bug_supervisor is None: | ||
370 | 446 | also_notified_subscribers.add(pillar.owner) | ||
371 | 447 | if recipients is not None: | ||
372 | 448 | recipients.addRegistrant(pillar.owner, pillar) | ||
373 | 449 | |||
374 | 450 | return sorted( | ||
375 | 451 | also_notified_subscribers, | ||
376 | 452 | key=operator.attrgetter('displayname')) | ||
377 | 453 | |||
378 | 454 | |||
379 | 455 | def add_bug_change_notifications(bug_delta, old_bugtask=None, | ||
380 | 456 | new_subscribers=None): | ||
381 | 457 | """Generate bug notifications and add them to the bug.""" | ||
382 | 458 | changes = get_bug_changes(bug_delta) | ||
383 | 459 | recipients = bug_delta.bug.getBugNotificationRecipients( | ||
384 | 460 | old_bug=bug_delta.bug_before_modification, | ||
385 | 461 | level=BugNotificationLevel.METADATA) | ||
386 | 462 | if old_bugtask is not None: | ||
387 | 463 | old_bugtask_recipients = BugNotificationRecipients() | ||
388 | 464 | get_bugtask_indirect_subscribers( | ||
389 | 465 | old_bugtask, recipients=old_bugtask_recipients, | ||
390 | 466 | level=BugNotificationLevel.METADATA) | ||
391 | 467 | recipients.update(old_bugtask_recipients) | ||
392 | 468 | for change in changes: | ||
393 | 469 | # XXX 2009-03-17 gmb [bug=344125] | ||
394 | 470 | # This if..else should be removed once the new BugChange API | ||
395 | 471 | # is complete and ubiquitous. | ||
396 | 472 | if IBugChange.providedBy(change): | ||
397 | 473 | if isinstance(change, BugDuplicateChange): | ||
398 | 474 | no_dupe_master_recipients = ( | ||
399 | 475 | bug_delta.bug.getBugNotificationRecipients( | ||
400 | 476 | old_bug=bug_delta.bug_before_modification, | ||
401 | 477 | level=BugNotificationLevel.METADATA, | ||
402 | 478 | include_master_dupe_subscribers=False)) | ||
403 | 479 | bug_delta.bug.addChange( | ||
404 | 480 | change, recipients=no_dupe_master_recipients) | ||
405 | 481 | elif (isinstance(change, BugTaskAssigneeChange) and | ||
406 | 482 | new_subscribers is not None): | ||
407 | 483 | for person in new_subscribers: | ||
408 | 484 | reason, rationale = recipients.getReason(person) | ||
409 | 485 | if 'Assignee' in rationale: | ||
410 | 486 | recipients.remove(person) | ||
411 | 487 | bug_delta.bug.addChange(change, recipients=recipients) | ||
412 | 488 | else: | ||
413 | 489 | bug_delta.bug.addChange(change, recipients=recipients) | ||
414 | 490 | else: | ||
415 | 491 | bug_delta.bug.addChangeNotification( | ||
416 | 492 | change, person=bug_delta.user, recipients=recipients) | ||
417 | 493 | |||
418 | 494 | |||
419 | 495 | @block_implicit_flushes | ||
420 | 496 | def notify_bugtask_edited(modified_bugtask, event): | ||
421 | 497 | """Notify CC'd subscribers of this bug that something has changed | ||
422 | 498 | on this task. | ||
423 | 499 | |||
424 | 500 | modified_bugtask must be an IBugTask. event must be an | ||
425 | 501 | IObjectModifiedEvent. | ||
426 | 502 | """ | ||
427 | 503 | bugtask_delta = event.object.getDelta(event.object_before_modification) | ||
428 | 504 | bug_delta = BugDelta( | ||
429 | 505 | bug=event.object.bug, | ||
430 | 506 | bugurl=canonical_url(event.object.bug), | ||
431 | 507 | bugtask_deltas=bugtask_delta, | ||
432 | 508 | user=IPerson(event.user)) | ||
433 | 509 | |||
434 | 510 | event_creator = IPerson(event.user) | ||
435 | 511 | previous_subscribers = event.object_before_modification.bug_subscribers | ||
436 | 512 | current_subscribers = event.object.bug_subscribers | ||
437 | 513 | prev_subs_set = set(previous_subscribers) | ||
438 | 514 | cur_subs_set = set(current_subscribers) | ||
439 | 515 | new_subs = cur_subs_set.difference(prev_subs_set) | ||
440 | 516 | |||
441 | 517 | add_bug_change_notifications( | ||
442 | 518 | bug_delta, old_bugtask=event.object_before_modification, | ||
443 | 519 | new_subscribers=new_subs) | ||
444 | 520 | |||
445 | 521 | _send_bug_details_to_new_bug_subscribers( | ||
446 | 522 | event.object.bug, previous_subscribers, current_subscribers, | ||
447 | 523 | event_creator=event_creator) | ||
448 | 524 | update_security_contact_subscriptions(modified_bugtask, event) | ||
449 | 525 | |||
450 | 526 | |||
451 | 527 | @block_implicit_flushes | ||
452 | 528 | def notify_bug_comment_added(bugmessage, event): | ||
453 | 529 | """Notify CC'd list that a message was added to this bug. | ||
454 | 530 | |||
455 | 531 | bugmessage must be an IBugMessage. event must be an | ||
456 | 532 | IObjectCreatedEvent. If bugmessage.bug is a duplicate the | ||
457 | 533 | comment will also be sent to the dup target's subscribers. | ||
458 | 534 | """ | ||
459 | 535 | bug = bugmessage.bug | ||
460 | 536 | bug.addCommentNotification(bugmessage.message) | ||
461 | 537 | |||
462 | 538 | |||
463 | 539 | @block_implicit_flushes | ||
464 | 540 | def notify_bug_attachment_added(bugattachment, event): | ||
465 | 541 | """Notify CC'd list that a new attachment has been added. | ||
466 | 542 | |||
467 | 543 | bugattachment must be an IBugAttachment. event must be an | ||
468 | 544 | IObjectCreatedEvent. | ||
469 | 545 | """ | ||
470 | 546 | bug = bugattachment.bug | ||
471 | 547 | bug_delta = BugDelta( | ||
472 | 548 | bug=bug, | ||
473 | 549 | bugurl=canonical_url(bug), | ||
474 | 550 | user=IPerson(event.user), | ||
475 | 551 | attachment={'new': bugattachment, 'old': None}) | ||
476 | 552 | |||
477 | 553 | add_bug_change_notifications(bug_delta) | ||
478 | 554 | |||
479 | 555 | |||
480 | 556 | @block_implicit_flushes | ||
481 | 557 | def notify_bug_attachment_removed(bugattachment, event): | ||
482 | 558 | """Notify that an attachment has been removed.""" | ||
483 | 559 | bug = bugattachment.bug | ||
484 | 560 | bug_delta = BugDelta( | ||
485 | 561 | bug=bug, | ||
486 | 562 | bugurl=canonical_url(bug), | ||
487 | 563 | user=IPerson(event.user), | ||
488 | 564 | attachment={'old': bugattachment, 'new': None}) | ||
489 | 565 | |||
490 | 566 | add_bug_change_notifications(bug_delta) | ||
491 | 567 | |||
492 | 568 | |||
493 | 569 | @block_implicit_flushes | ||
494 | 570 | def notify_bug_subscription_added(bug_subscription, event): | ||
495 | 571 | """Notify that a new bug subscription was added.""" | ||
496 | 572 | # When a user is subscribed to a bug by someone other | ||
497 | 573 | # than themselves, we send them a notification email. | ||
498 | 574 | if bug_subscription.person != bug_subscription.subscribed_by: | ||
499 | 575 | _send_bug_details_to_new_bug_subscribers( | ||
500 | 576 | bug_subscription.bug, [], [bug_subscription.person], | ||
501 | 577 | subscribed_by=bug_subscription.subscribed_by) | ||
502 | 578 | |||
503 | 579 | |||
504 | 580 | @block_implicit_flushes | 150 | @block_implicit_flushes |
505 | 581 | def notify_invitation_to_join_team(event): | 151 | def notify_invitation_to_join_team(event): |
506 | 582 | """Notify team admins that the team has been invited to join another team. | 152 | """Notify team admins that the team has been invited to join another team. |
507 | 583 | 153 | ||
508 | === modified file 'lib/canonical/launchpad/subscribers/karma.py' | |||
509 | --- lib/canonical/launchpad/subscribers/karma.py 2010-08-20 20:31:18 +0000 | |||
510 | +++ lib/canonical/launchpad/subscribers/karma.py 2010-08-23 20:18:06 +0000 | |||
511 | @@ -6,7 +6,7 @@ | |||
512 | 6 | 6 | ||
513 | 7 | from canonical.database.sqlbase import block_implicit_flushes | 7 | from canonical.database.sqlbase import block_implicit_flushes |
514 | 8 | from canonical.launchpad.interfaces import BugTaskStatus | 8 | from canonical.launchpad.interfaces import BugTaskStatus |
516 | 9 | from canonical.launchpad.mailnotification import get_bug_delta | 9 | from lp.bugs.subscribers.bug import get_bug_delta |
517 | 10 | from lp.code.enums import BranchMergeProposalStatus | 10 | from lp.code.enums import BranchMergeProposalStatus |
518 | 11 | from lp.registry.interfaces.person import IPerson | 11 | from lp.registry.interfaces.person import IPerson |
519 | 12 | 12 | ||
520 | @@ -18,6 +18,7 @@ | |||
521 | 18 | assert len(bug.bugtasks) >= 1 | 18 | assert len(bug.bugtasks) >= 1 |
522 | 19 | _assignKarmaUsingBugContext(IPerson(event.user), bug, 'bugcreated') | 19 | _assignKarmaUsingBugContext(IPerson(event.user), bug, 'bugcreated') |
523 | 20 | 20 | ||
524 | 21 | |||
525 | 21 | def _assign_karma_using_bugtask_context(person, bugtask, actionname): | 22 | def _assign_karma_using_bugtask_context(person, bugtask, actionname): |
526 | 22 | """Extract the right context from the bugtask and assign karma.""" | 23 | """Extract the right context from the bugtask and assign karma.""" |
527 | 23 | distribution = bugtask.distribution | 24 | distribution = bugtask.distribution |
528 | @@ -157,6 +158,7 @@ | |||
529 | 157 | """Assign karma to the user who registered the branch.""" | 158 | """Assign karma to the user who registered the branch.""" |
530 | 158 | branch.target.assignKarma(branch.registrant, 'branchcreated') | 159 | branch.target.assignKarma(branch.registrant, 'branchcreated') |
531 | 159 | 160 | ||
532 | 161 | |||
533 | 160 | @block_implicit_flushes | 162 | @block_implicit_flushes |
534 | 161 | def bug_branch_created(bug_branch, event): | 163 | def bug_branch_created(bug_branch, event): |
535 | 162 | """Assign karma to the user who linked the bug to the branch.""" | 164 | """Assign karma to the user who linked the bug to the branch.""" |
536 | 163 | 165 | ||
537 | === modified file 'lib/lp/bugs/configure.zcml' | |||
538 | --- lib/lp/bugs/configure.zcml 2010-08-19 03:06:27 +0000 | |||
539 | +++ lib/lp/bugs/configure.zcml 2010-08-23 20:18:06 +0000 | |||
540 | @@ -55,25 +55,22 @@ | |||
541 | 55 | handler="lp.bugs.subscribers.bugcreation.at_least_one_task"/> | 55 | handler="lp.bugs.subscribers.bugcreation.at_least_one_task"/> |
542 | 56 | <subscriber | 56 | <subscriber |
543 | 57 | for="canonical.launchpad.interfaces.IBug lazr.lifecycle.interfaces.IObjectCreatedEvent" | 57 | for="canonical.launchpad.interfaces.IBug lazr.lifecycle.interfaces.IObjectCreatedEvent" |
545 | 58 | handler="canonical.launchpad.mailnotification.notify_bug_added"/> | 58 | handler="lp.bugs.subscribers.bug.notify_bug_added"/> |
546 | 59 | <subscriber | 59 | <subscriber |
547 | 60 | for="canonical.launchpad.interfaces.IBug lazr.lifecycle.interfaces.IObjectCreatedEvent" | 60 | for="canonical.launchpad.interfaces.IBug lazr.lifecycle.interfaces.IObjectCreatedEvent" |
548 | 61 | handler="canonical.launchpad.subscribers.karma.bug_created"/> | 61 | handler="canonical.launchpad.subscribers.karma.bug_created"/> |
549 | 62 | <subscriber | 62 | <subscriber |
550 | 63 | for="canonical.launchpad.interfaces.IBug lazr.lifecycle.interfaces.IObjectModifiedEvent" | 63 | for="canonical.launchpad.interfaces.IBug lazr.lifecycle.interfaces.IObjectModifiedEvent" |
551 | 64 | handler="canonical.launchpad.mailnotification.notify_bug_modified"/> | ||
552 | 65 | <subscriber | ||
553 | 66 | for="canonical.launchpad.interfaces.IBug lazr.lifecycle.interfaces.IObjectModifiedEvent" | ||
554 | 67 | handler="canonical.launchpad.subscribers.karma.bug_modified"/> | 64 | handler="canonical.launchpad.subscribers.karma.bug_modified"/> |
555 | 68 | <subscriber | 65 | <subscriber |
556 | 69 | for="canonical.launchpad.interfaces.IBug lazr.lifecycle.interfaces.IObjectModifiedEvent" | 66 | for="canonical.launchpad.interfaces.IBug lazr.lifecycle.interfaces.IObjectModifiedEvent" |
557 | 70 | handler="lp.bugs.subscribers.buglastupdated.update_bug_date_last_updated"/> | 67 | handler="lp.bugs.subscribers.buglastupdated.update_bug_date_last_updated"/> |
558 | 71 | <subscriber | 68 | <subscriber |
559 | 72 | for="canonical.launchpad.interfaces.IBugAttachment lazr.lifecycle.interfaces.IObjectCreatedEvent" | 69 | for="canonical.launchpad.interfaces.IBugAttachment lazr.lifecycle.interfaces.IObjectCreatedEvent" |
561 | 73 | handler="canonical.launchpad.mailnotification.notify_bug_attachment_added"/> | 70 | handler="lp.bugs.subscribers.bug.notify_bug_attachment_added"/> |
562 | 74 | <subscriber | 71 | <subscriber |
563 | 75 | for="canonical.launchpad.interfaces.IBugAttachment lazr.lifecycle.interfaces.IObjectDeletedEvent" | 72 | for="canonical.launchpad.interfaces.IBugAttachment lazr.lifecycle.interfaces.IObjectDeletedEvent" |
565 | 76 | handler="canonical.launchpad.mailnotification.notify_bug_attachment_removed"/> | 73 | handler="lp.bugs.subscribers.bug.notify_bug_attachment_removed"/> |
566 | 77 | <subscriber | 74 | <subscriber |
567 | 78 | for="canonical.launchpad.interfaces.IBugAttachment lazr.lifecycle.interfaces.IObjectCreatedEvent" | 75 | for="canonical.launchpad.interfaces.IBugAttachment lazr.lifecycle.interfaces.IObjectCreatedEvent" |
568 | 79 | handler="lp.bugs.subscribers.buglastupdated.update_bug_date_last_updated"/> | 76 | handler="lp.bugs.subscribers.buglastupdated.update_bug_date_last_updated"/> |
569 | @@ -94,7 +91,7 @@ | |||
570 | 94 | handler="canonical.launchpad.subscribers.karma.cve_added"/> | 91 | handler="canonical.launchpad.subscribers.karma.cve_added"/> |
571 | 95 | <subscriber | 92 | <subscriber |
572 | 96 | for="canonical.launchpad.interfaces.IBugMessage lazr.lifecycle.interfaces.IObjectCreatedEvent" | 93 | for="canonical.launchpad.interfaces.IBugMessage lazr.lifecycle.interfaces.IObjectCreatedEvent" |
574 | 97 | handler="canonical.launchpad.mailnotification.notify_bug_comment_added"/> | 94 | handler="lp.bugs.subscribers.bug.notify_bug_comment_added"/> |
575 | 98 | <subscriber | 95 | <subscriber |
576 | 99 | for="canonical.launchpad.interfaces.IBugMessage lazr.lifecycle.interfaces.IObjectCreatedEvent" | 96 | for="canonical.launchpad.interfaces.IBugMessage lazr.lifecycle.interfaces.IObjectCreatedEvent" |
577 | 100 | handler="canonical.launchpad.subscribers.karma.bug_comment_added"/> | 97 | handler="canonical.launchpad.subscribers.karma.bug_comment_added"/> |
578 | @@ -115,7 +112,7 @@ | |||
579 | 115 | handler="lp.bugs.subscribers.buglastupdated.update_bug_date_last_updated"/> | 112 | handler="lp.bugs.subscribers.buglastupdated.update_bug_date_last_updated"/> |
580 | 116 | <subscriber | 113 | <subscriber |
581 | 117 | for="canonical.launchpad.interfaces.IBugSubscription lazr.lifecycle.interfaces.IObjectCreatedEvent" | 114 | for="canonical.launchpad.interfaces.IBugSubscription lazr.lifecycle.interfaces.IObjectCreatedEvent" |
583 | 118 | handler="canonical.launchpad.mailnotification.notify_bug_subscription_added"/> | 115 | handler="lp.bugs.subscribers.bug.notify_bug_subscription_added"/> |
584 | 119 | <subscriber | 116 | <subscriber |
585 | 120 | for="canonical.launchpad.interfaces.IBug lazr.lifecycle.interfaces.IObjectModifiedEvent" | 117 | for="canonical.launchpad.interfaces.IBug lazr.lifecycle.interfaces.IObjectModifiedEvent" |
586 | 121 | handler="lp.bugs.subscribers.bug.notify_bug_modified"/> | 118 | handler="lp.bugs.subscribers.bug.notify_bug_modified"/> |
587 | @@ -932,7 +929,7 @@ | |||
588 | 932 | handler="lp.bugs.subscribers.buglastupdated.update_bug_date_last_updated"/> | 929 | handler="lp.bugs.subscribers.buglastupdated.update_bug_date_last_updated"/> |
589 | 933 | <subscriber | 930 | <subscriber |
590 | 934 | for="canonical.launchpad.interfaces.IBugTask lazr.lifecycle.interfaces.IObjectModifiedEvent" | 931 | for="canonical.launchpad.interfaces.IBugTask lazr.lifecycle.interfaces.IObjectModifiedEvent" |
592 | 935 | handler="canonical.launchpad.mailnotification.notify_bugtask_edited"/> | 932 | handler="lp.bugs.subscribers.bugtask.notify_bugtask_edited"/> |
593 | 936 | <subscriber | 933 | <subscriber |
594 | 937 | for="canonical.launchpad.interfaces.IBugTask lazr.lifecycle.interfaces.IObjectModifiedEvent" | 934 | for="canonical.launchpad.interfaces.IBugTask lazr.lifecycle.interfaces.IObjectModifiedEvent" |
595 | 938 | handler="canonical.launchpad.subscribers.karma.bugtask_modified"/> | 935 | handler="canonical.launchpad.subscribers.karma.bugtask_modified"/> |
596 | 939 | 936 | ||
597 | === modified file 'lib/lp/bugs/doc/bugnotification-email.txt' | |||
598 | --- lib/lp/bugs/doc/bugnotification-email.txt 2010-08-04 09:42:07 +0000 | |||
599 | +++ lib/lp/bugs/doc/bugnotification-email.txt 2010-08-23 20:18:06 +0000 | |||
600 | @@ -19,10 +19,8 @@ | |||
601 | 19 | object it gets passed, the formatting logic has been cut into two | 19 | object it gets passed, the formatting logic has been cut into two |
602 | 20 | pieces: get_bug_changes and generate_bug_add_email. | 20 | pieces: get_bug_changes and generate_bug_add_email. |
603 | 21 | 21 | ||
608 | 22 | >>> from lp.bugs.adapters.bugchange import ( | 22 | >>> from lp.bugs.adapters.bugchange import get_bug_changes |
609 | 23 | ... get_bug_changes) | 23 | >>> from lp.bugs.mail.newbug import generate_bug_add_email |
606 | 24 | >>> from canonical.launchpad.mailnotification import ( | ||
607 | 25 | ... generate_bug_add_email) | ||
610 | 26 | 24 | ||
611 | 27 | Let's demonstrate what the bugmails will look like, by going through | 25 | Let's demonstrate what the bugmails will look like, by going through |
612 | 28 | the various events that can happen that would cause a notification to | 26 | the various events that can happen that would cause a notification to |
613 | @@ -479,8 +477,7 @@ | |||
614 | 479 | mailnotification.py contains a class, BugNotificationBuilder, which is | 477 | mailnotification.py contains a class, BugNotificationBuilder, which is |
615 | 480 | used to construct bug notification emails. | 478 | used to construct bug notification emails. |
616 | 481 | 479 | ||
619 | 482 | >>> from canonical.launchpad.mailnotification import ( | 480 | >>> from lp.bugs.mail.bugnotificationbuilder import BugNotificationBuilder |
618 | 483 | ... BugNotificationBuilder) | ||
620 | 484 | 481 | ||
621 | 485 | When instantiatiated it derives a list of common unchanging headers | 482 | When instantiatiated it derives a list of common unchanging headers |
622 | 486 | from the bug so that they are not calculated for every recipient. | 483 | from the bug so that they are not calculated for every recipient. |
623 | 487 | 484 | ||
624 | === modified file 'lib/lp/bugs/doc/bugsubscription.txt' | |||
625 | --- lib/lp/bugs/doc/bugsubscription.txt 2010-08-16 13:58:33 +0000 | |||
626 | +++ lib/lp/bugs/doc/bugsubscription.txt 2010-08-23 20:18:06 +0000 | |||
627 | @@ -101,10 +101,8 @@ | |||
628 | 101 | It is also possible to get the list of indirect subscribers for an | 101 | It is also possible to get the list of indirect subscribers for an |
629 | 102 | individual bug task. | 102 | individual bug task. |
630 | 103 | 103 | ||
635 | 104 | >>> from canonical.launchpad.mailnotification import ( | 104 | >>> from lp.bugs.subscribers.bug import get_bugtask_indirect_subscribers |
636 | 105 | ... get_bugtask_indirect_subscribers) | 105 | >>> get_bugtask_indirect_subscribers(linux_source_bug.bugtasks[0]) |
633 | 106 | >>> get_bugtask_indirect_subscribers( | ||
634 | 107 | ... linux_source_bug.bugtasks[0]) | ||
637 | 108 | [<Person at ...>] | 106 | [<Person at ...>] |
638 | 109 | 107 | ||
639 | 110 | The list of all bug subscribers can also be accessed via | 108 | The list of all bug subscribers can also be accessed via |
640 | 111 | 109 | ||
641 | === added file 'lib/lp/bugs/mail/newbug.py' | |||
642 | --- lib/lp/bugs/mail/newbug.py 1970-01-01 00:00:00 +0000 | |||
643 | +++ lib/lp/bugs/mail/newbug.py 2010-08-23 20:18:06 +0000 | |||
644 | @@ -0,0 +1,95 @@ | |||
645 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
646 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
647 | 3 | |||
648 | 4 | """Mail for new bugs.""" | ||
649 | 5 | |||
650 | 6 | __metaclass__ = type | ||
651 | 7 | __all__ = [ | ||
652 | 8 | 'generate_bug_add_email', | ||
653 | 9 | ] | ||
654 | 10 | |||
655 | 11 | from canonical.launchpad.webapp.publisher import canonical_url | ||
656 | 12 | from lp.services.mail.mailwrapper import MailWrapper | ||
657 | 13 | |||
658 | 14 | |||
659 | 15 | def generate_bug_add_email(bug, new_recipients=False, reason=None, | ||
660 | 16 | subscribed_by=None, event_creator=None): | ||
661 | 17 | """Generate a new bug notification from the given IBug. | ||
662 | 18 | |||
663 | 19 | If new_recipients is supplied we generate a notification explaining | ||
664 | 20 | that the new recipients have been subscribed to the bug. Otherwise | ||
665 | 21 | it's just a notification of a new bug report. | ||
666 | 22 | """ | ||
667 | 23 | subject = u"[Bug %d] [NEW] %s" % (bug.id, bug.title) | ||
668 | 24 | contents = '' | ||
669 | 25 | |||
670 | 26 | if bug.private: | ||
671 | 27 | # This is a confidential bug. | ||
672 | 28 | visibility = u"Private" | ||
673 | 29 | else: | ||
674 | 30 | # This is a public bug. | ||
675 | 31 | visibility = u"Public" | ||
676 | 32 | |||
677 | 33 | if bug.security_related: | ||
678 | 34 | visibility += ' security' | ||
679 | 35 | contents += '*** This bug is a security vulnerability ***\n\n' | ||
680 | 36 | |||
681 | 37 | bug_info = [] | ||
682 | 38 | # Add information about the affected upstreams and packages. | ||
683 | 39 | for bugtask in bug.bugtasks: | ||
684 | 40 | bug_info.append(u"** Affects: %s" % bugtask.bugtargetname) | ||
685 | 41 | bug_info.append(u" Importance: %s" % bugtask.importance.title) | ||
686 | 42 | |||
687 | 43 | if bugtask.assignee: | ||
688 | 44 | # There's a person assigned to fix this task, so show that | ||
689 | 45 | # information too. | ||
690 | 46 | bug_info.append( | ||
691 | 47 | u" Assignee: %s" % bugtask.assignee.unique_displayname) | ||
692 | 48 | bug_info.append(u" Status: %s\n" % bugtask.status.title) | ||
693 | 49 | |||
694 | 50 | if bug.tags: | ||
695 | 51 | bug_info.append('\n** Tags: %s' % ' '.join(bug.tags)) | ||
696 | 52 | |||
697 | 53 | mailwrapper = MailWrapper(width=72) | ||
698 | 54 | content_substitutions = { | ||
699 | 55 | 'visibility': visibility, | ||
700 | 56 | 'bug_url': canonical_url(bug), | ||
701 | 57 | 'bug_info': "\n".join(bug_info), | ||
702 | 58 | 'bug_title': bug.title, | ||
703 | 59 | 'description': mailwrapper.format(bug.description), | ||
704 | 60 | 'notification_rationale': reason, | ||
705 | 61 | } | ||
706 | 62 | |||
707 | 63 | if new_recipients: | ||
708 | 64 | if "assignee" in reason: | ||
709 | 65 | contents += ( | ||
710 | 66 | "You have been assigned a bug task for a %(visibility)s bug") | ||
711 | 67 | if event_creator is not None: | ||
712 | 68 | contents += " by %(assigner)s" | ||
713 | 69 | content_substitutions['assigner'] = ( | ||
714 | 70 | event_creator.unique_displayname) | ||
715 | 71 | else: | ||
716 | 72 | contents += "You have been subscribed to a %(visibility)s bug" | ||
717 | 73 | if subscribed_by is not None: | ||
718 | 74 | contents += " by %(subscribed_by)s" | ||
719 | 75 | content_substitutions['subscribed_by'] = ( | ||
720 | 76 | subscribed_by.unique_displayname) | ||
721 | 77 | contents += (":\n\n" | ||
722 | 78 | "%(description)s\n\n%(bug_info)s") | ||
723 | 79 | # The visibility appears mid-phrase so.. hack hack. | ||
724 | 80 | content_substitutions['visibility'] = visibility.lower() | ||
725 | 81 | # XXX: kiko, 2007-03-21: | ||
726 | 82 | # We should really have a centralized way of adding this | ||
727 | 83 | # footer, but right now we lack a INotificationRecipientSet | ||
728 | 84 | # for this particular situation. | ||
729 | 85 | contents += ( | ||
730 | 86 | "\n-- \n%(bug_title)s\n%(bug_url)s\n%(notification_rationale)s") | ||
731 | 87 | else: | ||
732 | 88 | contents += ("%(visibility)s bug reported:\n\n" | ||
733 | 89 | "%(description)s\n\n%(bug_info)s") | ||
734 | 90 | |||
735 | 91 | contents = contents % content_substitutions | ||
736 | 92 | |||
737 | 93 | contents = contents.rstrip() | ||
738 | 94 | |||
739 | 95 | return (subject, contents) | ||
740 | 0 | 96 | ||
741 | === modified file 'lib/lp/bugs/scripts/bugnotification.py' | |||
742 | --- lib/lp/bugs/scripts/bugnotification.py 2010-08-20 20:31:18 +0000 | |||
743 | +++ lib/lp/bugs/scripts/bugnotification.py 2010-08-23 20:18:06 +0000 | |||
744 | @@ -27,10 +27,6 @@ | |||
745 | 27 | get_email_template, | 27 | get_email_template, |
746 | 28 | ) | 28 | ) |
747 | 29 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities | 29 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
748 | 30 | from canonical.launchpad.mailnotification import ( | ||
749 | 31 | generate_bug_add_email, | ||
750 | 32 | MailWrapper, | ||
751 | 33 | ) | ||
752 | 34 | from canonical.launchpad.scripts.logger import log | 30 | from canonical.launchpad.scripts.logger import log |
753 | 35 | from canonical.launchpad.webapp import canonical_url | 31 | from canonical.launchpad.webapp import canonical_url |
754 | 36 | from lp.bugs.interfaces.bugmessage import IBugMessageSet | 32 | from lp.bugs.interfaces.bugmessage import IBugMessageSet |
755 | @@ -38,7 +34,9 @@ | |||
756 | 38 | BugNotificationBuilder, | 34 | BugNotificationBuilder, |
757 | 39 | get_bugmail_from_address, | 35 | get_bugmail_from_address, |
758 | 40 | ) | 36 | ) |
759 | 37 | from lp.bugs.mail.newbug import generate_bug_add_email | ||
760 | 41 | from lp.registry.interfaces.person import IPersonSet | 38 | from lp.registry.interfaces.person import IPersonSet |
761 | 39 | from lp.services.mail.mailwrapper import MailWrapper | ||
762 | 42 | 40 | ||
763 | 43 | 41 | ||
764 | 44 | def construct_email_notifications(bug_notifications): | 42 | def construct_email_notifications(bug_notifications): |
765 | 45 | 43 | ||
766 | === modified file 'lib/lp/bugs/subscribers/bug.py' | |||
767 | --- lib/lp/bugs/subscribers/bug.py 2010-01-08 03:12:30 +0000 | |||
768 | +++ lib/lp/bugs/subscribers/bug.py 2010-08-23 20:18:06 +0000 | |||
769 | @@ -2,19 +2,63 @@ | |||
770 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
771 | 3 | 3 | ||
772 | 4 | __metaclass__ = type | 4 | __metaclass__ = type |
776 | 5 | __all__ = ['notify_bug_modified'] | 5 | __all__ = [ |
777 | 6 | 6 | 'add_bug_change_notifications', | |
778 | 7 | 7 | 'get_bug_delta', | |
779 | 8 | 'get_bugtask_indirect_subscribers', | ||
780 | 9 | 'notify_bug_added', | ||
781 | 10 | 'notify_bug_attachment_added', | ||
782 | 11 | 'notify_bug_attachment_removed', | ||
783 | 12 | 'notify_bug_comment_added', | ||
784 | 13 | 'notify_bug_modified', | ||
785 | 14 | 'notify_bug_subscription_added', | ||
786 | 15 | 'send_bug_details_to_new_bug_subscribers', | ||
787 | 16 | ] | ||
788 | 17 | |||
789 | 18 | |||
790 | 19 | import datetime | ||
791 | 20 | from operator import attrgetter | ||
792 | 21 | |||
793 | 22 | from canonical.config import config | ||
794 | 8 | from canonical.database.sqlbase import block_implicit_flushes | 23 | from canonical.database.sqlbase import block_implicit_flushes |
795 | 24 | from canonical.launchpad.helpers import get_contact_email_addresses | ||
796 | 25 | from canonical.launchpad.mail import ( | ||
797 | 26 | format_address, | ||
798 | 27 | sendmail, | ||
799 | 28 | ) | ||
800 | 29 | from canonical.launchpad.webapp.publisher import canonical_url | ||
801 | 30 | from lp.bugs.adapters.bugchange import ( | ||
802 | 31 | BugDuplicateChange, | ||
803 | 32 | BugTaskAssigneeChange, | ||
804 | 33 | get_bug_changes, | ||
805 | 34 | ) | ||
806 | 35 | from lp.bugs.adapters.bugdelta import BugDelta | ||
807 | 36 | from lp.bugs.interfaces.bugchange import IBugChange | ||
808 | 37 | from lp.bugs.mail.bugnotificationbuilder import BugNotificationBuilder | ||
809 | 38 | from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients | ||
810 | 39 | from lp.bugs.mail.newbug import generate_bug_add_email | ||
811 | 40 | from lp.registry.enum import BugNotificationLevel | ||
812 | 9 | from lp.registry.interfaces.person import IPerson | 41 | from lp.registry.interfaces.person import IPerson |
813 | 42 | from lp.registry.interfaces.structuralsubscription import ( | ||
814 | 43 | IStructuralSubscriptionTarget, | ||
815 | 44 | ) | ||
816 | 45 | |||
817 | 46 | |||
818 | 47 | @block_implicit_flushes | ||
819 | 48 | def notify_bug_added(bug, event): | ||
820 | 49 | """Send an email notification that a bug was added. | ||
821 | 50 | |||
822 | 51 | Event must be an IObjectCreatedEvent. | ||
823 | 52 | """ | ||
824 | 53 | bug.addCommentNotification(bug.initial_message) | ||
825 | 10 | 54 | ||
826 | 11 | 55 | ||
827 | 12 | @block_implicit_flushes | 56 | @block_implicit_flushes |
828 | 13 | def notify_bug_modified(bug, event): | 57 | def notify_bug_modified(bug, event): |
829 | 14 | """Handle bug change events. | 58 | """Handle bug change events. |
830 | 15 | 59 | ||
833 | 16 | Subscribe the security contacts for a bug when it | 60 | Subscribe the security contacts for a bug when it becomes |
834 | 17 | becomes security-related. | 61 | security-related, and add notifications for the changes. |
835 | 18 | """ | 62 | """ |
836 | 19 | if (event.object.security_related and | 63 | if (event.object.security_related and |
837 | 20 | not event.object_before_modification.security_related): | 64 | not event.object_before_modification.security_related): |
838 | @@ -23,3 +67,222 @@ | |||
839 | 23 | for pillar in bug.affected_pillars: | 67 | for pillar in bug.affected_pillars: |
840 | 24 | if pillar.security_contact is not None: | 68 | if pillar.security_contact is not None: |
841 | 25 | bug.subscribe(pillar.security_contact, IPerson(event.user)) | 69 | bug.subscribe(pillar.security_contact, IPerson(event.user)) |
842 | 70 | |||
843 | 71 | bug_delta = get_bug_delta( | ||
844 | 72 | old_bug=event.object_before_modification, | ||
845 | 73 | new_bug=event.object, user=IPerson(event.user)) | ||
846 | 74 | |||
847 | 75 | if bug_delta is not None: | ||
848 | 76 | add_bug_change_notifications(bug_delta) | ||
849 | 77 | |||
850 | 78 | |||
851 | 79 | @block_implicit_flushes | ||
852 | 80 | def notify_bug_comment_added(bugmessage, event): | ||
853 | 81 | """Notify CC'd list that a message was added to this bug. | ||
854 | 82 | |||
855 | 83 | bugmessage must be an IBugMessage. event must be an | ||
856 | 84 | IObjectCreatedEvent. If bugmessage.bug is a duplicate the | ||
857 | 85 | comment will also be sent to the dup target's subscribers. | ||
858 | 86 | """ | ||
859 | 87 | bug = bugmessage.bug | ||
860 | 88 | bug.addCommentNotification(bugmessage.message) | ||
861 | 89 | |||
862 | 90 | |||
863 | 91 | @block_implicit_flushes | ||
864 | 92 | def notify_bug_attachment_added(bugattachment, event): | ||
865 | 93 | """Notify CC'd list that a new attachment has been added. | ||
866 | 94 | |||
867 | 95 | bugattachment must be an IBugAttachment. event must be an | ||
868 | 96 | IObjectCreatedEvent. | ||
869 | 97 | """ | ||
870 | 98 | bug = bugattachment.bug | ||
871 | 99 | bug_delta = BugDelta( | ||
872 | 100 | bug=bug, | ||
873 | 101 | bugurl=canonical_url(bug), | ||
874 | 102 | user=IPerson(event.user), | ||
875 | 103 | attachment={'new': bugattachment, 'old': None}) | ||
876 | 104 | |||
877 | 105 | add_bug_change_notifications(bug_delta) | ||
878 | 106 | |||
879 | 107 | |||
880 | 108 | @block_implicit_flushes | ||
881 | 109 | def notify_bug_attachment_removed(bugattachment, event): | ||
882 | 110 | """Notify that an attachment has been removed.""" | ||
883 | 111 | bug = bugattachment.bug | ||
884 | 112 | bug_delta = BugDelta( | ||
885 | 113 | bug=bug, | ||
886 | 114 | bugurl=canonical_url(bug), | ||
887 | 115 | user=IPerson(event.user), | ||
888 | 116 | attachment={'old': bugattachment, 'new': None}) | ||
889 | 117 | |||
890 | 118 | add_bug_change_notifications(bug_delta) | ||
891 | 119 | |||
892 | 120 | |||
893 | 121 | @block_implicit_flushes | ||
894 | 122 | def notify_bug_subscription_added(bug_subscription, event): | ||
895 | 123 | """Notify that a new bug subscription was added.""" | ||
896 | 124 | # When a user is subscribed to a bug by someone other | ||
897 | 125 | # than themselves, we send them a notification email. | ||
898 | 126 | if bug_subscription.person != bug_subscription.subscribed_by: | ||
899 | 127 | send_bug_details_to_new_bug_subscribers( | ||
900 | 128 | bug_subscription.bug, [], [bug_subscription.person], | ||
901 | 129 | subscribed_by=bug_subscription.subscribed_by) | ||
902 | 130 | |||
903 | 131 | |||
904 | 132 | def get_bug_delta(old_bug, new_bug, user): | ||
905 | 133 | """Compute the delta from old_bug to new_bug. | ||
906 | 134 | |||
907 | 135 | old_bug and new_bug are IBug's. user is an IPerson. Returns an | ||
908 | 136 | IBugDelta if there are changes, or None if there were no changes. | ||
909 | 137 | """ | ||
910 | 138 | changes = {} | ||
911 | 139 | |||
912 | 140 | for field_name in ("title", "description", "name", "private", | ||
913 | 141 | "security_related", "duplicateof", "tags"): | ||
914 | 142 | # fields for which we show old => new when their values change | ||
915 | 143 | old_val = getattr(old_bug, field_name) | ||
916 | 144 | new_val = getattr(new_bug, field_name) | ||
917 | 145 | if old_val != new_val: | ||
918 | 146 | changes[field_name] = {} | ||
919 | 147 | changes[field_name]["old"] = old_val | ||
920 | 148 | changes[field_name]["new"] = new_val | ||
921 | 149 | |||
922 | 150 | if changes: | ||
923 | 151 | changes["bug"] = new_bug | ||
924 | 152 | changes["bug_before_modification"] = old_bug | ||
925 | 153 | changes["bugurl"] = canonical_url(new_bug) | ||
926 | 154 | changes["user"] = user | ||
927 | 155 | return BugDelta(**changes) | ||
928 | 156 | else: | ||
929 | 157 | return None | ||
930 | 158 | |||
931 | 159 | |||
932 | 160 | def get_bugtask_indirect_subscribers(bugtask, recipients=None, level=None): | ||
933 | 161 | """Return the indirect subscribers for a bug task. | ||
934 | 162 | |||
935 | 163 | Return the list of people who should get notifications about | ||
936 | 164 | changes to the task because of having an indirect subscription | ||
937 | 165 | relationship with it (by subscribing to its target, being an | ||
938 | 166 | assignee or owner, etc...) | ||
939 | 167 | |||
940 | 168 | If `recipients` is present, add the subscribers to the set of | ||
941 | 169 | bug notification recipients. | ||
942 | 170 | """ | ||
943 | 171 | if bugtask.bug.private: | ||
944 | 172 | return set() | ||
945 | 173 | |||
946 | 174 | also_notified_subscribers = set() | ||
947 | 175 | |||
948 | 176 | # Assignees are indirect subscribers. | ||
949 | 177 | if bugtask.assignee: | ||
950 | 178 | also_notified_subscribers.add(bugtask.assignee) | ||
951 | 179 | if recipients is not None: | ||
952 | 180 | recipients.addAssignee(bugtask.assignee) | ||
953 | 181 | |||
954 | 182 | if IStructuralSubscriptionTarget.providedBy(bugtask.target): | ||
955 | 183 | also_notified_subscribers.update( | ||
956 | 184 | bugtask.target.getBugNotificationsRecipients( | ||
957 | 185 | recipients, level=level)) | ||
958 | 186 | |||
959 | 187 | if bugtask.milestone is not None: | ||
960 | 188 | also_notified_subscribers.update( | ||
961 | 189 | bugtask.milestone.getBugNotificationsRecipients( | ||
962 | 190 | recipients, level=level)) | ||
963 | 191 | |||
964 | 192 | # If the target's bug supervisor isn't set, | ||
965 | 193 | # we add the owner as a subscriber. | ||
966 | 194 | pillar = bugtask.pillar | ||
967 | 195 | if pillar.bug_supervisor is None: | ||
968 | 196 | also_notified_subscribers.add(pillar.owner) | ||
969 | 197 | if recipients is not None: | ||
970 | 198 | recipients.addRegistrant(pillar.owner, pillar) | ||
971 | 199 | |||
972 | 200 | return sorted( | ||
973 | 201 | also_notified_subscribers, | ||
974 | 202 | key=attrgetter('displayname')) | ||
975 | 203 | |||
976 | 204 | |||
977 | 205 | def add_bug_change_notifications(bug_delta, old_bugtask=None, | ||
978 | 206 | new_subscribers=None): | ||
979 | 207 | """Generate bug notifications and add them to the bug.""" | ||
980 | 208 | changes = get_bug_changes(bug_delta) | ||
981 | 209 | recipients = bug_delta.bug.getBugNotificationRecipients( | ||
982 | 210 | old_bug=bug_delta.bug_before_modification, | ||
983 | 211 | level=BugNotificationLevel.METADATA) | ||
984 | 212 | if old_bugtask is not None: | ||
985 | 213 | old_bugtask_recipients = BugNotificationRecipients() | ||
986 | 214 | get_bugtask_indirect_subscribers( | ||
987 | 215 | old_bugtask, recipients=old_bugtask_recipients, | ||
988 | 216 | level=BugNotificationLevel.METADATA) | ||
989 | 217 | recipients.update(old_bugtask_recipients) | ||
990 | 218 | for change in changes: | ||
991 | 219 | # XXX 2009-03-17 gmb [bug=344125] | ||
992 | 220 | # This if..else should be removed once the new BugChange API | ||
993 | 221 | # is complete and ubiquitous. | ||
994 | 222 | if IBugChange.providedBy(change): | ||
995 | 223 | if isinstance(change, BugDuplicateChange): | ||
996 | 224 | no_dupe_master_recipients = ( | ||
997 | 225 | bug_delta.bug.getBugNotificationRecipients( | ||
998 | 226 | old_bug=bug_delta.bug_before_modification, | ||
999 | 227 | level=BugNotificationLevel.METADATA, | ||
1000 | 228 | include_master_dupe_subscribers=False)) | ||
1001 | 229 | bug_delta.bug.addChange( | ||
1002 | 230 | change, recipients=no_dupe_master_recipients) | ||
1003 | 231 | elif (isinstance(change, BugTaskAssigneeChange) and | ||
1004 | 232 | new_subscribers is not None): | ||
1005 | 233 | for person in new_subscribers: | ||
1006 | 234 | reason, rationale = recipients.getReason(person) | ||
1007 | 235 | if 'Assignee' in rationale: | ||
1008 | 236 | recipients.remove(person) | ||
1009 | 237 | bug_delta.bug.addChange(change, recipients=recipients) | ||
1010 | 238 | else: | ||
1011 | 239 | bug_delta.bug.addChange(change, recipients=recipients) | ||
1012 | 240 | else: | ||
1013 | 241 | bug_delta.bug.addChangeNotification( | ||
1014 | 242 | change, person=bug_delta.user, recipients=recipients) | ||
1015 | 243 | |||
1016 | 244 | |||
1017 | 245 | def send_bug_details_to_new_bug_subscribers( | ||
1018 | 246 | bug, previous_subscribers, current_subscribers, subscribed_by=None, | ||
1019 | 247 | event_creator=None): | ||
1020 | 248 | """Send an email containing full bug details to new bug subscribers. | ||
1021 | 249 | |||
1022 | 250 | This function is designed to handle situations where bugtasks get | ||
1023 | 251 | reassigned to new products or sourcepackages, and the new bug subscribers | ||
1024 | 252 | need to be notified of the bug. | ||
1025 | 253 | """ | ||
1026 | 254 | prev_subs_set = set(previous_subscribers) | ||
1027 | 255 | cur_subs_set = set(current_subscribers) | ||
1028 | 256 | new_subs = cur_subs_set.difference(prev_subs_set) | ||
1029 | 257 | |||
1030 | 258 | to_addrs = set() | ||
1031 | 259 | for new_sub in new_subs: | ||
1032 | 260 | to_addrs.update(get_contact_email_addresses(new_sub)) | ||
1033 | 261 | |||
1034 | 262 | if not to_addrs: | ||
1035 | 263 | return | ||
1036 | 264 | |||
1037 | 265 | from_addr = format_address( | ||
1038 | 266 | 'Launchpad Bug Tracker', | ||
1039 | 267 | "%s@%s" % (bug.id, config.launchpad.bugs_domain)) | ||
1040 | 268 | # Now's a good a time as any for this email; don't use the original | ||
1041 | 269 | # reported date for the bug as it will just confuse mailer and | ||
1042 | 270 | # recipient. | ||
1043 | 271 | email_date = datetime.datetime.now() | ||
1044 | 272 | |||
1045 | 273 | # The new subscriber email is effectively the initial message regarding | ||
1046 | 274 | # a new bug. The bug's initial message is used in the References | ||
1047 | 275 | # header to establish the message's context in the email client. | ||
1048 | 276 | references = [bug.initial_message.rfc822msgid] | ||
1049 | 277 | recipients = bug.getBugNotificationRecipients() | ||
1050 | 278 | |||
1051 | 279 | bug_notification_builder = BugNotificationBuilder(bug, event_creator) | ||
1052 | 280 | for to_addr in sorted(to_addrs): | ||
1053 | 281 | reason, rationale = recipients.getReason(to_addr) | ||
1054 | 282 | subject, contents = generate_bug_add_email( | ||
1055 | 283 | bug, new_recipients=True, subscribed_by=subscribed_by, | ||
1056 | 284 | reason=reason, event_creator=event_creator) | ||
1057 | 285 | msg = bug_notification_builder.build( | ||
1058 | 286 | from_addr, to_addr, contents, subject, email_date, | ||
1059 | 287 | rationale=rationale, references=references) | ||
1060 | 288 | sendmail(msg) | ||
1061 | 26 | 289 | ||
1062 | === modified file 'lib/lp/bugs/subscribers/bugcreation.py' | |||
1063 | --- lib/lp/bugs/subscribers/bugcreation.py 2010-08-20 20:31:18 +0000 | |||
1064 | +++ lib/lp/bugs/subscribers/bugcreation.py 2010-08-23 20:18:06 +0000 | |||
1065 | @@ -2,8 +2,10 @@ | |||
1066 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1067 | 3 | 3 | ||
1068 | 4 | __metaclass__ = type | 4 | __metaclass__ = type |
1069 | 5 | __all__ = [ | ||
1070 | 6 | 'at_least_one_task', | ||
1071 | 7 | ] | ||
1072 | 5 | 8 | ||
1073 | 6 | from canonical.database.sqlbase import block_implicit_flushes | ||
1074 | 7 | from lp.bugs.interfaces.bug import CreatedBugWithNoBugTasksError | 9 | from lp.bugs.interfaces.bug import CreatedBugWithNoBugTasksError |
1075 | 8 | 10 | ||
1076 | 9 | 11 | ||
1077 | 10 | 12 | ||
1078 | === added file 'lib/lp/bugs/subscribers/bugtask.py' | |||
1079 | --- lib/lp/bugs/subscribers/bugtask.py 1970-01-01 00:00:00 +0000 | |||
1080 | +++ lib/lp/bugs/subscribers/bugtask.py 2010-08-23 20:18:06 +0000 | |||
1081 | @@ -0,0 +1,79 @@ | |||
1082 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
1083 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
1084 | 3 | |||
1085 | 4 | __metaclass__ = type | ||
1086 | 5 | __all__ = [ | ||
1087 | 6 | 'notify_bugtask_edited', | ||
1088 | 7 | 'update_security_contact_subscriptions', | ||
1089 | 8 | ] | ||
1090 | 9 | |||
1091 | 10 | |||
1092 | 11 | from canonical.database.sqlbase import block_implicit_flushes | ||
1093 | 12 | from canonical.launchpad.webapp.publisher import canonical_url | ||
1094 | 13 | from lp.bugs.adapters.bugdelta import BugDelta | ||
1095 | 14 | from lp.bugs.interfaces.bugtask import IUpstreamBugTask | ||
1096 | 15 | from lp.bugs.subscribers.bug import ( | ||
1097 | 16 | add_bug_change_notifications, | ||
1098 | 17 | send_bug_details_to_new_bug_subscribers, | ||
1099 | 18 | ) | ||
1100 | 19 | from lp.registry.interfaces.person import IPerson | ||
1101 | 20 | |||
1102 | 21 | |||
1103 | 22 | @block_implicit_flushes | ||
1104 | 23 | def update_security_contact_subscriptions(event): | ||
1105 | 24 | """Subscribe the new security contact when a bugtask's product changes. | ||
1106 | 25 | |||
1107 | 26 | Only subscribes the new security contact if the bug was marked a | ||
1108 | 27 | security issue originally. | ||
1109 | 28 | |||
1110 | 29 | No change is made for private bugs. | ||
1111 | 30 | """ | ||
1112 | 31 | if event.object.bug.private: | ||
1113 | 32 | return | ||
1114 | 33 | |||
1115 | 34 | if not IUpstreamBugTask.providedBy(event.object): | ||
1116 | 35 | return | ||
1117 | 36 | |||
1118 | 37 | bugtask_before_modification = event.object_before_modification | ||
1119 | 38 | bugtask_after_modification = event.object | ||
1120 | 39 | |||
1121 | 40 | if (bugtask_before_modification.product != | ||
1122 | 41 | bugtask_after_modification.product): | ||
1123 | 42 | new_product = bugtask_after_modification.product | ||
1124 | 43 | if (bugtask_before_modification.bug.security_related and | ||
1125 | 44 | new_product.security_contact): | ||
1126 | 45 | bugtask_after_modification.bug.subscribe( | ||
1127 | 46 | new_product.security_contact, IPerson(event.user)) | ||
1128 | 47 | |||
1129 | 48 | |||
1130 | 49 | @block_implicit_flushes | ||
1131 | 50 | def notify_bugtask_edited(modified_bugtask, event): | ||
1132 | 51 | """Notify CC'd subscribers of this bug that something has changed | ||
1133 | 52 | on this task. | ||
1134 | 53 | |||
1135 | 54 | modified_bugtask must be an IBugTask. event must be an | ||
1136 | 55 | IObjectModifiedEvent. | ||
1137 | 56 | """ | ||
1138 | 57 | bugtask_delta = event.object.getDelta(event.object_before_modification) | ||
1139 | 58 | bug_delta = BugDelta( | ||
1140 | 59 | bug=event.object.bug, | ||
1141 | 60 | bugurl=canonical_url(event.object.bug), | ||
1142 | 61 | bugtask_deltas=bugtask_delta, | ||
1143 | 62 | user=IPerson(event.user)) | ||
1144 | 63 | |||
1145 | 64 | event_creator = IPerson(event.user) | ||
1146 | 65 | previous_subscribers = event.object_before_modification.bug_subscribers | ||
1147 | 66 | current_subscribers = event.object.bug_subscribers | ||
1148 | 67 | prev_subs_set = set(previous_subscribers) | ||
1149 | 68 | cur_subs_set = set(current_subscribers) | ||
1150 | 69 | new_subs = cur_subs_set.difference(prev_subs_set) | ||
1151 | 70 | |||
1152 | 71 | add_bug_change_notifications( | ||
1153 | 72 | bug_delta, old_bugtask=event.object_before_modification, | ||
1154 | 73 | new_subscribers=new_subs) | ||
1155 | 74 | |||
1156 | 75 | send_bug_details_to_new_bug_subscribers( | ||
1157 | 76 | event.object.bug, previous_subscribers, current_subscribers, | ||
1158 | 77 | event_creator=event_creator) | ||
1159 | 78 | |||
1160 | 79 | update_security_contact_subscriptions(event) |
This is almost entirely just moving code; I have avoided making changes to the logic.