Source

django-registration / registration / forms.py

Full commit
"""
Forms and validation code for user registration.

"""


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


# 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' }


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 avoid defining a ``save()`` method -- the actual
    saving of collected user data is delegated to the active
    registration backend.

    """
    username = forms.RegexField(regex=r'^\w+$',
                                max_length=30,
                                widget=forms.TextInput(attrs=attrs_dict),
                                label=_("Username"),
                                error_messages={ 'invalid': _("This value must contain only letters, numbers and underscores.") })
    email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
                                                               maxlength=75)),
                             label=_("Email address"))
    password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
                                label=_("Password"))
    password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
                                label=_("Password (again)"))

    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(_("A user with that username already exists."))

    def clean_password2(self):
        """
        Verifiy that the values entered into the two password fields
        match. The error wil be applied to the second password 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['password2']
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 read and agree to the Terms of Service'),
                             error_messages={ 'required': _("You must agree to the terms to register.") })


class RegistrationFormUniqueEmail(RegistrationForm):
    """
    Subclass of ``RegistrationForm`` which enforces uniqueness of
    email addresses.

    """
    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(_("This email address is already in use. Please supply a different email address."))
        return self.cleaned_data['email']


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',
                   'yahoo.com']

    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(_("Registration using free email addresses is prohibited. Please supply a different email address."))
        return self.cleaned_data['email']


class ActivationForm(forms.Form):
    """
    Allows the user to manually supply an activation key.
    """
    key = forms.CharField(label=_(u'activation key'))


from registration.models import RegistrationProfile


class RestorePasswordForm(forms.Form):
    """
    Base form for forms for restoring passwords. Provides all the logic
    necessary to handle the actual work for setting up the confirmation,
    emailing the new password etc. Is not directly usable itself, but
    requires a subclass to provide the ``User`` instance of the account
    to work with in the ``user`` attribute.

    Subclasses are encouraged to allow mixins by using ``set_user`` to
    provide the user account.
    """

    NOT_FOUND_ERROR = _('The data you specified does not match a user account.')
    def raise_not_found(self): raise forms.ValidationError(self.NOT_FOUND_ERROR)

    def clean(self):
        # Make sure the user is active. Note that even without this check,
        # a is_active=False disabled user wouldn't be able to restore his
        # access using this mechanism.
        user = self.get_user()
        if user and not user.is_active:
            self.raise_not_found()
        return self.cleaned_data

    def save(self, **kwargs):
        """Reset the user's password, send out the confirmation email.

        Keyword arguments given in ``kwargs`` will be passed on and allow
        you to customize for example the email templates used.
        """
        user = self.get_user()
        if not user:
            return False
        return RegistrationProfile.objects.reset_password(user=user, **kwargs)

    def get_user(self):
        return getattr(self, 'user', None)

    def set_user(self, user):
        """
        Subclasses should use this to set the ``user`` attribute. This method
        will check if an already existing ``user`` attribute matches what
        was passed in, and raises a ValidationError otherwise. This makes
        mixing different subclasses possible, e.g. a class inheriting from
        "ByEmail" and "ByUsername" versions would then make sure both fields
        fit.
        """
        current_user = getattr(self, 'user', None)
        if current_user and user != current_user:
            self.raise_not_found()
        self.user = user


class RestorePasswordByEmailForm(RestorePasswordForm):
    """
    Reset the password of a user account specified via it's email address.
    This obviously requires unique email addresses and should thus be used
    with ``RegistrationFormUniqueEmail``.
    """

    email = forms.EmailField(label=_("email address"))

    def clean_email(self):
        try:
            self.set_user(User.objects.get(email=self.cleaned_data['email']))
        except User.DoesNotExist:
            self.raise_not_found()
        return self.cleaned_data['email']