rewrite ``activate`` view
[astakos] / snf-astakos-app / astakos / im / functions.py
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 from astakos.im.models import Invitation, AstakosUser
55
56 logger = logging.getLogger(__name__)
57
58 def logged(func, msg):
59     @wraps(func)
60     def with_logging(*args, **kwargs):
61         email = ''
62         user = None
63         if len(args) == 2 and isinstance(args[1], AstakosUser):
64             user = args[1]
65         elif len(args) == 1 and isinstance(args[0], HttpRequest):
66             request = args[0]
67             user = request.user
68         email = user.email if user and user.is_authenticated() else ''
69         r = func(*args, **kwargs)
70         if LOGGING_LEVEL:
71             logger._log(LOGGING_LEVEL, msg % email, [])
72         return r
73     return with_logging
74
75 login = logged(auth_login, '%s logged in.')
76 logout = logged(auth_logout, '%s logged out.')
77
78 def send_verification(user, template_name='im/activation_email.txt'):
79     """
80     Send email to user to verify his/her email and activate his/her account.
81     
82     Raises SendVerificationError
83     """
84     url = '%s?auth=%s&next=%s' % (urljoin(BASEURL, reverse('astakos.im.views.activate')),
85                                     quote(user.auth_token),
86                                     quote(urljoin(BASEURL, reverse('astakos.im.views.index'))))
87     message = render_to_string(template_name, {
88             'user': user,
89             'url': url,
90             'baseurl': BASEURL,
91             'site_name': SITENAME,
92             'support': DEFAULT_CONTACT_EMAIL})
93     sender = DEFAULT_FROM_EMAIL
94     try:
95         send_mail('%s alpha2 testing account activation is needed' % SITENAME, message, sender, [user.email])
96     except (SMTPException, socket.error) as e:
97         logger.exception(e)
98         raise SendVerificationError()
99     else:
100         msg = 'Sent activation %s' % user.email
101         logger._log(LOGGING_LEVEL, msg, [])
102
103 def send_activation(user, template_name='im/activation_email.txt'):
104     send_verification(user, template_name)
105     user.activation_sent = datetime.now()
106     user.save()
107
108 def send_admin_notification(user, template_name='im/admin_notification.txt'):
109     """
110     Send email to DEFAULT_ADMIN_EMAIL to notify for a new user registration.
111     
112     Raises SendNotificationError
113     """
114     if not DEFAULT_ADMIN_EMAIL:
115         return
116     message = render_to_string(template_name, {
117             'user': user,
118             'baseurl': BASEURL,
119             'site_name': SITENAME,
120             'support': DEFAULT_CONTACT_EMAIL})
121     sender = DEFAULT_FROM_EMAIL
122     try:
123         send_mail('%s alpha2 testing account notification' % SITENAME, message, sender, [DEFAULT_ADMIN_EMAIL])
124     except (SMTPException, socket.error) as e:
125         logger.exception(e)
126         raise SendNotificationError()
127     else:
128         msg = 'Sent admin notification for user %s' % user.email
129         logger._log(LOGGING_LEVEL, msg, [])
130
131 def send_helpdesk_notification(user, template_name='im/helpdesk_notification.txt'):
132     """
133     Send email to DEFAULT_CONTACT_EMAIL to notify for a new user activation.
134     
135     Raises SendNotificationError
136     """
137     if not DEFAULT_CONTACT_EMAIL:
138         return
139     message = render_to_string(template_name, {
140             'user': user,
141             'baseurl': BASEURL,
142             'site_name': SITENAME,
143             'support': DEFAULT_ADMIN_EMAIL})
144     sender = DEFAULT_FROM_EMAIL
145     try:
146         send_mail('%s alpha2 testing account notification' % SITENAME, message, sender, [DEFAULT_CONTACT_EMAIL])
147     except (SMTPException, socket.error) as e:
148         logger.exception(e)
149         raise SendNotificationError()
150     else:
151         msg = 'Sent helpdesk admin notification for user %s' % user.email
152         logger._log(LOGGING_LEVEL, msg, [])
153
154 def send_invitation(invitation, template_name='im/invitation.txt'):
155     """
156     Send invitation email.
157     
158     Raises SendInvitationError
159     """
160     subject = _('Invitation to %s alpha2 testing' % SITENAME)
161     url = '%s?code=%d' % (urljoin(BASEURL, reverse('astakos.im.views.index')), invitation.code)
162     message = render_to_string('im/invitation.txt', {
163                 'invitation': invitation,
164                 'url': url,
165                 'baseurl': BASEURL,
166                 'site_name': SITENAME,
167                 'support': DEFAULT_CONTACT_EMAIL})
168     sender = DEFAULT_FROM_EMAIL
169     try:
170         send_mail(subject, message, sender, [invitation.username])
171     except (SMTPException, socket.error) as e:
172         logger.exception(e)
173         raise SendInvitationError()
174     else:
175         msg = 'Sent invitation %s' % invitation
176         logger._log(LOGGING_LEVEL, msg, [])
177
178 def send_greeting(user, email_template_name='im/welcome_email.txt'):
179     """
180     Send welcome email.
181     
182     Raises SMTPException, socket.error
183     """
184     subject = _('Welcome to %s alpha2 testing' % SITENAME)
185     message = render_to_string(email_template_name, {
186                 'user': user,
187                 'url': urljoin(BASEURL, reverse('astakos.im.views.index')),
188                 'baseurl': BASEURL,
189                 'site_name': SITENAME,
190                 'support': DEFAULT_CONTACT_EMAIL})
191     sender = DEFAULT_FROM_EMAIL
192     try:
193         send_mail(subject, message, sender, [user.email])
194     except (SMTPException, socket.error) as e:
195         logger.exception(e)
196         raise SendGreetingError()
197     else:
198         msg = 'Sent greeting %s' % user.email
199         logger._log(LOGGING_LEVEL, msg, [])
200
201 def send_feedback(msg, data, user, email_template_name='im/feedback_mail.txt'):
202     subject = _("Feedback from %s alpha2 testing" % SITENAME)
203     from_email = user.email
204     recipient_list = [DEFAULT_CONTACT_EMAIL]
205     content = render_to_string(email_template_name, {
206         'message': msg,
207         'data': data,
208         'user': user})
209     try:
210         send_mail(subject, content, from_email, recipient_list)
211     except (SMTPException, socket.error) as e:
212         logger.exception(e)
213         raise SendFeedbackError()
214     else:
215         msg = 'Sent feedback from %s' % user.email
216         logger._log(LOGGING_LEVEL, msg, [])
217
218 def send_change_email(ec, request, email_template_name='registration/email_change_email.txt'):
219     try:
220         url = reverse('email_change_confirm',
221                       kwargs={'activation_key':ec.activation_key})
222         url = request.build_absolute_uri(url)
223         t = loader.get_template(email_template_name)
224         c = {'url': url, 'site_name': SITENAME}
225         from_email = DEFAULT_FROM_EMAIL
226         send_mail(_("Email change on %s alpha2 testing") % SITENAME,
227             t.render(Context(c)), from_email, [ec.new_email_address])
228     except (SMTPException, socket.error) as e:
229         logger.exception(e)
230         raise ChangeEmailError()
231     else:
232         msg = 'Sent change email for %s' % ec.user.email
233         logger._log(LOGGING_LEVEL, msg, [])
234
235 def activate(user, email_template_name='im/welcome_email.txt',
236                 helpdesk_email_template_name='im/helpdesk_notification.txt', verify_email=False):
237     """
238     Activates the specific user and sends email.
239     
240     Raises SendGreetingError, ValidationError
241     """
242     user.is_active = True
243     if verify_email:
244         user.email_verified = True
245     user.save()
246     send_helpdesk_notification(user, helpdesk_email_template_name)
247     send_greeting(user, email_template_name)
248
249 def switch_account_to_shibboleth(user, local_user):
250     if not user or not isinstance(user, AstakosUser):
251         return
252     
253     if not local_user or not isinstance(user, AstakosUser):
254         return
255     
256     if not user.provider == 'shibboleth':
257         return
258     
259     user.delete()
260     local_user.provider = 'shibboleth'
261     local_user.set_unusable_password()
262     local_user.third_party_identifier = user.third_party_identifier
263     local_user.save()
264     return local_user
265
266 def invite(invitation, inviter, email_template_name='im/welcome_email.txt'):
267     """
268     Send an invitation email and upon success reduces inviter's invitation by one.
269     
270     Raises SendInvitationError
271     """
272     invitation.inviter = inviter
273     invitation.save()
274     send_invitation(invitation, email_template_name)
275     inviter.invitations = max(0, inviter.invitations - 1)
276     inviter.save()
277
278 def set_user_credibility(email, has_credits):
279     try:
280         user = AstakosUser.objects.get(email=email, is_active=True)
281         user.has_credits = has_credits
282         user.save()
283     except AstakosUser.DoesNotExist, e:
284         logger.exception(e)
285     except ValidationError, e:
286         logger.exception(e)
287
288 class SendMailError(Exception):
289     pass
290
291 class SendAdminNotificationError(SendMailError):
292     def __init__(self):
293         self.message = _('Failed to send notification')
294         super(SendAdminNotificationError, self).__init__()
295
296 class SendVerificationError(SendMailError):
297     def __init__(self):
298         self.message = _('Failed to send verification')
299         super(SendVerificationError, self).__init__()
300
301 class SendInvitationError(SendMailError):
302     def __init__(self):
303         self.message = _('Failed to send invitation')
304         super(SendInvitationError, self).__init__()
305
306 class SendGreetingError(SendMailError):
307     def __init__(self):
308         self.message = _('Failed to send greeting')
309         super(SendGreetingError, self).__init__()
310
311 class SendFeedbackError(SendMailError):
312     def __init__(self):
313         self.message = _('Failed to send feedback')
314         super(SendFeedbackError, self).__init__()
315
316 class ChangeEmailError(SendMailError):
317     def __init__(self):
318         self.message = _('Failed to send change email')
319         super(ChangeEmailError, self).__init__()
320
321 class SendNotificationError(SendMailError):
322     def __init__(self):
323         self.message = _('Failed to send notification email')
324         super(SendNotificationError, self).__init__()