Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / functions.py @ 1fcf4a99

History | View | Annotate | Download (11.7 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

    
46
from urllib import quote
47
from urlparse import urljoin
48
from smtplib import SMTPException
49
from datetime import datetime
50
from functools import wraps
51

    
52
from astakos.im.settings import DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, \
53
    SITENAME, BASEURL, DEFAULT_ADMIN_EMAIL, LOGGING_LEVEL, \
54
    VERIFICATION_EMAIL_SUBJECT, ADMIN_NOTIFICATION_EMAIL_SUBJECT, \
55
    HELPDESK_NOTIFICATION_EMAIL_SUBJECT, INVITATION_EMAIL_SUBJECT, \
56
    GREETING_EMAIL_SUBJECT, FEEDBACK_EMAIL_SUBJECT, EMAIL_CHANGE_EMAIL_SUBJECT
57
from astakos.im.models import Invitation, AstakosUser
58

    
59
logger = logging.getLogger(__name__)
60

    
61
def logged(func, msg):
62
    @wraps(func)
63
    def with_logging(*args, **kwargs):
64
        email = ''
65
        user = None
66
        if len(args) == 2 and isinstance(args[1], AstakosUser):
67
            user = args[1]
68
        elif len(args) == 1 and isinstance(args[0], HttpRequest):
69
            request = args[0]
70
            user = request.user
71
        email = user.email if user and user.is_authenticated() else ''
72
        r = func(*args, **kwargs)
73
        if LOGGING_LEVEL:
74
            logger._log(LOGGING_LEVEL, msg % email, [])
75
        return r
76
    return with_logging
77

    
78
login = logged(auth_login, '%s logged in.')
79
logout = logged(auth_logout, '%s logged out.')
80

    
81
def send_verification(user, template_name='im/activation_email.txt'):
82
    """
83
    Send email to user to verify his/her email and activate his/her account.
84
    
85
    Raises SendVerificationError
86
    """
87
    url = '%s?auth=%s&next=%s' % (urljoin(BASEURL, reverse('astakos.im.views.activate')),
88
                                    quote(user.auth_token),
89
                                    quote(urljoin(BASEURL, reverse('astakos.im.views.index'))))
90
    message = render_to_string(template_name, {
91
            'user': user,
92
            'url': url,
93
            'baseurl': BASEURL,
94
            'site_name': SITENAME,
95
            'support': DEFAULT_CONTACT_EMAIL})
96
    sender = DEFAULT_FROM_EMAIL
97
    try:
98
        send_mail(_(VERIFICATION_EMAIL_SUBJECT), message, sender, [user.email])
99
    except (SMTPException, socket.error) as e:
100
        logger.exception(e)
101
        raise SendVerificationError()
102
    else:
103
        msg = 'Sent activation %s' % user.email
104
        logger._log(LOGGING_LEVEL, msg, [])
105

    
106
def send_activation(user, template_name='im/activation_email.txt'):
107
    send_verification(user, template_name)
108
    user.activation_sent = datetime.now()
109
    user.save()
110

    
111
def send_admin_notification(user, template_name='im/admin_notification.txt'):
112
    """
113
    Send email to DEFAULT_ADMIN_EMAIL to notify for a new user registration.
114
    
115
    Raises SendNotificationError
116
    """
117
    if not DEFAULT_ADMIN_EMAIL:
118
        return
119
    message = render_to_string(template_name, {
120
            'user': user,
121
            'baseurl': BASEURL,
122
            'site_name': SITENAME,
123
            'support': DEFAULT_CONTACT_EMAIL})
124
    sender = DEFAULT_FROM_EMAIL
125
    try:
126
        send_mail(_(ADMIN_NOTIFICATION_EMAIL_SUBJECT), message, sender, [DEFAULT_ADMIN_EMAIL])
127
    except (SMTPException, socket.error) as e:
128
        logger.exception(e)
129
        raise SendNotificationError()
130
    else:
131
        msg = 'Sent admin notification for user %s' % user.email
132
        logger._log(LOGGING_LEVEL, msg, [])
133

    
134
def send_helpdesk_notification(user, template_name='im/helpdesk_notification.txt'):
135
    """
136
    Send email to DEFAULT_CONTACT_EMAIL to notify for a new user activation.
137
    
138
    Raises SendNotificationError
139
    """
140
    if not DEFAULT_CONTACT_EMAIL:
141
        return
142
    message = render_to_string(template_name, {
143
            'user': user,
144
            'baseurl': BASEURL,
145
            'site_name': SITENAME,
146
            'support': DEFAULT_ADMIN_EMAIL})
147
    sender = DEFAULT_FROM_EMAIL
148
    try:
149
        send_mail(_(HELPDESK_NOTIFICATION_EMAIL_SUBJECT), message, sender, [DEFAULT_CONTACT_EMAIL])
150
    except (SMTPException, socket.error) as e:
151
        logger.exception(e)
152
        raise SendNotificationError()
153
    else:
154
        msg = 'Sent helpdesk admin notification for user %s' % user.email
155
        logger._log(LOGGING_LEVEL, msg, [])
156

    
157
def send_invitation(invitation, template_name='im/invitation.txt'):
158
    """
159
    Send invitation email.
160
    
161
    Raises SendInvitationError
162
    """
163
    subject = _(INVITATION_EMAIL_SUBJECT)
164
    url = '%s?code=%d' % (urljoin(BASEURL, reverse('astakos.im.views.index')), invitation.code)
165
    message = render_to_string('im/invitation.txt', {
166
                'invitation': invitation,
167
                'url': url,
168
                'baseurl': BASEURL,
169
                'site_name': SITENAME,
170
                'support': DEFAULT_CONTACT_EMAIL})
171
    sender = DEFAULT_FROM_EMAIL
172
    try:
173
        send_mail(subject, message, sender, [invitation.username])
174
    except (SMTPException, socket.error) as e:
175
        logger.exception(e)
176
        raise SendInvitationError()
177
    else:
178
        msg = 'Sent invitation %s' % invitation
179
        logger._log(LOGGING_LEVEL, msg, [])
180

    
181
def send_greeting(user, email_template_name='im/welcome_email.txt'):
182
    """
183
    Send welcome email.
184
    
185
    Raises SMTPException, socket.error
186
    """
187
    subject = _(GREETING_EMAIL_SUBJECT)
188
    message = render_to_string(email_template_name, {
189
                'user': user,
190
                'url': urljoin(BASEURL, reverse('astakos.im.views.index')),
191
                'baseurl': BASEURL,
192
                'site_name': SITENAME,
193
                'support': DEFAULT_CONTACT_EMAIL})
194
    sender = DEFAULT_FROM_EMAIL
195
    try:
196
        send_mail(subject, message, sender, [user.email])
197
    except (SMTPException, socket.error) as e:
198
        logger.exception(e)
199
        raise SendGreetingError()
200
    else:
201
        msg = 'Sent greeting %s' % user.email
202
        logger._log(LOGGING_LEVEL, msg, [])
203

    
204
def send_feedback(msg, data, user, email_template_name='im/feedback_mail.txt'):
205
    subject = _(FEEDBACK_EMAIL_SUBJECT)
206
    from_email = user.email
207
    recipient_list = [DEFAULT_CONTACT_EMAIL]
208
    content = render_to_string(email_template_name, {
209
        'message': msg,
210
        'data': data,
211
        'user': user})
212
    try:
213
        send_mail(subject, content, from_email, recipient_list)
214
    except (SMTPException, socket.error) as e:
215
        logger.exception(e)
216
        raise SendFeedbackError()
217
    else:
218
        msg = 'Sent feedback from %s' % user.email
219
        logger._log(LOGGING_LEVEL, msg, [])
220

    
221
def send_change_email(ec, request, email_template_name='registration/email_change_email.txt'):
222
    try:
223
        url = reverse('email_change_confirm',
224
                      kwargs={'activation_key':ec.activation_key})
225
        url = request.build_absolute_uri(url)
226
        t = loader.get_template(email_template_name)
227
        c = {'url': url, 'site_name': SITENAME}
228
        from_email = DEFAULT_FROM_EMAIL
229
        send_mail(_(EMAIL_CHANGE_EMAIL_SUBJECT),
230
            t.render(Context(c)), from_email, [ec.new_email_address])
231
    except (SMTPException, socket.error) as e:
232
        logger.exception(e)
233
        raise ChangeEmailError()
234
    else:
235
        msg = 'Sent change email for %s' % ec.user.email
236
        logger._log(LOGGING_LEVEL, msg, [])
237

    
238
def activate(user, email_template_name='im/welcome_email.txt',
239
                helpdesk_email_template_name='im/helpdesk_notification.txt', verify_email=False):
240
    """
241
    Activates the specific user and sends email.
242
    
243
    Raises SendGreetingError, ValidationError
244
    """
245
    user.is_active = True
246
    if verify_email:
247
        user.email_verified = True
248
    user.save()
249
    send_helpdesk_notification(user, helpdesk_email_template_name)
250
    send_greeting(user, email_template_name)
251

    
252
def switch_account_to_shibboleth(user, local_user, greeting_template_name='im/welcome_email.txt'):
253
    if not user or not isinstance(user, AstakosUser):
254
        return
255
    
256
    if not local_user or not isinstance(user, AstakosUser):
257
        return
258
    
259
    if not user.provider == 'shibboleth':
260
        return
261
    
262
    user.delete()
263
    local_user.provider = 'shibboleth'
264
    local_user.third_party_identifier = user.third_party_identifier
265
    local_user.save()
266
    send_greeting(local_user, greeting_template_name)
267
    return local_user
268

    
269
def invite(invitation, inviter, email_template_name='im/welcome_email.txt'):
270
    """
271
    Send an invitation email and upon success reduces inviter's invitation by one.
272
    
273
    Raises SendInvitationError
274
    """
275
    invitation.inviter = inviter
276
    invitation.save()
277
    send_invitation(invitation, email_template_name)
278
    inviter.invitations = max(0, inviter.invitations - 1)
279
    inviter.save()
280

    
281
def set_user_credibility(email, has_credits):
282
    try:
283
        user = AstakosUser.objects.get(email=email, is_active=True)
284
        user.has_credits = has_credits
285
        user.save()
286
    except AstakosUser.DoesNotExist, e:
287
        logger.exception(e)
288
    except ValidationError, e:
289
        logger.exception(e)
290

    
291
class SendMailError(Exception):
292
    pass
293

    
294
class SendAdminNotificationError(SendMailError):
295
    def __init__(self):
296
        self.message = _('Failed to send notification')
297
        super(SendAdminNotificationError, self).__init__()
298

    
299
class SendVerificationError(SendMailError):
300
    def __init__(self):
301
        self.message = _('Failed to send verification')
302
        super(SendVerificationError, self).__init__()
303

    
304
class SendInvitationError(SendMailError):
305
    def __init__(self):
306
        self.message = _('Failed to send invitation')
307
        super(SendInvitationError, self).__init__()
308

    
309
class SendGreetingError(SendMailError):
310
    def __init__(self):
311
        self.message = _('Failed to send greeting')
312
        super(SendGreetingError, self).__init__()
313

    
314
class SendFeedbackError(SendMailError):
315
    def __init__(self):
316
        self.message = _('Failed to send feedback')
317
        super(SendFeedbackError, self).__init__()
318

    
319
class ChangeEmailError(SendMailError):
320
    def __init__(self):
321
        self.message = _('Failed to send change email')
322
        super(ChangeEmailError, self).__init__()
323

    
324
class SendNotificationError(SendMailError):
325
    def __init__(self):
326
        self.message = _('Failed to send notification email')
327
        super(SendNotificationError, self).__init__()