Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / functions.py @ 674f9a52

History | View | Annotate | Download (12 kB)

1
# Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import logging
35
import socket
36

    
37
from django.utils.translation import ugettext as _
38
from django.template.loader import render_to_string
39
from django.core.mail import send_mail
40
from django.core.urlresolvers import reverse
41
from django.core.exceptions import ValidationError
42
from django.template import Context, loader
43
from django.contrib.auth import login as auth_login, logout as auth_logout
44
from django.http import HttpRequest
45
from django.conf import settings
46
from django.contrib.auth.models import AnonymousUser
47

    
48
from urllib import quote
49
from urlparse import urljoin
50
from smtplib import SMTPException
51
from datetime import datetime
52
from functools import wraps
53

    
54
from astakos.im.settings import (DEFAULT_CONTACT_EMAIL, SITENAME, BASEURL,
55
                                 LOGGING_LEVEL, VERIFICATION_EMAIL_SUBJECT,
56
                                 ACCOUNT_CREATION_SUBJECT,
57
                                 GROUP_CREATION_SUBJECT,
58
                                 HELPDESK_NOTIFICATION_EMAIL_SUBJECT,
59
                                 INVITATION_EMAIL_SUBJECT,
60
                                 GREETING_EMAIL_SUBJECT,
61
                                 FEEDBACK_EMAIL_SUBJECT,
62
                                 EMAIL_CHANGE_EMAIL_SUBJECT)
63
import astakos.im.models
64
import astakos.im.messages as astakos_messages
65

    
66
logger = logging.getLogger(__name__)
67

    
68

    
69
def logged(func, msg):
70
    @wraps(func)
71
    def with_logging(*args, **kwargs):
72
        email = ''
73
        user = None
74
        try:
75
            request = args[0]
76
            email = request.user.email
77
        except (KeyError, AttributeError), e:
78
            email = ''
79
        r = func(*args, **kwargs)
80
        if LOGGING_LEVEL:
81
            logger.log(LOGGING_LEVEL, msg % email)
82
        return r
83
    return with_logging
84

    
85
login = logged(auth_login, '%s logged in.')
86
logout = logged(auth_logout, '%s logged out.')
87

    
88

    
89
def send_verification(user, template_name='im/activation_email.txt'):
90
    """
91
    Send email to user to verify his/her email and activate his/her account.
92

93
    Raises SendVerificationError
94
    """
95
    url = '%s?auth=%s&next=%s' % (urljoin(BASEURL, reverse('activate')),
96
                                  quote(user.auth_token),
97
                                  quote(urljoin(BASEURL, reverse('index'))))
98
    message = render_to_string(template_name, {
99
                               'user': user,
100
                               'url': url,
101
                               'baseurl': BASEURL,
102
                               'site_name': SITENAME,
103
                               'support': DEFAULT_CONTACT_EMAIL})
104
    sender = settings.SERVER_EMAIL
105
    try:
106
        send_mail(_(VERIFICATION_EMAIL_SUBJECT), message, sender, [user.email])
107
    except (SMTPException, socket.error) as e:
108
        logger.exception(e)
109
        raise SendVerificationError()
110
    else:
111
        msg = 'Sent activation %s' % user.email
112
        logger.log(LOGGING_LEVEL, msg)
113

    
114

    
115
def send_activation(user, template_name='im/activation_email.txt'):
116
    send_verification(user, template_name)
117
    user.activation_sent = datetime.now()
118
    user.save()
119

    
120

    
121
def _send_admin_notification(template_name,
122
                             dictionary=None,
123
                             subject='alpha2 testing notification',):
124
    """
125
    Send notification email to settings.ADMINS.
126

127
    Raises SendNotificationError
128
    """
129
    if not settings.ADMINS:
130
        return
131
    dictionary = dictionary or {}
132
    message = render_to_string(template_name, dictionary)
133
    sender = settings.SERVER_EMAIL
134
    try:
135
        send_mail(subject,
136
                  message, sender, [i[1] for i in settings.ADMINS])
137
    except (SMTPException, socket.error) as e:
138
        logger.exception(e)
139
        raise SendNotificationError()
140
    else:
141
        msg = 'Sent admin notification for user %s' % dictionary
142
        logger.log(LOGGING_LEVEL, msg)
143

    
144

    
145
def send_account_creation_notification(template_name, dictionary=None):
146
    user = dictionary.get('user', AnonymousUser())
147
    subject = _(ACCOUNT_CREATION_SUBJECT) % {'user':user.get('email', '')}
148
    return _send_admin_notification(template_name, dictionary, subject=subject)
149

    
150

    
151
def send_group_creation_notification(template_name, dictionary=None):
152
    group = dictionary.get('group', astakos.im.models.AstakosGroup())
153
    subject = _(GROUP_CREATION_SUBJECT) % {'group':group.get('name', '')}
154
    return _send_admin_notification(template_name, dictionary, subject=subject)
155

    
156

    
157
def send_helpdesk_notification(user, template_name='im/account_notification.txt'):
158
    """
159
    Send email to DEFAULT_CONTACT_EMAIL to notify for a new user activation.
160

161
    Raises SendNotificationError
162
    """
163
    if not DEFAULT_CONTACT_EMAIL:
164
        return
165
    message = render_to_string(
166
        template_name,
167
        {'user': user}
168
    )
169
    sender = settings.SERVER_EMAIL
170
    try:
171
        send_mail(
172
            _(HELPDESK_NOTIFICATION_EMAIL_SUBJECT) % {'user': user.email},
173
            message, sender, [DEFAULT_CONTACT_EMAIL])
174
    except (SMTPException, socket.error) as e:
175
        logger.exception(e)
176
        raise SendNotificationError()
177
    else:
178
        msg = 'Sent helpdesk admin notification for %s' % user.email
179
        logger.log(LOGGING_LEVEL, msg)
180

    
181

    
182
def send_invitation(invitation, template_name='im/invitation.txt'):
183
    """
184
    Send invitation email.
185

186
    Raises SendInvitationError
187
    """
188
    subject = _(INVITATION_EMAIL_SUBJECT)
189
    url = '%s?code=%d' % (urljoin(BASEURL, reverse('index')), invitation.code)
190
    message = render_to_string(template_name, {
191
                               'invitation': invitation,
192
                               'url': url,
193
                               'baseurl': BASEURL,
194
                               'site_name': SITENAME,
195
                               'support': DEFAULT_CONTACT_EMAIL})
196
    sender = settings.SERVER_EMAIL
197
    try:
198
        send_mail(subject, message, sender, [invitation.username])
199
    except (SMTPException, socket.error) as e:
200
        logger.exception(e)
201
        raise SendInvitationError()
202
    else:
203
        msg = 'Sent invitation %s' % invitation
204
        logger.log(LOGGING_LEVEL, msg)
205
        invitation.inviter.invitations = max(0, self.invitations - 1)
206
        invitation.inviter.save()
207

    
208

    
209
def send_greeting(user, email_template_name='im/welcome_email.txt'):
210
    """
211
    Send welcome email.
212

213
    Raises SMTPException, socket.error
214
    """
215
    subject = _(GREETING_EMAIL_SUBJECT)
216
    message = render_to_string(email_template_name, {
217
                               'user': user,
218
                               'url': urljoin(BASEURL, reverse('index')),
219
                               'baseurl': BASEURL,
220
                               'site_name': SITENAME,
221
                               'support': DEFAULT_CONTACT_EMAIL})
222
    sender = settings.SERVER_EMAIL
223
    try:
224
        send_mail(subject, message, sender, [user.email])
225
    except (SMTPException, socket.error) as e:
226
        logger.exception(e)
227
        raise SendGreetingError()
228
    else:
229
        msg = 'Sent greeting %s' % user.email
230
        logger.log(LOGGING_LEVEL, msg)
231

    
232

    
233
def send_feedback(msg, data, user, email_template_name='im/feedback_mail.txt'):
234
    subject = _(FEEDBACK_EMAIL_SUBJECT)
235
    from_email = user.email
236
    recipient_list = [DEFAULT_CONTACT_EMAIL]
237
    content = render_to_string(email_template_name, {
238
        'message': msg,
239
        'data': data,
240
        'user': user})
241
    try:
242
        send_mail(subject, content, from_email, recipient_list)
243
    except (SMTPException, socket.error) as e:
244
        logger.exception(e)
245
        raise SendFeedbackError()
246
    else:
247
        msg = 'Sent feedback from %s' % user.email
248
        logger.log(LOGGING_LEVEL, msg)
249

    
250

    
251
def send_change_email(ec, request, email_template_name='registration/email_change_email.txt'):
252
    try:
253
        url = reverse('email_change_confirm',
254
                      kwargs={'activation_key': ec.activation_key})
255
        url = request.build_absolute_uri(url)
256
        t = loader.get_template(email_template_name)
257
        c = {'url': url, 'site_name': SITENAME}
258
        from_email = settings.SERVER_EMAIL
259
        send_mail(_(EMAIL_CHANGE_EMAIL_SUBJECT),
260
                  t.render(Context(c)), from_email, [ec.new_email_address])
261
    except (SMTPException, socket.error) as e:
262
        logger.exception(e)
263
        raise ChangeEmailError()
264
    else:
265
        msg = 'Sent change email for %s' % ec.user.email
266
        logger.log(LOGGING_LEVEL, msg)
267

    
268

    
269
def activate(user, email_template_name='im/welcome_email.txt',
270
             helpdesk_email_template_name='im/helpdesk_notification.txt', verify_email=False):
271
    """
272
    Activates the specific user and sends email.
273

274
    Raises SendGreetingError, ValidationError
275
    """
276
    user.is_active = True
277
    if verify_email:
278
        user.email_verified = True
279
    user.save()
280
    send_helpdesk_notification(user, helpdesk_email_template_name)
281
    send_greeting(user, email_template_name)
282

    
283

    
284
def switch_account_to_shibboleth(user, local_user,
285
                                 greeting_template_name='im/welcome_email.txt'):
286
    try:
287
        provider = user.provider
288
    except AttributeError:
289
        return
290
    else:
291
        if not provider == 'shibboleth':
292
            return
293
        user.delete()
294
        local_user.provider = 'shibboleth'
295
        local_user.third_party_identifier = user.third_party_identifier
296
        local_user.save()
297
        send_greeting(local_user, greeting_template_name)
298
        return local_user
299

    
300

    
301
class SendMailError(Exception):
302
    pass
303

    
304

    
305
class SendAdminNotificationError(SendMailError):
306
    def __init__(self):
307
        self.message = _(astakos_messages.ADMIN_NOTIFICATION_SEND_ERR)
308
        super(SendAdminNotificationError, self).__init__()
309

    
310

    
311
class SendVerificationError(SendMailError):
312
    def __init__(self):
313
        self.message = _(astakos_messages.VERIFICATION_SEND_ERR)
314
        super(SendVerificationError, self).__init__()
315

    
316

    
317
class SendInvitationError(SendMailError):
318
    def __init__(self):
319
        self.message = _(astakos_messages.INVITATION_SEND_ERR)
320
        super(SendInvitationError, self).__init__()
321

    
322

    
323
class SendGreetingError(SendMailError):
324
    def __init__(self):
325
        self.message = _(astakos_messages.GREETING_SEND_ERR)
326
        super(SendGreetingError, self).__init__()
327

    
328

    
329
class SendFeedbackError(SendMailError):
330
    def __init__(self):
331
        self.message = _(astakos_messages.FEEDBACK_SEND_ERR)
332
        super(SendFeedbackError, self).__init__()
333

    
334

    
335
class ChangeEmailError(SendMailError):
336
    def __init__(self):
337
        self.message = self.message = _(astakos_messages.CHANGE_EMAIL_SEND_ERR)
338
        super(ChangeEmailError, self).__init__()
339

    
340

    
341
class SendNotificationError(SendMailError):
342
    def __init__(self):
343
        self.message = _(astakos_messages.NOTIFICATION_SEND_ERR)
344
        super(SendNotificationError, self).__init__()