~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/services/mail/notification.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-12-22 04:45:35 UTC
  • mfrom: (14565.2.24 apocalyptic-pieces)
  • Revision ID: launchpad@pqm.canonical.com-20111222044535-jbjyzq3hzwiy7g20
[rs=sinzui][no-qa] Move javascript and scripts to lp. Dismantle
 mailnotification.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
2
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
 
 
4
 
# XXX: Gavin Panella 2008-11-21 bug=300725: This module need
5
 
# refactoring and/or splitting into a package or packages.
6
 
 
7
3
"""Event handlers that send email notifications."""
8
4
 
9
5
__metaclass__ = type
 
6
__all__ = [
 
7
    'send_process_error_notification',
 
8
    'get_unified_diff',
 
9
    ]
 
10
 
10
11
 
11
12
from difflib import unified_diff
12
13
from email import message_from_string
15
16
from email.MIMEText import MIMEText
16
17
import re
17
18
 
18
 
from zope.component import getUtility
19
19
 
20
20
from canonical.config import config
21
 
from canonical.database.sqlbase import block_implicit_flushes
22
 
from canonical.launchpad.helpers import (
23
 
    get_contact_email_addresses,
24
 
    get_email_template,
25
 
    )
26
 
from canonical.launchpad.webapp.interfaces import ILaunchpadRoot
27
 
from canonical.launchpad.webapp.publisher import canonical_url
28
 
from lp.blueprints.interfaces.specification import ISpecification
 
21
from canonical.launchpad.helpers import get_email_template
29
22
from lp.bugs.mail.bugnotificationbuilder import get_bugmail_error_address
30
 
from lp.registry.interfaces.person import IPerson
31
23
from lp.services.mail.mailwrapper import MailWrapper
32
 
# XXX 2010-06-16 gmb bug=594985
33
 
#     This shouldn't be here, but if we take it out lots of things cry,
34
 
#     which is sad.
35
 
from lp.services.mail.notificationrecipientset import NotificationRecipientSet
36
 
from lp.services.mail.sendmail import (
37
 
    format_address,
38
 
    sendmail,
39
 
    simple_sendmail,
40
 
    simple_sendmail_from_person,
41
 
    )
42
 
 
43
 
# Silence lint warnings.
44
 
NotificationRecipientSet
 
24
from lp.services.mail.sendmail import sendmail
45
25
 
46
26
 
47
27
CC = "CC"
105
85
    width.
106
86
 
107
87
        >>> print get_unified_diff(
108
 
        ...     'Some text\nAnother line\n',
 
88
        ...     'Some text\nAnother line\n',get_un
109
89
        ...     'Some more text\nAnother line\n',
110
90
        ...     text_width=72)
111
91
        - Some text
136
116
        for line in text_diff]
137
117
    text_diff = '\n'.join(text_diff)
138
118
    return text_diff
139
 
 
140
 
 
141
 
def specification_notification_subject(spec):
142
 
    """Format the email subject line for a specification."""
143
 
    return '[Blueprint %s] %s' % (spec.name, spec.title)
144
 
 
145
 
 
146
 
@block_implicit_flushes
147
 
def notify_specification_modified(spec, event):
148
 
    """Notify the related people that a specification has been modifed."""
149
 
    user = IPerson(event.user)
150
 
    spec_delta = spec.getDelta(event.object_before_modification, user)
151
 
    if spec_delta is None:
152
 
        # XXX: Bjorn Tillenius 2006-03-08:
153
 
        #      Ideally, if an IObjectModifiedEvent event is generated,
154
 
        #      spec_delta shouldn't be None. I'm not confident that we
155
 
        #      have enough test yet to assert this, though.
156
 
        return
157
 
 
158
 
    subject = specification_notification_subject(spec)
159
 
    indent = ' ' * 4
160
 
    info_lines = []
161
 
    for dbitem_name in ('definition_status', 'priority'):
162
 
        title = ISpecification[dbitem_name].title
163
 
        assert ISpecification[dbitem_name].required, (
164
 
            "The mail notification assumes %s can't be None" % dbitem_name)
165
 
        dbitem_delta = getattr(spec_delta, dbitem_name)
166
 
        if dbitem_delta is not None:
167
 
            old_item = dbitem_delta['old']
168
 
            new_item = dbitem_delta['new']
169
 
            info_lines.append("%s%s: %s => %s" % (
170
 
                indent, title, old_item.title, new_item.title))
171
 
 
172
 
    for person_attrname in ('approver', 'assignee', 'drafter'):
173
 
        title = ISpecification[person_attrname].title
174
 
        person_delta = getattr(spec_delta, person_attrname)
175
 
        if person_delta is not None:
176
 
            old_person = person_delta['old']
177
 
            if old_person is None:
178
 
                old_value = "(none)"
179
 
            else:
180
 
                old_value = old_person.displayname
181
 
            new_person = person_delta['new']
182
 
            if new_person is None:
183
 
                new_value = "(none)"
184
 
            else:
185
 
                new_value = new_person.displayname
186
 
            info_lines.append(
187
 
                "%s%s: %s => %s" % (indent, title, old_value, new_value))
188
 
 
189
 
    mail_wrapper = MailWrapper(width=72)
190
 
    if spec_delta.whiteboard is not None:
191
 
        if info_lines:
192
 
            info_lines.append('')
193
 
        whiteboard_delta = spec_delta.whiteboard
194
 
        if whiteboard_delta['old'] is None:
195
 
            info_lines.append('Whiteboard set to:')
196
 
            info_lines.append(mail_wrapper.format(whiteboard_delta['new']))
197
 
        else:
198
 
            whiteboard_diff = get_unified_diff(
199
 
                whiteboard_delta['old'], whiteboard_delta['new'], 72)
200
 
            info_lines.append('Whiteboard changed:')
201
 
            info_lines.append(whiteboard_diff)
202
 
 
203
 
    if not info_lines:
204
 
        # The specification was modified, but we don't yet support
205
 
        # sending notification for the change.
206
 
        return
207
 
    body = get_email_template('specification-modified.txt', 'blueprints') % {
208
 
        'editor': user.displayname,
209
 
        'info_fields': '\n'.join(info_lines),
210
 
        'spec_title': spec.title,
211
 
        'spec_url': canonical_url(spec)}
212
 
 
213
 
    for address in spec.notificationRecipientAddresses():
214
 
        simple_sendmail_from_person(user, address, subject, body)
215
 
 
216
 
 
217
 
@block_implicit_flushes
218
 
def notify_specification_subscription_created(specsub, event):
219
 
    """Notify a user that they have been subscribed to a blueprint."""
220
 
    user = IPerson(event.user)
221
 
    spec = specsub.specification
222
 
    person = specsub.person
223
 
    subject = specification_notification_subject(spec)
224
 
    mailwrapper = MailWrapper(width=72)
225
 
    body = mailwrapper.format(
226
 
        'You are now subscribed to the blueprint '
227
 
        '%(blueprint_name)s - %(blueprint_title)s.\n\n'
228
 
        '-- \n%(blueprint_url)s' %
229
 
        {'blueprint_name': spec.name,
230
 
         'blueprint_title': spec.title,
231
 
         'blueprint_url': canonical_url(spec)})
232
 
    for address in get_contact_email_addresses(person):
233
 
        simple_sendmail_from_person(user, address, subject, body)
234
 
 
235
 
 
236
 
@block_implicit_flushes
237
 
def notify_specification_subscription_modified(specsub, event):
238
 
    """Notify a subscriber to a blueprint that their
239
 
    subscription has changed.
240
 
    """
241
 
    user = IPerson(event.user)
242
 
    spec = specsub.specification
243
 
    person = specsub.person
244
 
    # Only send a notification if the
245
 
    # subscription changed by someone else.
246
 
    if person == user:
247
 
        return
248
 
    subject = specification_notification_subject(spec)
249
 
    if specsub.essential:
250
 
        specsub_type = 'Participation essential'
251
 
    else:
252
 
        specsub_type = 'Participation non-essential'
253
 
    mailwrapper = MailWrapper(width=72)
254
 
    body = mailwrapper.format(
255
 
        'Your subscription to the blueprint '
256
 
        '%(blueprint_name)s - %(blueprint_title)s '
257
 
        'has changed to [%(specsub_type)s].\n\n'
258
 
        '--\n  %(blueprint_url)s' %
259
 
        {'blueprint_name': spec.name,
260
 
         'blueprint_title': spec.title,
261
 
         'specsub_type': specsub_type,
262
 
         'blueprint_url': canonical_url(spec)})
263
 
    for address in get_contact_email_addresses(person):
264
 
        simple_sendmail_from_person(user, address, subject, body)
265
 
 
266
 
 
267
 
@block_implicit_flushes
268
 
def notify_new_ppa_subscription(subscription, event):
269
 
    """Notification that a new PPA subscription can be activated."""
270
 
    non_active_subscribers = subscription.getNonActiveSubscribers()
271
 
 
272
 
    archive = subscription.archive
273
 
 
274
 
    # We don't send notification emails for commercial PPAs as these
275
 
    # are purchased via software center (and do not mention Launchpad).
276
 
    if archive.commercial:
277
 
        return
278
 
 
279
 
    registrant_name = subscription.registrant.displayname
280
 
    ppa_displayname = archive.displayname
281
 
    ppa_reference = "ppa:%s/%s" % (
282
 
        archive.owner.name, archive.name)
283
 
    ppa_description = archive.description
284
 
    subject = 'PPA access granted for ' + ppa_displayname
285
 
 
286
 
    template = get_email_template('ppa-subscription-new.txt', app='soyuz')
287
 
 
288
 
    for person, preferred_email in non_active_subscribers:
289
 
        to_address = [preferred_email.email]
290
 
        root = getUtility(ILaunchpadRoot)
291
 
        recipient_subscriptions_url = "%s~/+archivesubscriptions" % (
292
 
            canonical_url(root))
293
 
        description_blurb = '.'
294
 
        if ppa_description is not None and ppa_description != '':
295
 
            description_blurb = (
296
 
                ' and has the following description:\n\n%s' % ppa_description)
297
 
        replacements = {
298
 
            'recipient_name': person.displayname,
299
 
            'registrant_name': registrant_name,
300
 
            'registrant_profile_url': canonical_url(subscription.registrant),
301
 
            'ppa_displayname': ppa_displayname,
302
 
            'ppa_reference': ppa_reference,
303
 
            'ppa_description_blurb': description_blurb,
304
 
            'recipient_subscriptions_url': recipient_subscriptions_url,
305
 
            }
306
 
        body = MailWrapper(72).format(template % replacements,
307
 
                                      force_wrap=True)
308
 
 
309
 
        from_address = format_address(
310
 
            registrant_name, config.canonical.noreply_from_address)
311
 
 
312
 
        headers = {
313
 
            'Sender': config.canonical.bounce_address,
314
 
            }
315
 
 
316
 
        # If the registrant has a preferred email, then use it for the
317
 
        # Reply-To.
318
 
        if subscription.registrant.preferredemail:
319
 
            headers['Reply-To'] = format_address(
320
 
                registrant_name,
321
 
                subscription.registrant.preferredemail.email)
322
 
 
323
 
        simple_sendmail(from_address, to_address, subject, body, headers)