From: Jordan Augé Date: Wed, 3 Jul 2013 13:29:58 +0000 (+0200) Subject: first draft of a registration form + email X-Git-Tag: myslice-0.2-1~107 X-Git-Url: http://git.onelab.eu/?p=myslice.git;a=commitdiff_plain;h=7a3fcd0eed5066b3189d79729e95e98167988481 first draft of a registration form + email --- diff --git a/myslice/settings.py b/myslice/settings.py index 4a19345c..bb9e74ad 100644 --- a/myslice/settings.py +++ b/myslice/settings.py @@ -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. diff --git a/myslice/urls.py b/myslice/urls.py index 08d6f9f1..8b1f31d9 100644 --- a/myslice/urls.py +++ b/myslice/urls.py @@ -49,4 +49,5 @@ urlpatterns = patterns( (r'^slice/(?P[\w\.]+)/?$', 'trash.sliceview.slice_view'), # Portal url(r'^portal/', include('portal.urls')), + ) diff --git a/portal/forms.py b/portal/forms.py index 43be6982..513b9526 100644 --- a/portal/forms.py +++ b/portal/forms.py @@ -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 ) diff --git a/portal/models.py b/portal/models.py index 3c950a6f..da0e0863 100644 --- a/portal/models.py +++ b/portal/models.py @@ -20,9 +20,32 @@ # 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 index 00000000..124f2958 --- /dev/null +++ b/portal/signals.py @@ -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 index 00000000..01f48d0e --- /dev/null +++ b/portal/templates/slice_request.html @@ -0,0 +1,20 @@ +{% extends "layout-unfold1.html" %} +{% load i18n %} + +{% block head %} +{{ wizard.form.media }} + +{% endblock %} + +{% block unfold1_main %} + +

Slice request

+ +{% if envoi %}Votre message a bien été envoyé !{% endif %} + +
{% csrf_token %} +{{ form.as_p }} + +
+ +{% endblock %} diff --git a/portal/templates/user_register.html b/portal/templates/user_register.html index 4c730757..dda88f8e 100644 --- a/portal/templates/user_register.html +++ b/portal/templates/user_register.html @@ -12,7 +12,7 @@ {% if envoi %}Votre message a bien été envoyé !{% endif %} -
{% csrf_token %} +{% csrf_token %} {{ form.as_p }}
diff --git a/portal/templates/user_register_email.txt b/portal/templates/user_register_email.txt new file mode 100644 index 00000000..dc34c217 --- /dev/null +++ b/portal/templates/user_register_email.txt @@ -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 index 00000000..fc024ddb --- /dev/null +++ b/portal/templates/user_register_email_subject.txt @@ -0,0 +1 @@ +pouet diff --git a/portal/urls.py b/portal/urls.py index 083a1e82..b3969ec0 100644 --- a/portal/urls.py +++ b/portal/urls.py @@ -20,12 +20,13 @@ # 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[-\w]+)/$", register_wizard, diff --git a/portal/util.py b/portal/util.py new file mode 100644 index 00000000..df3606cb --- /dev/null +++ b/portal/util.py @@ -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 diff --git a/portal/views.py b/portal/views.py index c84c20ae..0a27fae3 100644 --- a/portal/views.py +++ b/portal/views.py @@ -20,44 +20,146 @@ # 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