f8bbfe09658e8d9c1cae117d7b3cb76bc2b5e715
[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         logger._log(LOGGING_LEVEL, msg % email, [])
71         return r
72     return with_logging
73
74 login = logged(auth_login, '%s logged in.')
75 logout = logged(auth_logout, '%s logged out.')
76
77 def send_verification(user, template_name='im/activation_email.txt'):
78     """
79     Send email to user to verify his/her email and activate his/her account.
80     
81     Raises SendVerificationError
82     """
83     url = '%s?auth=%s&next=%s' % (urljoin(BASEURL, reverse('astakos.im.views.activate')),
84                                     quote(user.auth_token),
85                                     quote(BASEURL))
86     message = render_to_string(template_name, {
87             'user': user,
88             'url': url,
89             'baseurl': BASEURL,
90             'site_name': SITENAME,
91             'support': DEFAULT_CONTACT_EMAIL})
92     sender = DEFAULT_FROM_EMAIL
93     try:
94         send_mail('%s alpha2 testing account activation is needed' % SITENAME, message, sender, [user.email])
95     except (SMTPException, socket.error) as e:
96         logger.exception(e)
97         raise SendVerificationError()
98     else:
99         msg = 'Sent activation %s' % user.email
100         logger._log(LOGGING_LEVEL, msg, [])
101
102 def send_activation(user, template_name='im/activation_email.txt'):
103     send_verification(user, template_name)
104     user.activation_sent = datetime.now()
105     user.save()
106
107 def send_admin_notification(user, template_name='im/admin_notification.txt'):
108     """
109     Send email to DEFAULT_ADMIN_EMAIL to notify for a new user registration.
110     
111     Raises SendNotificationError
112     """
113     if not DEFAULT_ADMIN_EMAIL:
114         return
115     message = render_to_string(template_name, {
116             'user': user,
117             'baseurl': BASEURL,
118             'site_name': SITENAME,
119             'support': DEFAULT_CONTACT_EMAIL})
120     sender = DEFAULT_FROM_EMAIL
121     try:
122         send_mail('%s alpha2 testing account notification' % SITENAME, message, sender, [DEFAULT_ADMIN_EMAIL])
123     except (SMTPException, socket.error) as e:
124         logger.exception(e)
125         raise SendNotificationError()
126     else:
127         msg = 'Sent admin notification for user %s' % user.email
128         logger._log(LOGGING_LEVEL, msg, [])
129
130 def send_invitation(invitation, template_name='im/invitation.txt'):
131     """
132     Send invitation email.
133     
134     Raises SendInvitationError
135     """
136     subject = _('Invitation to %s alpha2 testing' % SITENAME)
137     url = '%s?code=%d' % (urljoin(BASEURL, reverse('astakos.im.views.index')), invitation.code)
138     message = render_to_string('im/invitation.txt', {
139                 'invitation': invitation,
140                 'url': url,
141                 'baseurl': BASEURL,
142                 'site_name': SITENAME,
143                 'support': DEFAULT_CONTACT_EMAIL})
144     sender = DEFAULT_FROM_EMAIL
145     try:
146         send_mail(subject, message, sender, [invitation.username])
147     except (SMTPException, socket.error) as e:
148         logger.exception(e)
149         raise SendInvitationError()
150     else:
151         msg = 'Sent invitation %s' % invitation
152         logger._log(LOGGING_LEVEL, msg, [])
153
154 def send_greeting(user, email_template_name='im/welcome_email.txt'):
155     """
156     Send welcome email.
157     
158     Raises SMTPException, socket.error
159     """
160     subject = _('Welcome to %s alpha2 testing' % SITENAME)
161     message = render_to_string(email_template_name, {
162                 'user': user,
163                 'url': urljoin(BASEURL, reverse('astakos.im.views.index')),
164                 'baseurl': BASEURL,
165                 'site_name': SITENAME,
166                 'support': DEFAULT_CONTACT_EMAIL})
167     sender = DEFAULT_FROM_EMAIL
168     try:
169         send_mail(subject, message, sender, [user.email])
170     except (SMTPException, socket.error) as e:
171         logger.exception(e)
172         raise SendGreetingError()
173     else:
174         msg = 'Sent greeting %s' % user.email
175         logger._log(LOGGING_LEVEL, msg, [])
176
177 def send_feedback(msg, data, user, email_template_name='im/feedback_mail.txt'):
178     subject = _("Feedback from %s alpha2 testing" % SITENAME)
179     from_email = user.email
180     recipient_list = [DEFAULT_CONTACT_EMAIL]
181     content = render_to_string(email_template_name, {
182         'message': msg,
183         'data': data,
184         'user': user})
185     try:
186         send_mail(subject, content, from_email, recipient_list)
187     except (SMTPException, socket.error) as e:
188         logger.exception(e)
189         raise SendFeedbackError()
190     else:
191         msg = 'Sent feedback from %s' % user.email
192         logger._log(LOGGING_LEVEL, msg, [])
193
194 def send_change_email(ec, request, email_template_name='registration/email_change_email.txt'):
195     try:
196         url = reverse('email_change_confirm',
197                       kwargs={'activation_key':ec.activation_key})
198         url = request.build_absolute_uri(url)
199         t = loader.get_template(email_template_name)
200         c = {'url': url, 'site_name': SITENAME}
201         from_email = DEFAULT_FROM_EMAIL
202         send_mail(_("Email change on %s alpha2 testing") % SITENAME,
203             t.render(Context(c)), from_email, [ec.new_email_address])
204     except (SMTPException, socket.error) as e:
205         logger.exception(e)
206         raise ChangeEmailError()
207     else:
208         msg = 'Sent change email for %s' % ec.user.email
209         logger._log(LOGGING_LEVEL, msg, [])
210
211 def activate(user, email_template_name='im/welcome_email.txt'):
212     """
213     Activates the specific user and sends email.
214     
215     Raises SendGreetingError, ValidationError
216     """
217     user.is_active = True
218     user.save()
219     send_greeting(user, email_template_name)
220
221 def invite(invitation, inviter, email_template_name='im/welcome_email.txt'):
222     """
223     Send an invitation email and upon success reduces inviter's invitation by one.
224     
225     Raises SendInvitationError
226     """
227     invitation.inviter = inviter
228     invitation.save()
229     send_invitation(invitation, email_template_name)
230     inviter.invitations = max(0, inviter.invitations - 1)
231     inviter.save()
232
233 def set_user_credibility(email, has_credits):
234     try:
235         user = AstakosUser.objects.get(email=email, is_active=True)
236         user.has_credits = has_credits
237         user.save()
238     except AstakosUser.DoesNotExist, e:
239         logger.exception(e)
240     except ValidationError, e:
241         logger.exception(e)
242
243 class SendMailError(Exception):
244     pass
245
246 class SendAdminNotificationError(SendMailError):
247     def __init__(self):
248         self.message = _('Failed to send notification')
249         super(SendAdminNotificationError, self).__init__()
250
251 class SendVerificationError(SendMailError):
252     def __init__(self):
253         self.message = _('Failed to send verification')
254         super(SendVerificationError, self).__init__()
255
256 class SendInvitationError(SendMailError):
257     def __init__(self):
258         self.message = _('Failed to send invitation')
259         super(SendInvitationError, self).__init__()
260
261 class SendGreetingError(SendMailError):
262     def __init__(self):
263         self.message = _('Failed to send greeting')
264         super(SendGreetingError, self).__init__()
265
266 class SendFeedbackError(SendMailError):
267     def __init__(self):
268         self.message = _('Failed to send feedback')
269         super(SendFeedbackError, self).__init__()
270
271 class ChangeEmailError(SendMailError):
272     def __init__(self):
273         self.message = _('Failed to send change email')
274         super(ChangeEmailError, self).__init__()