first draft of a registration form + email
authorJordan Augé <jordan.auge@lip6.fr>
Wed, 3 Jul 2013 13:29:58 +0000 (15:29 +0200)
committerJordan Augé <jordan.auge@lip6.fr>
Wed, 3 Jul 2013 13:29:58 +0000 (15:29 +0200)
12 files changed:
myslice/settings.py
myslice/urls.py
portal/forms.py
portal/models.py
portal/signals.py [new file with mode: 0644]
portal/templates/slice_request.html [new file with mode: 0644]
portal/templates/user_register.html
portal/templates/user_register_email.txt [new file with mode: 0644]
portal/templates/user_register_email_subject.txt [new file with mode: 0644]
portal/urls.py
portal/util.py [new file with mode: 0644]
portal/views.py

index 4a19345..bb9e74a 100644 (file)
@@ -154,10 +154,17 @@ INSTALLED_APPS = (
     # Uncomment the next line to enable admin documentation:
     # 'django.contrib.admindocs',
     'portal',
-    'django.contrib.formtools',
-#    'crispy_forms',
+# DEPRECATED #    'django.contrib.formtools',
+# DEPRECATED ##    'crispy_forms',
+# DEPRECATED #
+# DEPRECATED #    # User registration
+# DEPRECATED #    'django.contrib.auth',
+# DEPRECATED #    'django.contrib.sites',
+# DEPRECATED #    'registration',
 )
 
+ACCOUNT_ACTIVATION_DAYS = 7 # One-week activation window; you may, of course, use a different value.
+
 # A sample logging configuration. The only tangible logging
 # performed by this configuration is to send an email to
 # the site admins on every HTTP 500 error when DEBUG=False.
index 08d6f9f..8b1f31d 100644 (file)
@@ -49,4 +49,5 @@ urlpatterns = patterns(
     (r'^slice/(?P<slicename>[\w\.]+)/?$', 'trash.sliceview.slice_view'),
     # Portal
     url(r'^portal/', include('portal.urls')),
+
 )
index 43be698..513b952 100644 (file)
@@ -24,26 +24,79 @@ from django import forms
 from portal.models import PendingUser, PendingSlice
 #from crispy_forms.helper import FormHelper
 #from crispy_forms.layout import Submit
+from django.utils.translation import ugettext_lazy as _
 
-class UserRegisterForm(forms.ModelForm):
-# DEPRECATED #    def __init__(self, *args, **kwargs):
-# DEPRECATED #        self.helper = FormHelper()
-# DEPRECATED #        self.helper.form_tag = False
-# DEPRECATED #        #self.helper.form_id = 'id-exampleForm'
-# DEPRECATED #        self.helper.form_class = 'blueForms'
-# DEPRECATED #        self.helper.form_method = 'post'
-# DEPRECATED #        #self.helper.form_action = 'submit_survey'
-# DEPRECATED #        self.helper.add_input(Submit('submit', 'Submit'))
-# DEPRECATED #        super(RegisterUserForm, self).__init__(*args, **kwargs)
-
-    first_name = forms.CharField( widget=forms.TextInput )
-    last_name  = forms.CharField( widget=forms.TextInput )
-    email      = forms.CharField( widget=forms.TextInput )
-    password   = forms.CharField( widget=forms.PasswordInput )
-    password2  = forms.CharField( widget=forms.PasswordInput )
+class UserRegisterForm(forms.Form): # Not ModelForm
+    """
+    Form for registering a new user account.
+    
+    Validates that the requested username is not already in use, and
+    requires the password to be entered twice to catch typos.
+    
+    Subclasses should feel free to add any additional validation they
+    need, but should avoid defining a ``save()`` method -- the actual
+    saving of collected user data is delegated to the active
+    registration backend.
+
+    """
+    required_css_class = 'required'
+    
+    first_name = forms.RegexField(regex=r'^[\w.@+-]+$',
+                                 max_length=30,
+                                 label=_("First name"),
+                                 error_messages={'invalid': _("This value may contain only letters, numbers and @/./+/-/_ characters.")})
+    last_name = forms.RegexField(regex=r'^[\w.@+-]+$',
+                                 max_length=30,
+                                 label=_("Last name"),
+                                 error_messages={'invalid': _("This value may contain only letters, numbers and @/./+/-/_ characters.")})
+    email = forms.EmailField(label=_("E-mail"))
+    password1 = forms.CharField(widget=forms.PasswordInput,
+                                label=_("Password"))
+    password2 = forms.CharField(widget=forms.PasswordInput,
+                                label=_("Password (again)"))
     keypair    = forms.CharField( widget=forms.FileInput )
-    class Meta:
-        model = PendingUser
+
+    tos = forms.BooleanField(widget=forms.CheckboxInput,
+                             label=_(u'I have read and agree to the Terms of Service'),
+                             error_messages={'required': _("You must agree to the terms to register")})
+
+#    def clean_username(self):
+#        """
+#        Validate that the username is alphanumeric and is not already
+#        in use.
+#        
+#        """
+#        existing = User.objects.filter(username__iexact=self.cleaned_data['username'])
+#        if existing.exists():
+#            raise forms.ValidationError(_("A user with that username already exists."))
+#        else:
+#            return self.cleaned_data['username']
+
+    def clean_email(self):
+        """
+        Validate that the supplied email address is unique for the
+        site.
+        
+        """
+        if PendingUser.objects.filter(email__iexact=self.cleaned_data['email']):
+            raise forms.ValidationError(_("This email address is already in use. Please supply a different email address."))
+        return self.cleaned_data['email']
+
+    def clean(self):
+        """
+        Verifiy that the values entered into the two password fields
+        match. Note that an error here will end up in
+        ``non_field_errors()`` because it doesn't apply to a single
+        field.
+        
+        """
+        if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:
+            if self.cleaned_data['password1'] != self.cleaned_data['password2']:
+                raise forms.ValidationError(_("The two password fields didn't match."))
+        return self.cleaned_data
+
+# DEPRECATED #    class Meta:
+# DEPRECATED #        model = PendingUser
 
 class SliceRequestForm(forms.ModelForm):
     slice_name = forms.CharField( widget=forms.TextInput )
index 3c950a6..da0e086 100644 (file)
 # this program; see the file COPYING.  If not, write to the Free Software
 # Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
-from django.db import models
-from .validators import validate_email
+import datetime
+import hashlib
+import random
+import re
 
+from django.conf              import settings
+from django.core.mail         import send_mail
+from django.db                import models
+from django.db                import transaction
+from django.utils.translation import ugettext_lazy as _
+from django.template.loader   import render_to_string
+
+#from django.core.validators import validate_email
+
+try:
+    from django.contrib.auth import get_user_model
+    User = get_user_model()
+except ImportError:
+    from django.contrib.auth.models import User
+
+try:
+    from django.utils.timezone import now as datetime_now
+except ImportError:
+    datetime_now = datetime.datetime.now
+
+SHA1_RE = re.compile('^[a-f0-9]{40}$')
 
 # Create your models here.
 
@@ -30,15 +53,257 @@ class Institution(models.Model):
     name = models.TextField()
     # list of associated email domains 
 
+# code borrowed from django-registration
+# https://bitbucket.org/ubernostrum/django-registration/
+
+class RegistrationManager(models.Manager):
+    """
+    Custom manager for the ``RegistrationProfile`` model.
+    
+    The methods defined here provide shortcuts for account creation
+    and activation (including generation and emailing of activation
+    keys), and for cleaning out expired inactive accounts.
+    
+    """
+    def activate_user(self, activation_key):
+        """
+        Validate an activation key and activate the corresponding
+        ``User`` if valid.
+        
+        If the key is valid and has not expired, return the ``User``
+        after activating.
+        
+        If the key is not valid or has expired, return ``False``.
+        
+        If the key is valid but the ``User`` is already active,
+        return ``False``.
+        
+        To prevent reactivation of an account which has been
+        deactivated by site administrators, the activation key is
+        reset to the string constant ``RegistrationProfile.ACTIVATED``
+        after successful activation.
+
+        """
+        # Make sure the key we're trying conforms to the pattern of a
+        # SHA1 hash; if it doesn't, no point trying to look it up in
+        # the database.
+        if SHA1_RE.search(activation_key):
+            try:
+                profile = self.get(activation_key=activation_key)
+            except self.model.DoesNotExist:
+                return False
+            if not profile.activation_key_expired():
+                user = profile.user
+                user.is_active = True
+                user.save()
+                profile.activation_key = self.model.ACTIVATED
+                profile.save()
+                return user
+        return False
+    
+    def create_user(self, first_name, last_name, email, password):
+        pending_user = self.create(first_name=first_name, last_name=last_name, email=email, password=password)
+        return pending_user
+
+    def create_inactive_user(self, first_name, last_name, email, password, site,
+                             send_email=True):
+        """
+        Create a new, inactive ``User``, generate a
+        ``RegistrationProfile`` and email its activation key to the
+        ``User``, returning the new ``User``.
+
+        By default, an activation email will be sent to the new
+        user. To disable this, pass ``send_email=False``.
+        
+        """
+        salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
+        if isinstance(email, unicode):
+            email = email.encode('utf-8')
+        activation_key = hashlib.sha1(salt+email).hexdigest()
+
+        new_user = PendingUser.objects.create_user(first_name, last_name, email, password)
+        new_user.is_active = False
+        new_user.activation_key=activation_key
+        new_user.save()
+
+        # We might not need this
+        #registration_profile = self.create_profile(new_user)
+
+        if send_email:
+            new_user.send_activation_email(site)
+            #registration_profile.send_activation_email(site)
+
+        return new_user
+    create_inactive_user = transaction.commit_on_success(create_inactive_user)
+
+    def create_profile(self, user):
+        """
+        Create a ``RegistrationProfile`` for a given
+        ``User``, and return the ``RegistrationProfile``.
+        
+        The activation key for the ``RegistrationProfile`` will be a
+        SHA1 hash, generated from a combination of the ``User``'s
+        username and a random salt.
+        
+        """
+        salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
+        username = user.username
+        if isinstance(username, unicode):
+            username = username.encode('utf-8')
+        activation_key = hashlib.sha1(salt+username).hexdigest()
+        return self.create(user=user,
+                           activation_key=activation_key)
+        
+    def delete_expired_users(self):
+        """
+        Remove expired instances of ``RegistrationProfile`` and their
+        associated ``User``s.
+        
+        Accounts to be deleted are identified by searching for
+        instances of ``RegistrationProfile`` with expired activation
+        keys, and then checking to see if their associated ``User``
+        instances have the field ``is_active`` set to ``False``; any
+        ``User`` who is both inactive and has an expired activation
+        key will be deleted.
+        
+        It is recommended that this method be executed regularly as
+        part of your routine site maintenance; this application
+        provides a custom management command which will call this
+        method, accessible as ``manage.py cleanupregistration``.
+        
+        Regularly clearing out accounts which have never been
+        activated serves two useful purposes:
+        
+        1. It alleviates the ocasional need to reset a
+           ``RegistrationProfile`` and/or re-send an activation email
+           when a user does not receive or does not act upon the
+           initial activation email; since the account will be
+           deleted, the user will be able to simply re-register and
+           receive a new activation key.
+        
+        2. It prevents the possibility of a malicious user registering
+           one or more accounts and never activating them (thus
+           denying the use of those usernames to anyone else); since
+           those accounts will be deleted, the usernames will become
+           available for use again.
+        
+        If you have a troublesome ``User`` and wish to disable their
+        account while keeping it in the database, simply delete the
+        associated ``RegistrationProfile``; an inactive ``User`` which
+        does not have an associated ``RegistrationProfile`` will not
+        be deleted.
+        
+        """
+        for profile in self.all():
+            try:
+                if profile.activation_key_expired():
+                    user = profile.user
+                    if not user.is_active:
+                        user.delete()
+                        profile.delete()
+            except User.DoesNotExist:
+                profile.delete()
+
+
 class PendingUser(models.Model):
     # NOTE We might consider migrating the fields to CharField, which would
     # simplify form creation in forms.py
     first_name  = models.TextField()
     last_name   = models.TextField()
-    email       = models.EmailField(validators=[validate_email])
+    email       = models.EmailField() #validators=[validate_email])
     password    = models.TextField()
     keypair     = models.TextField()
     # institution
 
+    objects = RegistrationManager()
+
+    class Meta:
+        verbose_name = _('registration profile')
+        verbose_name_plural = _('registration profiles')
+    
+    def __unicode__(self):
+        return u"Registration information for %s" % self.user
+
+    def activation_key_expired(self):
+        """
+        Determine whether this ``RegistrationProfile``'s activation
+        key has expired, returning a boolean -- ``True`` if the key
+        has expired.
+        
+        Key expiration is determined by a two-step process:
+        
+        1. If the user has already activated, the key will have been
+           reset to the string constant ``ACTIVATED``. Re-activating
+           is not permitted, and so this method returns ``True`` in
+           this case.
+
+        2. Otherwise, the date the user signed up is incremented by
+           the number of days specified in the setting
+           ``ACCOUNT_ACTIVATION_DAYS`` (which should be the number of
+           days after signup during which a user is allowed to
+           activate their account); if the result is less than or
+           equal to the current date, the key has expired and this
+           method returns ``True``.
+        
+        """
+        expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS)
+        return self.activation_key == self.ACTIVATED or \
+               (self.user.date_joined + expiration_date <= datetime_now())
+    activation_key_expired.boolean = True
+
+    def send_activation_email(self, site):
+        """
+        Send an activation email to the user associated with this
+        ``RegistrationProfile``.
+        
+        The activation email will make use of two templates:
+
+        ``user_register_email_subject.txt``
+            This template will be used for the subject line of the
+            email. Because it is used as the subject line of an email,
+            this template's output **must** be only a single line of
+            text; output longer than one line will be forcibly joined
+            into only a single line.
+
+        ``user_register_email.txt``
+            This template will be used for the body of the email.
+
+        These templates will each receive the following context
+        variables:
+
+        ``activation_key``
+            The activation key for the new account.
+
+        ``expiration_days``
+            The number of days remaining during which the account may
+            be activated.
+
+        ``site``
+            An object representing the site on which the user
+            registered; depending on whether ``django.contrib.sites``
+            is installed, this may be an instance of either
+            ``django.contrib.sites.models.Site`` (if the sites
+            application is installed) or
+            ``django.contrib.sites.models.RequestSite`` (if
+            not). Consult the documentation for the Django sites
+            framework for details regarding these objects' interfaces.
+
+        """
+        ctx_dict = {'activation_key': self.activation_key,
+                    'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
+                    'site': site}
+        subject = render_to_string('user_register_email_subject.txt',
+                                   ctx_dict)
+        # Email subject *must not* contain newlines
+        subject = ''.join(subject.splitlines())
+
+        message = render_to_string('user_register_email.txt',
+                                   ctx_dict)
+
+        send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [self.email])
+
+
+
+
 class PendingSlice(models.Model):
     slice_name  = models.TextField()
diff --git a/portal/signals.py b/portal/signals.py
new file mode 100644 (file)
index 0000000..124f295
--- /dev/null
@@ -0,0 +1,7 @@
+from django.dispatch import Signal
+
+# A new user has registered.
+user_registered = Signal(providing_args=["user", "request"])
+
+# A user has activated his or her account.
+user_activated = Signal(providing_args=["user", "request"])
diff --git a/portal/templates/slice_request.html b/portal/templates/slice_request.html
new file mode 100644 (file)
index 0000000..01f48d0
--- /dev/null
@@ -0,0 +1,20 @@
+{% extends "layout-unfold1.html" %}
+{% load i18n %}
+
+{% block head %}
+{{ wizard.form.media }}
+<link rel="stylesheet" type="text/css" href="{{STATIC_URL}}css/smart_wizard.css" />
+{% endblock %}
+
+{% block unfold1_main %}
+
+  <h1>Slice request</h1>
+
+{% if envoi %}Votre message a bien été envoyé !{% endif %}
+<form action="" method="post">{% csrf_token %}
+{{ form.as_p }}
+<input type="submit" value="Submit" />
+</form>
+
+{% endblock %}
index 4c73075..dda88f8 100644 (file)
@@ -12,7 +12,7 @@
 
 {% if envoi %}Votre message a bien été envoyé !{% endif %}
  
-<form action="" method="post">{% csrf_token %}
+<form enctype="multipart/form-data" action="" method="post">{% csrf_token %}
 {{ form.as_p }}
 <input type="submit" value="Submit" />
 </form>
diff --git a/portal/templates/user_register_email.txt b/portal/templates/user_register_email.txt
new file mode 100644 (file)
index 0000000..dc34c21
--- /dev/null
@@ -0,0 +1,4 @@
+Your have registered for an account in {{site}}.
+
+Your activation key is :{{activation_key}}; it will expire in {{expiration_days}} days.
+
diff --git a/portal/templates/user_register_email_subject.txt b/portal/templates/user_register_email_subject.txt
new file mode 100644 (file)
index 0000000..fc024dd
--- /dev/null
@@ -0,0 +1 @@
+pouet
index 083a1e8..b3969ec 100644 (file)
 # this program; see the file COPYING.  If not, write to the Free Software
 # Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
-from django.conf.urls import patterns, url
+from django.conf.urls import patterns, include, url
 
 # DEPRECATED #from django.forms.formsets import formset_factory
 # DEPRECATED #from portal.forms import RegisterUserForm, RegisterUserStep2Form
 # DEPRECATED #from portal.views import RegisterUserWizardView
 from portal import views
+from portal.views import UserRegisterView, UserValidateView
 
 # DEPRECATED #named_register_forms = (
 # DEPRECATED #    ("step1", RegisterUserForm),
@@ -37,14 +38,16 @@ from portal import views
 
 urlpatterns = patterns('',
     # User registration
-    url(r'^user/register/?$',  views.user_register,  name='user_register'),
+    url(r'^user/register/?$', UserRegisterView.as_view(), name='user_register'),
+    # User validation
+    url(r'^user/validate/?$', UserValidateView.as_view(), name='user_validate'),
     # Slice request
-    url(r'^user/validate/?$',  views.user_validate,  name='user_validate'),
-    # User confirmation
-    url(r'^slice/request/?$',  views.slice_request,  name='slice_request'),
+    #url(r'^slice/request/?$',  views.slice_request,  name='slice_request'),
     # Slice confirmation
-    url(r'^slice/validate/?$', views.slice_validate, name='slice_validate'),
+    #url(r'^slice/validate/?$', views.slice_validate, name='slice_validate'),
 )
+# (r'^accounts/', include('registration.backends.default.urls')),
+
 
 # DEPRECATED #    url(r'^$', views.index, name='index'),
 # DEPRECATED #    url(r"^registerwizard/(?P<step>[-\w]+)/$", register_wizard,
diff --git a/portal/util.py b/portal/util.py
new file mode 100644 (file)
index 0000000..df3606c
--- /dev/null
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+#
+# Code borrowed from the django-registration application
+# https://bitbucket.org/ubernostrum/django-registration
+
+from django.shortcuts import redirect
+from django.views.generic.edit import FormView
+from django.views.generic.base  import TemplateView
+
+class _RequestPassingFormView(FormView):
+    """
+    A version of FormView which passes extra arguments to certain
+    methods, notably passing the HTTP request nearly everywhere, to
+    enable finer-grained processing.
+    
+    """
+    def get(self, request, *args, **kwargs):
+        # Pass request to get_form_class and get_form for per-request
+        # form control.
+        form_class = self.get_form_class(request)
+        form = self.get_form(form_class)
+        return self.render_to_response(self.get_context_data(form=form))
+
+    def post(self, request, *args, **kwargs):
+        # Pass request to get_form_class and get_form for per-request
+        # form control.
+        form_class = self.get_form_class(request)
+        form = self.get_form(form_class)
+        if form.is_valid():
+            # Pass request to form_valid.
+            return self.form_valid(request, form)
+        else:
+            return self.form_invalid(form)
+
+    def get_form_class(self, request=None):
+        return super(_RequestPassingFormView, self).get_form_class()
+
+    def get_form_kwargs(self, request=None, form_class=None):
+        return super(_RequestPassingFormView, self).get_form_kwargs()
+
+    def get_initial(self, request=None):
+        return super(_RequestPassingFormView, self).get_initial()
+
+    def get_success_url(self, request=None, user=None):
+        # We need to be able to use the request and the new user when
+        # constructing success_url.
+        return super(_RequestPassingFormView, self).get_success_url()
+
+    def form_valid(self, form, request=None):
+        return super(_RequestPassingFormView, self).form_valid(form)
+
+    def form_invalid(self, form, request=None):
+        return super(_RequestPassingFormView, self).form_invalid(form)
+
+class RegistrationView(_RequestPassingFormView):
+    """
+    Base class for user registration views.
+    
+    """
+    disallowed_url = 'registration_disallowed'
+    #form_class =  RegistrationForm
+    http_method_names = ['get', 'post', 'head', 'options', 'trace']
+    success_url = None
+    template_name = 'user_register.html' #registration/registration_form.html'
+
+    def dispatch(self, request, *args, **kwargs):
+        """
+        Check that user signup is allowed before even bothering to
+        dispatch or do other processing.
+        
+        """
+        if not self.registration_allowed(request):
+            return redirect(self.disallowed_url)
+        return super(RegistrationView, self).dispatch(request, *args, **kwargs)
+
+    def form_valid(self, request, form):
+        new_user = self.register(request, **form.cleaned_data)
+        success_url = self.get_success_url(request, new_user)
+        
+        # success_url may be a simple string, or a tuple providing the
+        # full argument set for redirect(). Attempting to unpack it
+        # tells us which one it is.
+        try:
+            to, args, kwargs = success_url
+            return redirect(to, *args, **kwargs)
+        except ValueError:
+            return redirect(success_url)
+
+    def registration_allowed(self, request):
+        """
+        Override this to enable/disable user registration, either
+        globally or on a per-request basis.
+        
+        """
+        return True
+
+    def register(self, request, **cleaned_data):
+        """
+        Implement user-registration logic here. Access to both the
+        request and the full cleaned_data of the registration form is
+        available here.
+        
+        """
+        raise NotImplementedError
+
+class ActivationView(TemplateView):
+    """
+    Base class for user activation views.
+    
+    """
+    http_method_names = ['get']
+    template_name = 'registration/activate.html'
+
+    def get(self, request, *args, **kwargs):
+        activated_user = self.activate(request, *args, **kwargs)
+        if activated_user:
+            signals.user_activated.send(sender=self.__class__,
+                                        user=activated_user,
+                                        request=request)
+            success_url = self.get_success_url(request, activated_user)
+            try:
+                to, args, kwargs = success_url
+                return redirect(to, *args, **kwargs)
+            except ValueError:
+                return redirect(success_url)
+        return super(ActivationView, self).get(request, *args, **kwargs)
+
+    def activate(self, request, *args, **kwargs):
+        """
+        Implement account-activation logic here.
+        
+        """
+        raise NotImplementedError
+
+    def get_success_url(self, request, user):
+        raise NotImplementedError
+
+
+# DEPRECATED #def user_register(request):
+# DEPRECATED #    if request.method == 'POST':
+# DEPRECATED #        form = UserRegisterForm(request.POST)
+# DEPRECATED #        if form.is_valid():
+# DEPRECATED #            first_name = form.cleaned_data['first_name']
+# DEPRECATED #            last_name  = form.cleaned_data['last_name']
+# DEPRECATED #            email      = form.cleaned_data['email']
+# DEPRECATED #            password   = form.cleaned_data['password']
+# DEPRECATED #            password2  = form.cleaned_data['password2']
+# DEPRECATED #            keypair    = form.cleaned_data['keypair']
+# DEPRECATED #            ## Ici nous pouvons traiter les données du formulaire
+# DEPRECATED #            #sujet = form.cleaned_data['sujet']
+# DEPRECATED #            #message = form.cleaned_data['message']
+# DEPRECATED #            #envoyeur = form.cleaned_data['envoyeur']
+# DEPRECATED #            #renvoi = form.cleaned_data['renvoi']
+# DEPRECATED #            ## Nous pourrions ici envoyer l'e-mail grâce aux données que nous venons de récupérer
+# DEPRECATED #            #envoi = True
+# DEPRECATED #
+# DEPRECATED #            
+# DEPRECATED #    else:
+# DEPRECATED #        form = UserRegisterForm()
+# DEPRECATED #    return render(request, 'user_register.html', locals())
+# DEPRECATED #
+# DEPRECATED #def user_validate(request):
+# DEPRECATED #    pass
+
+def slice_request(request):
+    if request.method == 'POST':
+        form = SliceRequestForm(request.POST)
+        if form.is_valid():
+            slice_name = form.cleaned_data['slice_name']
+    else:
+        form = SliceRequestForm()
+    return render(request, 'slice_request.html', locals())
+
+def slice_validate(request):
+    pass
index c84c20a..0a27fae 100644 (file)
 # this program; see the file COPYING.  If not, write to the Free Software
 # Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
-from django.shortcuts import render
-from portal.forms     import UserRegisterForm, SliceRequestForm
+from django.conf                import settings
+from django.contrib.sites.models import RequestSite
+from django.contrib.sites.models import Site
 
-def user_register(request):
-    if request.method == 'POST':
-        form = UserRegisterForm(request.POST)
-        if form.is_valid():
-            first_name = form.cleaned_data['first_name']
-            last_name  = form.cleaned_data['last_name']
-            email      = form.cleaned_data['email']
-            password   = form.cleaned_data['password']
-            password2  = form.cleaned_data['password2']
-            keypair    = form.cleaned_data['keypair']
-            ## Ici nous pouvons traiter les données du formulaire
-            #sujet = form.cleaned_data['sujet']
-            #message = form.cleaned_data['message']
-            #envoyeur = form.cleaned_data['envoyeur']
-            #renvoi = form.cleaned_data['renvoi']
-            ## Nous pourrions ici envoyer l'e-mail grâce aux données que nous venons de récupérer
-            #envoi = True
-    else:
-        form = UserRegisterForm()
-    return render(request, 'user_register.html', locals())
+from django.shortcuts           import render
+from portal.forms               import UserRegisterForm, SliceRequestForm
+from portal.util                import RegistrationView, ActivationView
+from portal.models              import PendingUser, PendingSlice
+from portal                     import signals
 
-def user_validate(request):
-    pass
+class UserRegisterView(RegistrationView):
+    """
+    A registration backend which follows a simple workflow:
 
-def slice_request(request):
-    if request.method == 'POST':
-        form = SliceRequestForm(request.POST)
-        if form.is_valid():
-            slice_name = form.cleaned_data['slice_name']
-    else:
-        form = SliceRequestForm()
-    return render(request, 'slice_request.html', locals())
+    1. User signs up, inactive account is created.
+
+    2. Email is sent to user with activation link.
+
+    3. User clicks activation link, account is now active.
+
+    Using this backend requires that
+
+    * ``registration`` be listed in the ``INSTALLED_APPS`` setting
+      (since this backend makes use of models defined in this
+      application).
+
+    * The setting ``ACCOUNT_ACTIVATION_DAYS`` be supplied, specifying
+      (as an integer) the number of days from registration during
+      which a user may activate their account (after that period
+      expires, activation will be disallowed).
+
+    * The creation of the templates
+      ``registration/activation_email_subject.txt`` and
+      ``registration/activation_email.txt``, which will be used for
+      the activation email. See the notes for this backends
+      ``register`` method for details regarding these templates.
+
+    Additionally, registration can be temporarily closed by adding the
+    setting ``REGISTRATION_OPEN`` and setting it to
+    ``False``. Omitting this setting, or setting it to ``True``, will
+    be interpreted as meaning that registration is currently open and
+    permitted.
+
+    Internally, this is accomplished via storing an activation key in
+    an instance of ``registration.models.RegistrationProfile``. See
+    that model and its custom manager for full documentation of its
+    fields and supported operations.
+    
+    """
+    form_class = UserRegisterForm
+    
+    def register(self, request, **cleaned_data):
+        """
+        Given a username, email address and password, register a new
+        user account, which will initially be inactive.
+
+        Along with the new ``User`` object, a new
+        ``registration.models.RegistrationProfile`` will be created,
+        tied to that ``User``, containing the activation key which
+        will be used for this account.
+
+        An email will be sent to the supplied email address; this
+        email should contain an activation link. The email will be
+        rendered using two templates. See the documentation for
+        ``RegistrationProfile.send_activation_email()`` for
+        information about these templates and the contexts provided to
+        them.
+
+        After the ``User`` and ``RegistrationProfile`` are created and
+        the activation email is sent, the signal
+        ``registration.signals.user_registered`` will be sent, with
+        the new ``User`` as the keyword argument ``user`` and the
+        class of this backend as the sender.
+
+        """
+        first_name = cleaned_data['first_name']
+        last_name  = cleaned_data['last_name']
+        email      = cleaned_data['email']
+        password   = cleaned_data['password1']
+        #password2  = cleaned_data['password2']
+        keypair    = cleaned_data['keypair']
+
+        #if Site._meta.installed:
+        #    site = Site.objects.get_current()
+        #else:
+        #    site = RequestSite(request) 
+        site = None
+
+        new_user = PendingUser.objects.create_inactive_user(first_name, last_name, email, password, site)
+        signals.user_registered.send(sender=self.__class__,
+                                     user=new_user,
+                                     request=request)
+        return new_user
+
+    def registration_allowed(self, request):
+        """
+        Indicate whether account registration is currently permitted,
+        based on the value of the setting ``REGISTRATION_OPEN``. This
+        is determined as follows:
+
+        * If ``REGISTRATION_OPEN`` is not specified in settings, or is
+          set to ``True``, registration is permitted.
+
+        * If ``REGISTRATION_OPEN`` is both specified and set to
+          ``False``, registration is not permitted.
+        
+        """
+        return getattr(settings, 'REGISTRATION_OPEN', True)
+
+    def get_success_url(self, request, user):
+        """
+        Return the name of the URL to redirect to after successful
+        user registration.
+        
+        """
+        return ('registration_complete', (), {})
+
+
+class UserValidateView(ActivationView):
+    def activate(self, request, activation_key):
+        """
+        Given an an activation key, look up and activate the user
+        account corresponding to that key (if possible).
+
+        After successful activation, the signal
+        ``registration.signals.user_activated`` will be sent, with the
+        newly activated ``User`` as the keyword argument ``user`` and
+        the class of this backend as the sender.
+        
+        """
+        activated_user = RegistrationProfile.objects.activate_user(activation_key)
+        if activated_user:
+            signals.user_activated.send(sender=self.__class__,
+                                        user=activated_user,
+                                        request=request)
+        return activated_user
+
+    def get_success_url(self, request, user):
+        return ('registration_activation_complete', (), {})
 
-def slice_validate(request):
-    pass
 
 # DEPRECATED #from portal.portalpage  import PortalPage
 # DEPRECATED #from plugins.wizard     import Wizard