1 # Copyright 2011 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
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.
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.
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.
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
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
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
56 logger = logging.getLogger(__name__)
58 def logged(func, msg):
60 def with_logging(*args, **kwargs):
63 if len(args) == 2 and isinstance(args[1], AstakosUser):
65 elif len(args) == 1 and isinstance(args[0], HttpRequest):
68 email = user.email if user and user.is_authenticated() else ''
69 r = func(*args, **kwargs)
71 logger._log(LOGGING_LEVEL, msg % email, [])
75 login = logged(auth_login, '%s logged in.')
76 logout = logged(auth_logout, '%s logged out.')
78 def send_verification(user, template_name='im/activation_email.txt'):
80 Send email to user to verify his/her email and activate his/her account.
82 Raises SendVerificationError
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, {
91 'site_name': SITENAME,
92 'support': DEFAULT_CONTACT_EMAIL})
93 sender = DEFAULT_FROM_EMAIL
95 send_mail('%s alpha2 testing account activation is needed' % SITENAME, message, sender, [user.email])
96 except (SMTPException, socket.error) as e:
98 raise SendVerificationError()
100 msg = 'Sent activation %s' % user.email
101 logger._log(LOGGING_LEVEL, msg, [])
103 def send_activation(user, template_name='im/activation_email.txt'):
104 send_verification(user, template_name)
105 user.activation_sent = datetime.now()
108 def send_admin_notification(user, template_name='im/admin_notification.txt'):
110 Send email to DEFAULT_ADMIN_EMAIL to notify for a new user registration.
112 Raises SendNotificationError
114 if not DEFAULT_ADMIN_EMAIL:
116 message = render_to_string(template_name, {
119 'site_name': SITENAME,
120 'support': DEFAULT_CONTACT_EMAIL})
121 sender = DEFAULT_FROM_EMAIL
123 send_mail('%s alpha2 testing account notification' % SITENAME, message, sender, [DEFAULT_ADMIN_EMAIL])
124 except (SMTPException, socket.error) as e:
126 raise SendNotificationError()
128 msg = 'Sent admin notification for user %s' % user.email
129 logger._log(LOGGING_LEVEL, msg, [])
131 def send_helpdesk_notification(user, template_name='im/helpdesk_notification.txt'):
133 Send email to DEFAULT_CONTACT_EMAIL to notify for a new user activation.
135 Raises SendNotificationError
137 if not DEFAULT_CONTACT_EMAIL:
139 message = render_to_string(template_name, {
142 'site_name': SITENAME,
143 'support': DEFAULT_ADMIN_EMAIL})
144 sender = DEFAULT_FROM_EMAIL
146 send_mail('%s alpha2 testing account notification' % SITENAME, message, sender, [DEFAULT_CONTACT_EMAIL])
147 except (SMTPException, socket.error) as e:
149 raise SendNotificationError()
151 msg = 'Sent helpdesk admin notification for user %s' % user.email
152 logger._log(LOGGING_LEVEL, msg, [])
154 def send_invitation(invitation, template_name='im/invitation.txt'):
156 Send invitation email.
158 Raises SendInvitationError
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,
166 'site_name': SITENAME,
167 'support': DEFAULT_CONTACT_EMAIL})
168 sender = DEFAULT_FROM_EMAIL
170 send_mail(subject, message, sender, [invitation.username])
171 except (SMTPException, socket.error) as e:
173 raise SendInvitationError()
175 msg = 'Sent invitation %s' % invitation
176 logger._log(LOGGING_LEVEL, msg, [])
178 def send_greeting(user, email_template_name='im/welcome_email.txt'):
182 Raises SMTPException, socket.error
184 subject = _('Welcome to %s alpha2 testing' % SITENAME)
185 message = render_to_string(email_template_name, {
187 'url': urljoin(BASEURL, reverse('astakos.im.views.index')),
189 'site_name': SITENAME,
190 'support': DEFAULT_CONTACT_EMAIL})
191 sender = DEFAULT_FROM_EMAIL
193 send_mail(subject, message, sender, [user.email])
194 except (SMTPException, socket.error) as e:
196 raise SendGreetingError()
198 msg = 'Sent greeting %s' % user.email
199 logger._log(LOGGING_LEVEL, msg, [])
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, {
210 send_mail(subject, content, from_email, recipient_list)
211 except (SMTPException, socket.error) as e:
213 raise SendFeedbackError()
215 msg = 'Sent feedback from %s' % user.email
216 logger._log(LOGGING_LEVEL, msg, [])
218 def send_change_email(ec, request, email_template_name='registration/email_change_email.txt'):
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:
230 raise ChangeEmailError()
232 msg = 'Sent change email for %s' % ec.user.email
233 logger._log(LOGGING_LEVEL, msg, [])
235 def activate(user, email_template_name='im/welcome_email.txt',
236 helpdesk_email_template_name='im/helpdesk_notification.txt', verify_email=False):
238 Activates the specific user and sends email.
240 Raises SendGreetingError, ValidationError
242 user.is_active = True
244 user.email_verified = True
246 send_helpdesk_notification(user, helpdesk_email_template_name)
247 send_greeting(user, email_template_name)
249 def switch_account_to_shibboleth(user, local_user):
250 if not user or not isinstance(user, AstakosUser):
253 if not local_user or not isinstance(user, AstakosUser):
256 if not user.provider == 'shibboleth':
260 local_user.provider = 'shibboleth'
261 local_user.set_unusable_password()
262 local_user.third_party_identifier = user.third_party_identifier
266 def invite(invitation, inviter, email_template_name='im/welcome_email.txt'):
268 Send an invitation email and upon success reduces inviter's invitation by one.
270 Raises SendInvitationError
272 invitation.inviter = inviter
274 send_invitation(invitation, email_template_name)
275 inviter.invitations = max(0, inviter.invitations - 1)
278 def set_user_credibility(email, has_credits):
280 user = AstakosUser.objects.get(email=email, is_active=True)
281 user.has_credits = has_credits
283 except AstakosUser.DoesNotExist, e:
285 except ValidationError, e:
288 class SendMailError(Exception):
291 class SendAdminNotificationError(SendMailError):
293 self.message = _('Failed to send notification')
294 super(SendAdminNotificationError, self).__init__()
296 class SendVerificationError(SendMailError):
298 self.message = _('Failed to send verification')
299 super(SendVerificationError, self).__init__()
301 class SendInvitationError(SendMailError):
303 self.message = _('Failed to send invitation')
304 super(SendInvitationError, self).__init__()
306 class SendGreetingError(SendMailError):
308 self.message = _('Failed to send greeting')
309 super(SendGreetingError, self).__init__()
311 class SendFeedbackError(SendMailError):
313 self.message = _('Failed to send feedback')
314 super(SendFeedbackError, self).__init__()
316 class ChangeEmailError(SendMailError):
318 self.message = _('Failed to send change email')
319 super(ChangeEmailError, self).__init__()
321 class SendNotificationError(SendMailError):
323 self.message = _('Failed to send notification email')
324 super(SendNotificationError, self).__init__()