Source code for beat.web.ui.registration.forms

#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

###############################################################################
#                                                                             #
# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
# Contact: beat.support@idiap.ch                                              #
#                                                                             #
# This file is part of the beat.web module of the BEAT platform.              #
#                                                                             #
# Commercial License Usage                                                    #
# Licensees holding valid commercial BEAT licenses may use this file in       #
# accordance with the terms contained in a written agreement between you      #
# and Idiap. For further information contact tto@idiap.ch                     #
#                                                                             #
# Alternatively, this file may be used under the terms of the GNU Affero      #
# Public License version 3 as published by the Free Software and appearing    #
# in the file LICENSE.AGPL included in the packaging of this file.            #
# The BEAT platform is distributed in the hope that it will be useful, but    #
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
#                                                                             #
# You should have received a copy of the GNU Affero Public License along      #
# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
#                                                                             #
###############################################################################

"""
Forms and validation code for user registration.

"""

import datetime

from django.contrib.auth.models import User
from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _

from .models import RegistrationProfile
from .models import PreregistrationProfile

from ...accounts.models import SupervisionTrack
from ...accounts.models import Profile

# I put this on all required fields, because it's easier to pick up
# on them with CSS or JavaScript if they have a class of "required"
# in the HTML. Your mileage may vary. If/when Django ticket #3515
# lands in trunk, this will no longer be necessary.
attrs_dict = { 'class': 'required' }


[docs]class RegistrationForm(forms.Form): """ 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 either preserve the base ``save()`` or implement a ``save()`` method which returns a ``User``. """ username = forms.RegexField(regex=r'^\w+$', max_length=30, widget=forms.TextInput(attrs=attrs_dict), label=_(u'Username')) first_name = forms.RegexField(regex=r'^[\w ]+$', max_length=30, widget=forms.TextInput(attrs=attrs_dict), label=_(u'First name')) last_name = forms.RegexField(regex=r'^[\w ]+$', max_length=30, widget=forms.TextInput(attrs=attrs_dict), label=_(u'Last name')) email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=75)), label=_(u'Institutional E-mail address')) password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), label=_(u'Password')) password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), label=_(u'Password (again)')) supervisor = forms.RegexField(regex=r'^\w+$', max_length=30, widget=forms.TextInput(attrs=attrs_dict), label=_(u'Supervisor Username'))
[docs] def clean_username(self): """ Validate that the username is alphanumeric and is not already in use. """ try: user = User.objects.get(username__iexact=self.cleaned_data['username']) except User.DoesNotExist: return self.cleaned_data['username'] raise forms.ValidationError(_(u'This username is already taken. Please choose another.'))
[docs] def clean_supervisor(self): """ Validate that the username is alphanumeric and exists. """ try: user = User.objects.get(username__iexact=self.cleaned_data['supervisor']) if user.profile.status == Profile.BLOCKED: raise forms.ValidationError(_(u'This user is not a valid supervisor. Please choose another.')) except User.DoesNotExist: raise forms.ValidationError(_(u'This supervisor username does not exist. Please choose another.')) if not user.profile.is_supervisor: raise forms.ValidationError(_(u'This user is not a recognized supervisor. Please choose another.')) return self.cleaned_data['supervisor']
[docs] 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']: self._errors["password2"] = self.error_class([_(u'You must type the same password each time')]) del self.cleaned_data["password1"] del self.cleaned_data["password2"] return self.cleaned_data
[docs] def save(self): """ Create the new ``User`` and ``RegistrationProfile``, and returns the ``User`` (by calling ``RegistrationProfile.objects.create_inactive_user()``). """ new_user = RegistrationProfile.objects.create_inactive_user( username=self.cleaned_data['username'], first_name=self.cleaned_data['first_name'], last_name=self.cleaned_data['last_name'], password=self.cleaned_data['password1'], email=self.cleaned_data['email'], ) #Create and assign key new_user.profile.supervision_key = new_user.profile._generate_current_supervision_key() supervisor = User.objects.get(username = self.cleaned_data['supervisor']) supervisiontrack = SupervisionTrack.objects.create( supervisee = new_user, supervisor = supervisor, is_valid = False, ) #Assign key to supervision track supervisiontrack.supervision_key = new_user.profile.supervision_key supervisiontrack.save() new_user.profile.is_supervisor = False new_user.profile.status = Profile.NEWUSER new_user.profile.registration_date = datetime.datetime.now() new_user.profile.supervision.add(supervisiontrack) new_user.save() return new_user
[docs]class PreregistrationForm(forms.Form): """ Form for pre-registering a new user account. """ first_name = forms.RegexField(regex=r'^[\w ]+$', max_length=30, widget=forms.TextInput(attrs=attrs_dict), label=_(u'First name')) last_name = forms.RegexField(regex=r'^[\w ]+$', max_length=30, widget=forms.TextInput(attrs=attrs_dict), label=_(u'Last name')) email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=75)), label=_(u'E-mail address'))
[docs] def save(self): profile = PreregistrationProfile() profile.first_name = self.cleaned_data['first_name'] profile.last_name = self.cleaned_data['last_name'] profile.email = self.cleaned_data['email'] profile.save() return profile
[docs]class RegistrationFormTermsOfService(RegistrationForm): """ Subclass of ``RegistrationForm`` which adds a required checkbox for agreeing to a site's Terms of Service. """ tos = forms.BooleanField( widget=forms.CheckboxInput(attrs=attrs_dict), label=u'I have carefully read the <a href="">Terms of Service</a>, which include the Privacy and Data Protection Terms of Use, and fully agree and undertake to comply with all provisions therein by checking this box.', error_messages=dict(required=u"You must agree to the Terms of Service in order to register"), )
[docs]class RegistrationFormUniqueEmail(RegistrationForm): """ Subclass of ``RegistrationForm`` which enforces uniqueness of email addresses. """
[docs] def clean_email(self): """ Validate that the supplied email address is unique for the site. """ if User.objects.filter(email__iexact=self.cleaned_data['email']): raise forms.ValidationError(_(u'This email address is already in use. Please supply a different email address.')) return self.cleaned_data['email']
[docs]class RegistrationFormNoFreeEmail(RegistrationForm): """ Subclass of ``RegistrationForm`` which disallows registration with email addresses from popular free webmail services; moderately useful for preventing automated spam registrations. To change the list of banned domains, subclass this form and override the attribute ``bad_domains``. """ bad_domains = ['aim.com', 'aol.com', 'email.com', 'gmail.com', 'googlemail.com', 'hotmail.com', 'hushmail.com', 'msn.com', 'mail.ru', 'mailinator.com', 'live.com']
[docs] def clean_email(self): """ Check the supplied email address against a list of known free webmail domains. """ email_domain = self.cleaned_data['email'].split('@')[1] if email_domain in self.bad_domains: raise forms.ValidationError(_(u'Registration using free email addresses is prohibited. Please supply a different email address.')) return self.cleaned_data['email']
[docs]class BlockedUserRevalidationForm(forms.Form): """ 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 either preserve the base ``save()`` or implement a ``save()`` method which returns a ``User``. """ username = forms.RegexField(regex=r'^\w+$', max_length=30, widget=forms.TextInput(attrs=attrs_dict), label=_(u'Username')) password = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), label=_(u'Password')) supervisor = forms.RegexField(regex=r'^\w+$', max_length=30, widget=forms.TextInput(attrs=attrs_dict), label=_(u'Supervisor Username (Your account needs to be re-validated by a recognized person)'))
[docs] def clean_username(self): """ Validate that the username is alphanumeric and user exists """ try: user = User.objects.get(username__iexact=self.cleaned_data['username']) return self.cleaned_data['username'] except User.DoesNotExist: raise forms.ValidationError(_(u'This username has not been recognized'))
[docs] def clean_supervisor(self): """ Validate that the username is alphanumeric and exists. """ try: user = User.objects.get(username__iexact=self.cleaned_data['supervisor']) except User.DoesNotExist: raise forms.ValidationError(_(u'This supervisor username does not exist. Please choose another.')) if not user.profile.is_supervisor: raise forms.ValidationError(_(u'This user is not a recognized supervisor. Please choose another.')) return self.cleaned_data['supervisor']
[docs] 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']: self._errors["password2"] = self.error_class([_(u'You must type the same password each time')]) del self.cleaned_data["password1"] del self.cleaned_data["password2"] return self.cleaned_data
[docs]class RegistrationSupervisorForm(forms.Form): """ 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 either preserve the base ``save()`` or implement a ``save()`` method which returns a ``User``. """ username = forms.RegexField(regex=r'^\w+$', max_length=30, widget=forms.TextInput(attrs=attrs_dict), label=_(u'Username')) first_name = forms.RegexField(regex=r'^[\w ]+$', max_length=30, widget=forms.TextInput(attrs=attrs_dict), label=_(u'First name')) last_name = forms.RegexField(regex=r'^[\w ]+$', max_length=30, widget=forms.TextInput(attrs=attrs_dict), label=_(u'Last name')) email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=75)), label=_(u'Institutional E-mail address')) password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), label=_(u'Password')) password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), label=_(u'Password (again)'))
[docs] def clean_username(self): """ Validate that the username is alphanumeric and is not already in use. """ try: user = User.objects.get(username__iexact=self.cleaned_data['username']) except User.DoesNotExist: return self.cleaned_data['username'] raise forms.ValidationError(_(u'This username is already taken. Please choose another.'))
[docs] 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']: self._errors["password2"] = self.error_class([_(u'You must type the same password each time')]) del self.cleaned_data["password1"] del self.cleaned_data["password2"] return self.cleaned_data
[docs] def save(self): """ Create the new ``User`` and ``RegistrationProfile``, and returns the ``User`` (by calling ``RegistrationProfile.objects.create_inactive_user()``). """ new_user = RegistrationProfile.objects.create_inactive_user( username=self.cleaned_data['username'], first_name=self.cleaned_data['first_name'], last_name=self.cleaned_data['last_name'], password=self.cleaned_data['password1'], email=self.cleaned_data['email'], ) #Create and assign key new_user.profile.supervision_key = new_user.profile._generate_current_supervision_key() new_user.profile.is_supervisor = True new_user.profile.status = Profile.NEWUSER new_user.profile.registration_date = datetime.datetime.now() new_user.save() return new_user
[docs]class RegistrationFormTermsOfServiceSupervisor(RegistrationSupervisorForm): """ Subclass of ``RegistrationForm`` which adds a required checkbox for agreeing to a site's Terms of Service. """ tos = forms.BooleanField( widget=forms.CheckboxInput(attrs=attrs_dict), label=u'I have carefully read the <a href="">Terms of Service</a>, which include the Privacy and Data Protection Terms of Use, and fully agree and undertake to comply with all provisions therein by checking this box.', error_messages=dict(required=u"You must agree to the Terms of Service in order to register"), )