From 683cf2445cd50ed8ceeff390af43d116a0bcedfe Mon Sep 17 00:00:00 2001 From: Sofia Papagiannaki Date: Wed, 14 Mar 2012 17:14:54 +0200 Subject: [PATCH] change signup flow, remove activateuser command and introduce sendactivation command --- snf-astakos-app/astakos/im/backends.py | 68 +++++---------- snf-astakos-app/astakos/im/functions.py | 87 ++++++++++++++++---- .../{activateuser.py => sendactivation.py} | 12 +-- snf-astakos-app/astakos/im/views.py | 25 ++++-- 4 files changed, 115 insertions(+), 77 deletions(-) rename snf-astakos-app/astakos/im/management/commands/{activateuser.py => sendactivation.py} (88%) diff --git a/snf-astakos-app/astakos/im/backends.py b/snf-astakos-app/astakos/im/backends.py index e99bbef..ae699f4 100644 --- a/snf-astakos-app/astakos/im/backends.py +++ b/snf-astakos-app/astakos/im/backends.py @@ -42,12 +42,12 @@ from django.db import transaction from django.core.urlresolvers import reverse from smtplib import SMTPException -from urllib import quote from urlparse import urljoin from astakos.im.models import AstakosUser, Invitation from astakos.im.forms import * from astakos.im.util import get_invitation +from astakos.im.functions import send_verification, send_notification, activate from astakos.im.settings import INVITATIONS_ENABLED, DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, MODERATION_ENABLED, SITENAME, BASEURL, DEFAULT_ADMIN_EMAIL, RE_USER_EMAIL_PATTERNS import socket @@ -157,7 +157,7 @@ class InvitationsBackend(SignupBackend): return False @transaction.commit_manually - def signup(self, form, email_template_name='im/activation_email.txt', admin_email_template_name='im/admin_notification.txt'): + def signup(self, form, verification_template_name='im/activation_email.txt', greeting_template_name='im/welcome_email.txt', admin_email_template_name='im/admin_notification.txt'): """ Initially creates an inactive user account. If the user is preaccepted (has a valid invitation code) the user is activated and if the request @@ -172,23 +172,26 @@ class InvitationsBackend(SignupBackend): user = form.save() if self._is_preaccepted(user): if user.email_verified: - user.is_active = True - user.save() - message = _('Registration completed. You can now login.') + try: + activate(user, greeting_template_name) + message = _('Registration completed. You can now login.') + except (SMTPException, socket.error) as e: + status = messages.ERROR + name = 'strerror' + message = getattr(e, 'name') if hasattr(e, 'name') else e else: try: - _send_verification(self.request, user, email_template_name) + send_verification(user, verification_template_name) message = _('Verification sent to %s' % user.email) except (SMTPException, socket.error) as e: status = messages.ERROR name = 'strerror' - message = getattr(e, name) if hasattr(e, name) else e + message = getattr(e, 'name') if hasattr(e, 'name') else e else: - _send_notification(user, admin_email_template_name) - message = _('Your request for an account was successfully sent \ - and pending approval from our administrators. You \ - will be notified by email the next days. \ - Thanks for being patient, the GRNET team') + send_notification(user, admin_email_template_name) + message = _('Your request for an account was successfully received and is now pending \ + approval. You will be notified by email in the next few days. Thanks for \ + your interest in ~okeanos! The GRNET team.') status = messages.SUCCESS except Invitation.DoesNotExist, e: status = messages.ERROR @@ -264,23 +267,22 @@ class SimpleBackend(SignupBackend): status = messages.SUCCESS if not self._is_preaccepted(user): try: - _send_notification(user, admin_email_template_name) - message = _('Your request for an account was successfully sent \ - and pending approval from our administrators. You \ - will be notified by email the next days. \ - Thanks for being patient, the GRNET team') + send_notification(user, admin_email_template_name) + message = _('Your request for an account was successfully received and is now pending \ + approval. You will be notified by email in the next few days. Thanks for \ + your interest in ~okeanos! The GRNET team.') except (SMTPException, socket.error) as e: status = messages.ERROR name = 'strerror' - message = getattr(e, name) if hasattr(e, name) else e + message = getattr(e, 'name') if hasattr(e, 'name') else e else: try: - _send_verification(self.request, user, email_template_name) + send_verification(user, email_template_name) message = _('Verification sent to %s' % user.email) except (SMTPException, socket.error) as e: status = messages.ERROR name = 'strerror' - message = getattr(e, name) if hasattr(e, name) else e + message = getattr(e, 'name') if hasattr(e, 'name') else e # rollback in case of error if status == messages.ERROR: @@ -288,29 +290,3 @@ class SimpleBackend(SignupBackend): else: transaction.commit() return status, message, user - -def _send_verification(request, user, template_name): - url = '%s?auth=%s&next=%s' % (urljoin(BASEURL, reverse('astakos.im.views.activate')), - quote(user.auth_token), - quote(BASEURL)) - message = render_to_string(template_name, { - 'user': user, - 'url': url, - 'baseurl': BASEURL, - 'site_name': SITENAME, - 'support': DEFAULT_CONTACT_EMAIL}) - sender = DEFAULT_FROM_EMAIL - send_mail('%s alpha2 testing account activation' % SITENAME, message, sender, [user.email]) - logger.info('Sent activation %s', user) - -def _send_notification(user, template_name): - if not DEFAULT_ADMIN_EMAIL: - return - message = render_to_string(template_name, { - 'user': user, - 'baseurl': BASEURL, - 'site_name': SITENAME, - 'support': DEFAULT_CONTACT_EMAIL}) - sender = DEFAULT_FROM_EMAIL - send_mail('%s alpha2 testing account notification' % SITENAME, message, sender, [DEFAULT_ADMIN_EMAIL]) - logger.info('Sent admin notification for user %s', user) diff --git a/snf-astakos-app/astakos/im/functions.py b/snf-astakos-app/astakos/im/functions.py index 43bb9d6..d9f3196 100644 --- a/snf-astakos-app/astakos/im/functions.py +++ b/snf-astakos-app/astakos/im/functions.py @@ -37,22 +37,75 @@ from django.utils.translation import ugettext as _ from django.template.loader import render_to_string from django.core.mail import send_mail from django.core.urlresolvers import reverse +from urllib import quote from urlparse import urljoin from random import randint -from astakos.im.settings import DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, SITENAME, BASEURL +from astakos.im.settings import DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, SITENAME, BASEURL, DEFAULT_ADMIN_EMAIL from astakos.im.models import Invitation logger = logging.getLogger(__name__) -def activate(user, email_template_name='im/welcome_email.txt'): +def send_verification(user, template_name='im/activation_email.txt'): """ - Activates the specific user and sends email. + Send email to user to verify his/her email and activate his/her account. + + Raises SMTPException, socket.error + """ + url = '%s?auth=%s&next=%s' % (urljoin(BASEURL, reverse('astakos.im.views.activate')), + quote(user.auth_token), + quote(BASEURL)) + message = render_to_string(template_name, { + 'user': user, + 'url': url, + 'baseurl': BASEURL, + 'site_name': SITENAME, + 'support': DEFAULT_CONTACT_EMAIL}) + sender = DEFAULT_FROM_EMAIL + send_mail('%s alpha2 testing account activation' % SITENAME, message, sender, [user.email]) + logger.info('Sent activation %s', user) + +def send_notification(user, template_name='im/admin_notification.txt'): + """ + Send email to DEFAULT_ADMIN_EMAIL to notify for a new user registration. + + Raises SMTPException, socket.error + """ + if not DEFAULT_ADMIN_EMAIL: + return + message = render_to_string(template_name, { + 'user': user, + 'baseurl': BASEURL, + 'site_name': SITENAME, + 'support': DEFAULT_CONTACT_EMAIL}) + sender = DEFAULT_FROM_EMAIL + send_mail('%s alpha2 testing account notification' % SITENAME, message, sender, [DEFAULT_ADMIN_EMAIL]) + logger.info('Sent admin notification for user %s', user) + +def send_invitation(invitation, template_name='im/invitation.txt'): + """ + Send invitation email. + + Raises SMTPException, socket.error + """ + subject = _('Invitation to %s alpha2 testing' % SITENAME) + url = '%s?code=%d' % (urljoin(BASEURL, reverse('astakos.im.views.signup')), invitation.code) + message = render_to_string('im/invitation.txt', { + 'invitation': invitation, + 'url': url, + 'baseurl': BASEURL, + 'site_name': SITENAME, + 'support': DEFAULT_CONTACT_EMAIL}) + sender = DEFAULT_FROM_EMAIL + send_mail(subject, message, sender, [invitation.username]) + logger.info('Sent invitation %s', invitation) + +def send_greeting(user, email_template_name='im/welcome_email.txt'): + """ + Send welcome email. Raises SMTPException, socket.error """ - user.is_active = True - user.save() subject = _('Welcome to %s alpha2 testing' % SITENAME) message = render_to_string(email_template_name, { 'user': user, @@ -64,6 +117,16 @@ def activate(user, email_template_name='im/welcome_email.txt'): send_mail(subject, message, sender, [user.email]) logger.info('Sent greeting %s', user) +def activate(user, email_template_name='im/welcome_email.txt'): + """ + Activates the specific user and sends email. + + Raises SMTPException, socket.error + """ + user.is_active = True + user.save() + send_greeting(user, email_template_name) + def _generate_invitation_code(): while True: code = randint(1, 2L**63 - 1) @@ -73,7 +136,7 @@ def _generate_invitation_code(): except Invitation.DoesNotExist: return code -def invite(inviter, username, realname): +def invite(inviter, username, realname, email_template_name='im/welcome_email.txt'): """ Send an invitation email and upon success reduces inviter's invitation by one. @@ -85,16 +148,6 @@ def invite(inviter, username, realname): code=code, realname=realname) invitation.save() - subject = _('Invitation to %s alpha2 testing' % SITENAME) - url = '%s?code=%d' % (urljoin(BASEURL, reverse('astakos.im.views.signup')), code) - message = render_to_string('im/invitation.txt', { - 'invitation': invitation, - 'url': url, - 'baseurl': BASEURL, - 'service': SITENAME, - 'support': DEFAULT_CONTACT_EMAIL}) - sender = DEFAULT_FROM_EMAIL - send_mail(subject, message, sender, [invitation.username]) - logger.info('Sent invitation %s', invitation) + send_invitation(invitation, email_template_name) inviter.invitations = max(0, inviter.invitations - 1) inviter.save() diff --git a/snf-astakos-app/astakos/im/management/commands/activateuser.py b/snf-astakos-app/astakos/im/management/commands/sendactivation.py similarity index 88% rename from snf-astakos-app/astakos/im/management/commands/activateuser.py rename to snf-astakos-app/astakos/im/management/commands/sendactivation.py index 716700b..f448c80 100644 --- a/snf-astakos-app/astakos/im/management/commands/activateuser.py +++ b/snf-astakos-app/astakos/im/management/commands/sendactivation.py @@ -34,16 +34,15 @@ from django.core.management.base import BaseCommand, CommandError from django.db import transaction -from astakos.im.functions import activate +from astakos.im.functions import send_verification from ._common import get_user class Command(BaseCommand): args = " [user ID or email] ..." - help = "Activates one or more users" + help = "Sends an activation email to one or more users" - @transaction.commit_manually def handle(self, *args, **options): if not args: raise CommandError("No user was given") @@ -59,11 +58,6 @@ class Command(BaseCommand): self.stderr.write(msg) continue - try: - activate(user) - transaction.commit() - except Exception, e: - transaction.rollback() - raise e + send_verification(user) self.stdout.write("Activated '%s'\n" % (user.email,)) \ No newline at end of file diff --git a/snf-astakos-app/astakos/im/views.py b/snf-astakos-app/astakos/im/views.py index ad85749..30c0cc6 100644 --- a/snf-astakos-app/astakos/im/views.py +++ b/snf-astakos-app/astakos/im/views.py @@ -49,13 +49,14 @@ from django.contrib import messages from django.db import transaction from django.contrib.auth import logout as auth_logout from django.utils.http import urlencode -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, HttpResponseBadRequest from django.db.utils import IntegrityError from astakos.im.models import AstakosUser, Invitation from astakos.im.backends import get_backend from astakos.im.util import get_context, prepare_response, set_cookie from astakos.im.forms import * +from astakos.im.functions import send_greeting from astakos.im.settings import DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, COOKIE_NAME, COOKIE_DOMAIN, IM_MODULES, SITENAME, BASEURL, LOGOUT_NEXT from astakos.im.functions import invite as invite_func @@ -396,18 +397,32 @@ def logout(request, template='registration/logged_out.html', extra_context={}): response.write(render_to_string(template, context_instance=context)) return response -def activate(request): +@transaction.commit_manually +def activate(request, email_template_name='im/welcome_email.txt', on_failure=''): """ - Activates the user identified by the ``auth`` request parameter + Activates the user identified by the ``auth`` request parameter, sends a welcome email + and renews the user token. + + The view uses commit_manually decorator in order to ensure the user state will be updated + only if the email will be send successfully. """ token = request.GET.get('auth') next = request.GET.get('next') try: user = AstakosUser.objects.get(auth_token=token) except AstakosUser.DoesNotExist: - return HttpResponseBadRequest('No such user') + return HttpResponseBadRequest(_('No such user')) user.is_active = True user.email_verified = True user.save() - return prepare_response(request, user, next, renew=True) + try: + send_greeting(user, email_template_name) + response = prepare_response(request, user, next, renew=True) + transaction.commit() + return response + except (SMTPException, socket.error) as e: + message = getattr(e, 'name') if hasattr(e, 'name') else e + messages.add_message(request, messages.ERROR, message) + transaction.rollback() + return signup(request, on_failure='im/signup.html') -- 1.7.10.4