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 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
59 logger = logging.getLogger(__name__)
61 def logged(func, msg):
63 def with_logging(*args, **kwargs):
66 if len(args) == 2 and isinstance(args[1], AstakosUser):
68 elif len(args) == 1 and isinstance(args[0], HttpRequest):
71 email = user.email if user and user.is_authenticated() else ''
72 r = func(*args, **kwargs)
74 logger._log(LOGGING_LEVEL, msg % email, [])
78 login = logged(auth_login, '%s logged in.')
79 logout = logged(auth_logout, '%s logged out.')
81 def send_verification(user, template_name='im/activation_email.txt'):
83 Send email to user to verify his/her email and activate his/her account.
85 Raises SendVerificationError
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, {
94 'site_name': SITENAME,
95 'support': DEFAULT_CONTACT_EMAIL})
96 sender = DEFAULT_FROM_EMAIL
98 send_mail(_(VERIFICATION_EMAIL_SUBJECT), message, sender, [user.email])
99 except (SMTPException, socket.error) as e:
101 raise SendVerificationError()
103 msg = 'Sent activation %s' % user.email
104 logger._log(LOGGING_LEVEL, msg, [])
106 def send_activation(user, template_name='im/activation_email.txt'):
107 send_verification(user, template_name)
108 user.activation_sent = datetime.now()
111 def send_admin_notification(user, template_name='im/admin_notification.txt'):
113 Send email to DEFAULT_ADMIN_EMAIL to notify for a new user registration.
115 Raises SendNotificationError
117 if not DEFAULT_ADMIN_EMAIL:
119 message = render_to_string(template_name, {
122 'site_name': SITENAME,
123 'support': DEFAULT_CONTACT_EMAIL})
124 sender = DEFAULT_FROM_EMAIL
126 send_mail(_(ADMIN_NOTIFICATION_EMAIL_SUBJECT) % {'user': user.email}, message, sender, [DEFAULT_ADMIN_EMAIL])
127 except (SMTPException, socket.error) as e:
129 raise SendNotificationError()
131 msg = 'Sent admin notification for user %s' % user.email
132 logger._log(LOGGING_LEVEL, msg, [])
134 def send_helpdesk_notification(user, template_name='im/helpdesk_notification.txt'):
136 Send email to DEFAULT_CONTACT_EMAIL to notify for a new user activation.
138 Raises SendNotificationError
140 if not DEFAULT_CONTACT_EMAIL:
142 message = render_to_string(template_name, {
145 'site_name': SITENAME,
146 'support': DEFAULT_ADMIN_EMAIL})
147 sender = DEFAULT_FROM_EMAIL
149 send_mail(_(HELPDESK_NOTIFICATION_EMAIL_SUBJECT) % {'user': user.email}, message, sender, [DEFAULT_CONTACT_EMAIL])
150 except (SMTPException, socket.error) as e:
152 raise SendNotificationError()
154 msg = 'Sent helpdesk admin notification for user %s' % user.email
155 logger._log(LOGGING_LEVEL, msg, [])
157 def send_invitation(invitation, template_name='im/invitation.txt'):
159 Send invitation email.
161 Raises SendInvitationError
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,
169 'site_name': SITENAME,
170 'support': DEFAULT_CONTACT_EMAIL})
171 sender = DEFAULT_FROM_EMAIL
173 send_mail(subject, message, sender, [invitation.username])
174 except (SMTPException, socket.error) as e:
176 raise SendInvitationError()
178 msg = 'Sent invitation %s' % invitation
179 logger._log(LOGGING_LEVEL, msg, [])
181 def send_greeting(user, email_template_name='im/welcome_email.txt'):
185 Raises SMTPException, socket.error
187 subject = _(GREETING_EMAIL_SUBJECT)
188 message = render_to_string(email_template_name, {
190 'url': urljoin(BASEURL, reverse('astakos.im.views.index')),
192 'site_name': SITENAME,
193 'support': DEFAULT_CONTACT_EMAIL})
194 sender = DEFAULT_FROM_EMAIL
196 send_mail(subject, message, sender, [user.email])
197 except (SMTPException, socket.error) as e:
199 raise SendGreetingError()
201 msg = 'Sent greeting %s' % user.email
202 logger._log(LOGGING_LEVEL, msg, [])
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, {
213 send_mail(subject, content, from_email, recipient_list)
214 except (SMTPException, socket.error) as e:
216 raise SendFeedbackError()
218 msg = 'Sent feedback from %s' % user.email
219 logger._log(LOGGING_LEVEL, msg, [])
221 def send_change_email(ec, request, email_template_name='registration/email_change_email.txt'):
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:
233 raise ChangeEmailError()
235 msg = 'Sent change email for %s' % ec.user.email
236 logger._log(LOGGING_LEVEL, msg, [])
238 def activate(user, email_template_name='im/welcome_email.txt',
239 helpdesk_email_template_name='im/helpdesk_notification.txt', verify_email=False):
241 Activates the specific user and sends email.
243 Raises SendGreetingError, ValidationError
245 user.is_active = True
247 user.email_verified = True
249 send_helpdesk_notification(user, helpdesk_email_template_name)
250 send_greeting(user, email_template_name)
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):
256 if not local_user or not isinstance(user, AstakosUser):
259 if not user.provider == 'shibboleth':
263 local_user.provider = 'shibboleth'
264 local_user.third_party_identifier = user.third_party_identifier
266 send_greeting(local_user, greeting_template_name)
269 def invite(invitation, inviter, email_template_name='im/welcome_email.txt'):
271 Send an invitation email and upon success reduces inviter's invitation by one.
273 Raises SendInvitationError
275 invitation.inviter = inviter
277 send_invitation(invitation, email_template_name)
278 inviter.invitations = max(0, inviter.invitations - 1)
281 def set_user_credibility(email, has_credits):
283 user = AstakosUser.objects.get(email=email, is_active=True)
284 user.has_credits = has_credits
286 except AstakosUser.DoesNotExist, e:
288 except ValidationError, e:
291 class SendMailError(Exception):
294 class SendAdminNotificationError(SendMailError):
296 self.message = _('Failed to send notification')
297 super(SendAdminNotificationError, self).__init__()
299 class SendVerificationError(SendMailError):
301 self.message = _('Failed to send verification')
302 super(SendVerificationError, self).__init__()
304 class SendInvitationError(SendMailError):
306 self.message = _('Failed to send invitation')
307 super(SendInvitationError, self).__init__()
309 class SendGreetingError(SendMailError):
311 self.message = _('Failed to send greeting')
312 super(SendGreetingError, self).__init__()
314 class SendFeedbackError(SendMailError):
316 self.message = _('Failed to send feedback')
317 super(SendFeedbackError, self).__init__()
319 class ChangeEmailError(SendMailError):
321 self.message = _('Failed to send change email')
322 super(ChangeEmailError, self).__init__()
324 class SendNotificationError(SendMailError):
326 self.message = _('Failed to send notification email')
327 super(SendNotificationError, self).__init__()