Commits

Anonymous committed 4232dea Draft

Change name from registration to registration_html_email.\n Added sending html emails

Comments (0)

Files changed (129)

+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>django-registration-html-mail</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.python.pydev.PyDevBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.python.pydev.pythonNature</nature>
+	</natures>
+</projectDescription>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?>
+
+<pydev_project>
+<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
+<path>/django-registration-html-mail</path>
+</pydev_pathproperty>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+</pydev_project>

build/lib.linux-x86_64-2.7/registration_email/__init__.py

+VERSION = (0, 8, 0, 'final', 0)
+
+def get_version(version=None):
+    """Derives a PEP386-compliant version number from VERSION."""
+    if version is None:
+        version = VERSION
+    assert len(version) == 5
+    assert version[3] in ('alpha', 'beta', 'rc', 'final')
+
+    # Now build the two parts of the version number:
+    # main = X.Y[.Z]
+    # sub = .devN - for pre-alpha releases
+    #     | {a|b|c}N - for alpha, beta and rc releases
+
+    parts = 2 if version[2] == 0 else 3
+    main = '.'.join(str(x) for x in version[:parts])
+
+    sub = ''
+    if version[3] == 'alpha' and version[4] == 0:
+        # At the toplevel, this would cause an import loop.
+        from django.utils.version import get_svn_revision
+        svn_revision = get_svn_revision()[4:]
+        if svn_revision != 'unknown':
+            sub = '.dev%s' % svn_revision
+
+    elif version[3] != 'final':
+        mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'}
+        sub = mapping[version[3]] + str(version[4])
+
+    return main + sub

build/lib.linux-x86_64-2.7/registration_email/admin.py

+from django.contrib import admin
+from django.contrib.sites.models import RequestSite
+from django.contrib.sites.models import Site
+from django.utils.translation import ugettext_lazy as _
+
+from registration_html_email.models import RegistrationProfile
+
+
+class RegistrationAdmin(admin.ModelAdmin):
+    actions = ['activate_users', 'resend_activation_email']
+    list_display = ('user', 'activation_key_expired')
+    raw_id_fields = ['user']
+    search_fields = ('user__username', 'user__first_name', 'user__last_name')
+
+    def activate_users(self, request, queryset):
+        """
+        Activates the selected users, if they are not alrady
+        activated.
+        
+        """
+        for profile in queryset:
+            RegistrationProfile.objects.activate_user(profile.activation_key)
+    activate_users.short_description = _("Activate users")
+
+    def resend_activation_email(self, request, queryset):
+        """
+        Re-sends activation emails for the selected users.
+
+        Note that this will *only* send activation emails for users
+        who are eligible to activate; emails will not be sent to users
+        whose activation keys have expired or who have already
+        activated.
+        
+        """
+        if Site._meta.installed:
+            site = Site.objects.get_current()
+        else:
+            site = RequestSite(request)
+
+        for profile in queryset:
+            if not profile.activation_key_expired():
+                profile.send_activation_email(site)
+    resend_activation_email.short_description = _("Re-send activation emails")
+
+
+admin.site.register(RegistrationProfile, RegistrationAdmin)

build/lib.linux-x86_64-2.7/registration_email/auth_urls.py

+"""
+URL patterns for the views included in ``django.contrib.auth``.
+
+Including these URLs (via the ``include()`` directive) will set up the
+following patterns based at whatever URL prefix they are included
+under:
+
+* User login at ``login/``.
+
+* User logout at ``logout/``.
+
+* The two-step password change at ``password/change/`` and
+  ``password/change/done/``.
+
+* The four-step password reset at ``password/reset/``,
+  ``password/reset/confirm/``, ``password/reset/complete/`` and
+  ``password/reset/done/``.
+
+The default registration backend already has an ``include()`` for
+these URLs, so under the default setup it is not necessary to manually
+include these views. Other backends may or may not include them;
+consult a specific backend's documentation for details.
+
+"""
+
+from django.conf.urls.defaults import *
+
+from django.contrib.auth import views as auth_views
+
+
+urlpatterns = patterns('',
+                       url(r'^login/$',
+                           auth_views.login,
+                           {'template_name': 'registration/login.html'},
+                           name='auth_login'),
+                       url(r'^logout/$',
+                           auth_views.logout,
+                           {'template_name': 'registration/logout.html'},
+                           name='auth_logout'),
+                       url(r'^password/change/$',
+                           auth_views.password_change,
+                           name='auth_password_change'),
+                       url(r'^password/change/done/$',
+                           auth_views.password_change_done,
+                           name='auth_password_change_done'),
+                       url(r'^password/reset/$',
+                           auth_views.password_reset,
+                           name='auth_password_reset'),
+                       url(r'^password/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
+                           auth_views.password_reset_confirm,
+                           name='auth_password_reset_confirm'),
+                       url(r'^password/reset/complete/$',
+                           auth_views.password_reset_complete,
+                           name='auth_password_reset_complete'),
+                       url(r'^password/reset/done/$',
+                           auth_views.password_reset_done,
+                           name='auth_password_reset_done'),
+)

build/lib.linux-x86_64-2.7/registration_email/backends/__init__.py

+from django.core.exceptions import ImproperlyConfigured
+
+
+# Python 2.7 has an importlib with import_module; for older Pythons,
+# Django's bundled copy provides it.
+try: # pragma: no cover
+    from importlib import import_module # pragma: no cover
+except ImportError: # pragma: no cover
+    from django.utils.importlib import import_module # pragma: no cover
+
+def get_backend(path):
+    """
+    Return an instance of a registration backend, given the dotted
+    Python import path (as a string) to the backend class.
+
+    If the backend cannot be located (e.g., because no such module
+    exists, or because the module does not contain a class of the
+    appropriate name), ``django.core.exceptions.ImproperlyConfigured``
+    is raised.
+    
+    """
+    i = path.rfind('.')
+    module, attr = path[:i], path[i+1:]
+    try:
+        mod = import_module(module)
+    except ImportError, e:
+        raise ImproperlyConfigured('Error loading registration_html_email backend %s: "%s"' % (module, e))
+    try:
+        backend_class = getattr(mod, attr)
+    except AttributeError:
+        raise ImproperlyConfigured('Module "%s" does not define a registration_html_email backend named "%s"' % (module, attr))
+    return backend_class()

build/lib.linux-x86_64-2.7/registration_email/backends/default/__init__.py

+from django.conf import settings
+from django.contrib.sites.models import RequestSite
+from django.contrib.sites.models import Site
+
+from registration_html_email import signals
+from registration_html_email.forms import RegistrationForm
+from registration_html_email.models import RegistrationProfile
+
+
+class DefaultBackend(object):
+    """
+    A registration backend which follows a simple workflow:
+
+    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_html_email.models.RegistrationProfile``. See
+    that model and its custom manager for full documentation of its
+    fields and supported operations.
+    
+    """
+    def register(self, request, **kwargs):
+        """
+        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_html_email.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_html_email.signals.user_registered`` will be sent, with
+        the new ``User`` as the keyword argument ``user`` and the
+        class of this backend as the sender.
+
+        """
+        username, email, password = kwargs['username'], kwargs['email'], kwargs['password1']
+        if Site._meta.installed:
+            site = Site.objects.get_current()
+        else:
+            site = RequestSite(request)
+        new_user = RegistrationProfile.objects.create_inactive_user(username, email,
+                                                                    password, site)
+        signals.user_registered.send(sender=self.__class__,
+                                     user=new_user,
+                                     request=request)
+        return new_user
+
+    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_html_email.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 = RegistrationProfile.objects.activate_user(activation_key)
+        if activated:
+            signals.user_activated.send(sender=self.__class__,
+                                        user=activated,
+                                        request=request)
+        return activated
+
+    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_form_class(self, request):
+        """
+        Return the default form class used for user registration_html_email.
+        
+        """
+        return RegistrationForm
+
+    def post_registration_redirect(self, request, user):
+        """
+        Return the name of the URL to redirect to after successful
+        user registration_html_email.
+        
+        """
+        return ('registration_complete', (), {})
+
+    def post_activation_redirect(self, request, user):
+        """
+        Return the name of the URL to redirect to after successful
+        account activation.
+        
+        """
+        return ('registration_activation_complete', (), {})

build/lib.linux-x86_64-2.7/registration_email/backends/default/urls.py

+"""
+URLconf for registration and activation, using django-registration's
+default backend.
+
+If the default behavior of these views is acceptable to you, simply
+use a line like this in your root URLconf to set up the default URLs
+for registration::
+
+    (r'^accounts/', include('registration_html_email.backends.default.urls')),
+
+This will also automatically set up the views in
+``django.contrib.auth`` at sensible default locations.
+
+If you'd like to customize the behavior (e.g., by passing extra
+arguments to the various views) or split up the URLs, feel free to set
+up your own URL patterns for these views instead.
+
+"""
+
+
+from django.conf.urls.defaults import *
+from django.views.generic.simple import direct_to_template
+
+from registration_html_email.views import activate
+from registration_html_email.views import register
+
+
+urlpatterns = patterns('',
+                       url(r'^activate/complete/$',
+                           direct_to_template,
+                           {'template': 'registration/activation_complete.html'},
+                           name='registration_activation_complete'),
+                       # Activation keys get matched by \w+ instead of the more specific
+                       # [a-fA-F0-9]{40} because a bad activation key should still get to the view;
+                       # that way it can return a sensible "invalid key" message instead of a
+                       # confusing 404.
+                       url(r'^activate/(?P<activation_key>\w+)/$',
+                           activate,
+                           {'backend': 'registration_html_email.backends.default.DefaultBackend'},
+                           name='registration_activate'),
+                       url(r'^register/$',
+                           register,
+                           {'backend': 'registration_html_email.backends.default.DefaultBackend'},
+                           name='registration_register'),
+                       url(r'^register/complete/$',
+                           direct_to_template,
+                           {'template': 'registration/registration_complete.html'},
+                           name='registration_complete'),
+                       url(r'^register/closed/$',
+                           direct_to_template,
+                           {'template': 'registration/registration_closed.html'},
+                           name='registration_disallowed'),
+                       (r'', include('registration_html_email.auth_urls')),
+                       )

build/lib.linux-x86_64-2.7/registration_email/backends/simple/__init__.py

+from django.conf import settings
+from django.contrib.auth import authenticate
+from django.contrib.auth import login
+from django.contrib.auth.models import User
+
+from registration_html_email import signals
+from registration_html_email.forms import RegistrationForm
+
+
+class SimpleBackend(object):
+    """
+    A registration backend which implements the simplest possible
+    workflow: a user supplies a username, email address and password
+    (the bare minimum for a useful account), and is immediately signed
+    up and logged in.
+    
+    """
+    def register(self, request, **kwargs):
+        """
+        Create and immediately log in a new user.
+        
+        """
+        username, email, password = kwargs['username'], kwargs['email'], kwargs['password1']
+        User.objects.create_user(username, email, password)
+        
+        # authenticate() always has to be called before login(), and
+        # will return the user we just created.
+        new_user = authenticate(username=username, password=password)
+        login(request, new_user)
+        signals.user_registered.send(sender=self.__class__,
+                                     user=new_user,
+                                     request=request)
+        return new_user
+
+    def activate(self, **kwargs):
+        raise NotImplementedError
+
+    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_form_class(self, request):
+        return RegistrationForm
+
+    def post_registration_redirect(self, request, user):
+        """
+        After registration, redirect to the user's account page.
+        
+        """
+        return (user.get_absolute_url(), (), {})
+
+    def post_activation_redirect(self, request, user):
+        raise NotImplementedError

build/lib.linux-x86_64-2.7/registration_email/backends/simple/urls.py

+"""
+URLconf for registration and activation, using django-registration's
+one-step backend.
+
+If the default behavior of these views is acceptable to you, simply
+use a line like this in your root URLconf to set up the default URLs
+for registration::
+
+    (r'^accounts/', include('registration_html_email.backends.simple.urls')),
+
+This will also automatically set up the views in
+``django.contrib.auth`` at sensible default locations.
+
+If you'd like to customize the behavior (e.g., by passing extra
+arguments to the various views) or split up the URLs, feel free to set
+up your own URL patterns for these views instead.
+
+"""
+
+
+from django.conf.urls.defaults import *
+from django.views.generic.simple import direct_to_template
+
+from registration_html_email.views import activate
+from registration_html_email.views import register
+
+
+urlpatterns = patterns('',
+                       url(r'^register/$',
+                           register,
+                           {'backend': 'registration_html_email.backends.simple.SimpleBackend'},
+                           name='registration_register'),
+                       url(r'^register/closed/$',
+                           direct_to_template,
+                           {'template': 'registration/registration_closed.html'},
+                           name='registration_disallowed'),
+                       (r'', include('registration_html_email.auth_urls')),
+                       )

build/lib.linux-x86_64-2.7/registration_email/forms.py

+"""
+Forms and validation code for user registration_html_email.
+
+"""
+
+
+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 may contain only letters, numbers and @/./+/-/_ characters.")})
+    email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
+                                                               maxlength=75)),
+                             label=_("E-mail"))
+    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.
+        
+        """
+        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(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
+
+
+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']

build/lib.linux-x86_64-2.7/registration_email/management/__init__.py

Empty file added.

build/lib.linux-x86_64-2.7/registration_email/management/commands/__init__.py

Empty file added.

build/lib.linux-x86_64-2.7/registration_email/management/commands/cleanupregistration.py

+"""
+A management command which deletes expired accounts (e.g.,
+accounts which signed up but never activated) from the database.
+
+Calls ``RegistrationProfile.objects.delete_expired_users()``, which
+contains the actual logic for determining which accounts are deleted.
+
+"""
+
+from django.core.management.base import NoArgsCommand
+
+from registration_html_email.models import RegistrationProfile
+
+
+class Command(NoArgsCommand):
+    help = "Delete expired user registrations from the database"
+
+    def handle_noargs(self, **options):
+        RegistrationProfile.objects.delete_expired_users()

build/lib.linux-x86_64-2.7/registration_email/models.py

+import datetime
+import hashlib
+import random
+import re
+
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.db import models
+from django.db import transaction
+from django.template.loader import render_to_string, get_template
+from django.utils.translation import ugettext_lazy as _
+from django.core.mail import EmailMultiAlternatives
+from django.template import Context
+
+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}$')
+
+
+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_inactive_user(self, username, 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``.
+        
+        """
+        new_user = User.objects.create_user(username, email, password)
+        new_user.is_active = False
+        new_user.save()
+
+        registration_profile = self.create_profile(new_user)
+
+        if send_email:
+            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 RegistrationProfile(models.Model):
+    """
+    A simple profile which stores an activation key for use during
+    user account registration_html_email.
+    
+    Generally, you will not want to interact directly with instances
+    of this model; the provided manager includes methods
+    for creating and activating new accounts, as well as for cleaning
+    out accounts which have never been activated.
+    
+    While it is possible to use this model as the value of the
+    ``AUTH_PROFILE_MODULE`` setting, it's not recommended that you do
+    so. This model's sole purpose is to store data temporarily during
+    account registration and activation.
+    
+    """
+    ACTIVATED = u"ALREADY_ACTIVATED"
+    
+    user = models.ForeignKey(User, unique=True, verbose_name=_('user'))
+    activation_key = models.CharField(_('activation key'), max_length=40)
+    
+    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:
+
+        ``registration/activation_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.
+
+        ``registration/activation_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('registration/activation_email_subject.txt',
+                                   ctx_dict)
+        # Email subject *must not* contain newlines
+        subject = ''.join(subject.splitlines())
+        
+        message = render_to_string('registration/activation_email.txt',
+                                   ctx_dict)
+          
+        plaintemplate = get_template('mails/activation_email.txt')
+        htmltemplate = get_template('mails/activation_email.html')
+        
+        d = Context(ctx_dict)
+        
+        from_email, to = settings.DEFAULT_FROM_EMAIL, self.user.email
+        text_content = plaintemplate.render(d)
+        html_content = htmltemplate.render(d)
+        msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
+        msg.attach_alternative(html_content, "text/html")
+        msg.send()
+        #self.user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
+    

build/lib.linux-x86_64-2.7/registration_email/signals.py

+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"])

build/lib.linux-x86_64-2.7/registration_email/tests/__init__.py

+from django.test import TestCase
+
+import registration
+
+from registration_html_email.tests.backends import *
+from registration_html_email.tests.forms import *
+from registration_html_email.tests.models import *
+from registration_html_email.tests.views import *
+
+
+class RegistrationVersionInfoTests(TestCase):
+    """
+    Test django-registration's internal version-reporting
+    infrastructure.
+    
+    """
+    def setUp(self):
+        self.version = registration_html_email.VERSION
+
+    def tearDown(self):
+        registration_html_email.VERSION = self.version
+    
+    def test_get_version(self):
+        """
+        Test the version-info reporting.
+        
+        """
+        versions = [
+            {'version': (1, 0, 0, 'alpha', 0),
+             'expected': "1.0 pre-alpha"},
+            {'version': (1, 0, 1, 'alpha', 1),
+             'expected': "1.0.1 alpha 1"},
+            {'version': (1, 1, 0, 'beta', 2),
+             'expected': "1.1 beta 2"},
+            {'version': (1, 2, 1, 'rc', 3),
+             'expected': "1.2.1 rc 3"},
+            {'version': (1, 3, 0, 'final', 0),
+             'expected': "1.3"},
+            {'version': (1, 4, 1, 'beta', 0),
+             'expected': "1.4.1 beta"},
+            ]
+        
+        for version_dict in versions:
+            registration_html_email.VERSION = version_dict['version']
+            self.assertEqual(registration_html_email.get_version(), version_dict['expected'])

build/lib.linux-x86_64-2.7/registration_email/tests/backends.py

+import datetime
+
+from django.conf import settings
+from django.contrib import admin
+from django.contrib.auth.models import User
+from django.contrib.sessions.middleware import SessionMiddleware
+from django.contrib.sites.models import Site
+from django.core import mail
+from django.core.exceptions import ImproperlyConfigured
+from django.core.handlers.wsgi import WSGIRequest
+from django.test import Client
+from django.test import TestCase
+
+from registration_html_email import forms
+from registration_html_email import signals
+from registration_html_email.admin import RegistrationAdmin
+from registration_html_email.backends import get_backend
+from registration_html_email.backends.default import DefaultBackend
+from registration_html_email.backends.simple import SimpleBackend
+from registration_html_email.models import RegistrationProfile
+
+
+class _MockRequestClient(Client):
+    """
+    A ``django.test.Client`` subclass which can return mock
+    ``HttpRequest`` objects.
+    
+    """
+    def request(self, **request):
+        """
+        Rather than issuing a request and returning the response, this
+        simply constructs an ``HttpRequest`` object and returns it.
+        
+        """
+        environ = {
+            'HTTP_COOKIE': self.cookies,
+            'PATH_INFO': '/',
+            'QUERY_STRING': '',
+            'REMOTE_ADDR': '127.0.0.1',
+            'REQUEST_METHOD': 'GET',
+            'SCRIPT_NAME': '',
+            'SERVER_NAME': 'testserver',
+            'SERVER_PORT': '80',
+            'SERVER_PROTOCOL': 'HTTP/1.1',
+            'wsgi.version': (1,0),
+            'wsgi.url_scheme': 'http',
+            'wsgi.errors': self.errors,
+            'wsgi.multiprocess':True,
+            'wsgi.multithread': False,
+            'wsgi.run_once': False,
+            'wsgi.input': None,
+            }
+        environ.update(self.defaults)
+        environ.update(request)
+        request = WSGIRequest(environ)
+
+        # We have to manually add a session since we'll be bypassing
+        # the middleware chain.
+        session_middleware = SessionMiddleware()
+        session_middleware.process_request(request)
+        return request
+
+
+def _mock_request():
+    """
+    Construct and return a mock ``HttpRequest`` object; this is used
+    in testing backend methods which expect an ``HttpRequest`` but
+    which are not being called from views.
+    
+    """
+    return _MockRequestClient().request()
+
+
+class BackendRetrievalTests(TestCase):
+    """
+    Test that utilities for retrieving the active backend work
+    properly.
+
+    """
+    def test_get_backend(self):
+        """
+        Verify that ``get_backend()`` returns the correct value when
+        passed a valid backend.
+
+        """
+        self.failUnless(isinstance(get_backend('registration_html_email.backends.default.DefaultBackend'),
+                                   DefaultBackend))
+
+    def test_backend_error_invalid(self):
+        """
+        Test that a nonexistent/unimportable backend raises the
+        correct exception.
+
+        """
+        self.assertRaises(ImproperlyConfigured, get_backend,
+                          'registration_html_email.backends.doesnotexist.NonExistentBackend')
+
+    def test_backend_attribute_error(self):
+        """
+        Test that a backend module which exists but does not have a
+        class of the specified name raises the correct exception.
+        
+        """
+        self.assertRaises(ImproperlyConfigured, get_backend,
+                          'registration_html_email.backends.default.NonexistentBackend')
+
+
+class DefaultRegistrationBackendTests(TestCase):
+    """
+    Test the default registration backend.
+
+    Running these tests successfully will require two templates to be
+    created for the sending of activation emails; details on these
+    templates and their contexts may be found in the documentation for
+    the default backend.
+
+    """
+    backend = DefaultBackend()
+    
+    def setUp(self):
+        """
+        Create an instance of the default backend for use in testing,
+        and set ``ACCOUNT_ACTIVATION_DAYS`` if it's not set already.
+
+        """
+        self.old_activation = getattr(settings, 'ACCOUNT_ACTIVATION_DAYS', None)
+        if self.old_activation is None:
+            settings.ACCOUNT_ACTIVATION_DAYS = 7 # pragma: no cover
+
+    def tearDown(self):
+        """
+        Yank out ``ACCOUNT_ACTIVATION_DAYS`` back out if it wasn't
+        originally set.
+
+        """
+        if self.old_activation is None:
+            settings.ACCOUNT_ACTIVATION_DAYS = self.old_activation # pragma: no cover
+
+    def test_registration(self):
+        """
+        Test the registration process: registration creates a new
+        inactive account and a new profile with activation key,
+        populates the correct account data and sends an activation
+        email.
+
+        """
+        new_user = self.backend.register(_mock_request(),
+                                         username='bob',
+                                         email='bob@example.com',
+                                         password1='secret')
+
+        # Details of the returned user must match what went in.
+        self.assertEqual(new_user.username, 'bob')
+        self.failUnless(new_user.check_password('secret'))
+        self.assertEqual(new_user.email, 'bob@example.com')
+
+        # New user must not be active.
+        self.failIf(new_user.is_active)
+
+        # A registration profile was created, and an activation email
+        # was sent.
+        self.assertEqual(RegistrationProfile.objects.count(), 1)
+        self.assertEqual(len(mail.outbox), 1)
+
+    def test_registration_no_sites(self):
+        """
+        Test that registration still functions properly when
+        ``django.contrib.sites`` is not installed; the fallback will
+        be a ``RequestSite`` instance.
+        
+        """
+        Site._meta.installed = False
+        new_user = self.backend.register(_mock_request(),
+                                         username='bob',
+                                         email='bob@example.com',
+                                         password1='secret')
+
+        self.assertEqual(new_user.username, 'bob')
+        self.failUnless(new_user.check_password('secret'))
+        self.assertEqual(new_user.email, 'bob@example.com')
+
+        self.failIf(new_user.is_active)
+
+        self.assertEqual(RegistrationProfile.objects.count(), 1)
+        self.assertEqual(len(mail.outbox), 1)
+        
+        Site._meta.installed = True
+
+    def test_valid_activation(self):
+        """
+        Test the activation process: activating within the permitted
+        window sets the account's ``is_active`` field to ``True`` and
+        resets the activation key.
+
+        """
+        valid_user = self.backend.register(_mock_request(),
+                                           username='alice',
+                                           email='alice@example.com',
+                                           password1='swordfish')
+
+        valid_profile = RegistrationProfile.objects.get(user=valid_user)
+        activated = self.backend.activate(_mock_request(),
+                                          valid_profile.activation_key)
+        self.assertEqual(activated.username, valid_user.username)
+        self.failUnless(activated.is_active)
+
+        # Fetch the profile again to verify its activation key has
+        # been reset.
+        valid_profile = RegistrationProfile.objects.get(user=valid_user)
+        self.assertEqual(valid_profile.activation_key,
+                         RegistrationProfile.ACTIVATED)
+
+    def test_invalid_activation(self):
+        """
+        Test the activation process: trying to activate outside the
+        permitted window fails, and leaves the account inactive.
+
+        """
+        expired_user = self.backend.register(_mock_request(),
+                                             username='bob',
+                                             email='bob@example.com',
+                                             password1='secret')
+
+        expired_user.date_joined = expired_user.date_joined - datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS)
+        expired_user.save()
+        expired_profile = RegistrationProfile.objects.get(user=expired_user)
+        self.failIf(self.backend.activate(_mock_request(),
+                                          expired_profile.activation_key))
+        self.failUnless(expired_profile.activation_key_expired())
+
+    def test_allow(self):
+        """
+        Test that the setting ``REGISTRATION_OPEN`` appropriately
+        controls whether registration is permitted.
+
+        """
+        old_allowed = getattr(settings, 'REGISTRATION_OPEN', True)
+        settings.REGISTRATION_OPEN = True
+        self.failUnless(self.backend.registration_allowed(_mock_request()))
+
+        settings.REGISTRATION_OPEN = False
+        self.failIf(self.backend.registration_allowed(_mock_request()))
+        settings.REGISTRATION_OPEN = old_allowed
+
+    def test_form_class(self):
+        """
+        Test that the default form class returned is
+        ``registration_html_email.forms.RegistrationForm``.
+
+        """
+        self.failUnless(self.backend.get_form_class(_mock_request()) is forms.RegistrationForm)
+
+    def test_post_registration_redirect(self):
+        """
+        Test that the default post-registration redirect is the named
+        pattern ``registration_complete``.
+
+        """
+        self.assertEqual(self.backend.post_registration_redirect(_mock_request(), User()),
+                         ('registration_complete', (), {}))
+
+    def test_registration_signal(self):
+        """
+        Test that registering a user sends the ``user_registered``
+        signal.
+        
+        """
+        def receiver(sender, **kwargs):
+            self.failUnless('user' in kwargs)
+            self.assertEqual(kwargs['user'].username, 'bob')
+            self.failUnless('request' in kwargs)
+            self.failUnless(isinstance(kwargs['request'], WSGIRequest))
+            received_signals.append(kwargs.get('signal'))
+
+        received_signals = []
+        signals.user_registered.connect(receiver, sender=self.backend.__class__)
+
+        self.backend.register(_mock_request(),
+                              username='bob',
+                              email='bob@example.com',
+                              password1='secret')
+
+        self.assertEqual(len(received_signals), 1)
+        self.assertEqual(received_signals, [signals.user_registered])
+
+    def test_activation_signal_success(self):
+        """
+        Test that successfully activating a user sends the
+        ``user_activated`` signal.
+        
+        """
+        def receiver(sender, **kwargs):
+            self.failUnless('user' in kwargs)
+            self.assertEqual(kwargs['user'].username, 'bob')
+            self.failUnless('request' in kwargs)
+            self.failUnless(isinstance(kwargs['request'], WSGIRequest))
+            received_signals.append(kwargs.get('signal'))
+
+        received_signals = []
+        signals.user_activated.connect(receiver, sender=self.backend.__class__)
+
+        new_user = self.backend.register(_mock_request(),
+                                         username='bob',
+                                         email='bob@example.com',
+                                         password1='secret')
+        profile = RegistrationProfile.objects.get(user=new_user)
+        self.backend.activate(_mock_request(), profile.activation_key)
+
+        self.assertEqual(len(received_signals), 1)
+        self.assertEqual(received_signals, [signals.user_activated])
+
+    def test_activation_signal_failure(self):
+        """
+        Test that an unsuccessful activation attempt does not send the
+        ``user_activated`` signal.
+        
+        """
+        receiver = lambda sender, **kwargs: received_signals.append(kwargs.get('signal'))
+
+        received_signals = []
+        signals.user_activated.connect(receiver, sender=self.backend.__class__)
+
+        new_user = self.backend.register(_mock_request(),
+                                         username='bob',
+                                         email='bob@example.com',
+                                         password1='secret')
+        new_user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS + 1)
+        new_user.save()
+        profile = RegistrationProfile.objects.get(user=new_user)
+        self.backend.activate(_mock_request(), profile.activation_key)
+
+        self.assertEqual(len(received_signals), 0)
+
+    def test_email_send_action(self):
+        """
+        Test re-sending of activation emails via admin action.
+        
+        """
+        admin_class = RegistrationAdmin(RegistrationProfile, admin.site)
+        
+        alice = self.backend.register(_mock_request(),
+                                      username='alice',
+                                      email='alice@example.com',
+                                      password1='swordfish')
+        
+        admin_class.resend_activation_email(_mock_request(),
+                                            RegistrationProfile.objects.all())
+        self.assertEqual(len(mail.outbox), 2) # One on registering, one more on the resend.
+        
+        RegistrationProfile.objects.filter(user=alice).update(activation_key=RegistrationProfile.ACTIVATED)
+        admin_class.resend_activation_email(_mock_request(),
+                                            RegistrationProfile.objects.all())
+        self.assertEqual(len(mail.outbox), 2) # No additional email because the account has activated.
+
+    def test_email_send_action_no_sites(self):
+        """
+        Test re-sending of activation emails via admin action when
+        ``django.contrib.sites`` is not installed; the fallback will
+        be a ``RequestSite`` instance.
+        
+        """
+        Site._meta.installed = False
+        admin_class = RegistrationAdmin(RegistrationProfile, admin.site)
+        
+        alice = self.backend.register(_mock_request(),
+                                      username='alice',
+                                      email='alice@example.com',
+                                      password1='swordfish')
+        
+        admin_class.resend_activation_email(_mock_request(),
+                                            RegistrationProfile.objects.all())
+        self.assertEqual(len(mail.outbox), 2) # One on registering, one more on the resend.
+        
+        RegistrationProfile.objects.filter(user=alice).update(activation_key=RegistrationProfile.ACTIVATED)
+        admin_class.resend_activation_email(_mock_request(),
+                                            RegistrationProfile.objects.all())
+        self.assertEqual(len(mail.outbox), 2) # No additional email because the account has activated.
+        Site._meta.installed = True
+
+    def test_activation_action(self):
+        """
+        Test manual activation of users view admin action.
+        
+        """
+        admin_class = RegistrationAdmin(RegistrationProfile, admin.site)
+
+        alice = self.backend.register(_mock_request(),
+                                      username='alice',
+                                      email='alice@example.com',
+                                      password1='swordfish')
+
+        admin_class.activate_users(_mock_request(),
+                                   RegistrationProfile.objects.all())
+        self.failUnless(User.objects.get(username='alice').is_active)
+
+
+class SimpleRegistrationBackendTests(TestCase):
+    """
+    Test the simple registration backend, which does signup and
+    immediate activation.
+    
+    """
+    backend = SimpleBackend()
+    
+    def test_registration(self):
+        """
+        Test the registration process: registration creates a new
+        inactive account and a new profile with activation key,
+        populates the correct account data and sends an activation
+        email.
+
+        """
+        new_user = self.backend.register(_mock_request(),
+                                         username='bob',
+                                         email='bob@example.com',
+                                         password1='secret')
+
+        # Details of the returned user must match what went in.
+        self.assertEqual(new_user.username, 'bob')
+        self.failUnless(new_user.check_password('secret'))
+        self.assertEqual(new_user.email, 'bob@example.com')
+
+        # New user must not be active.
+        self.failUnless(new_user.is_active)
+
+    def test_allow(self):
+        """
+        Test that the setting ``REGISTRATION_OPEN`` appropriately
+        controls whether registration is permitted.
+
+        """
+        old_allowed = getattr(settings, 'REGISTRATION_OPEN', True)
+        settings.REGISTRATION_OPEN = True
+        self.failUnless(self.backend.registration_allowed(_mock_request()))
+
+        settings.REGISTRATION_OPEN = False
+        self.failIf(self.backend.registration_allowed(_mock_request()))
+        settings.REGISTRATION_OPEN = old_allowed
+
+    def test_form_class(self):
+        """
+        Test that the default form class returned is
+        ``registration_html_email.forms.RegistrationForm``.
+
+        """
+        self.failUnless(self.backend.get_form_class(_mock_request()) is forms.RegistrationForm)
+
+    def test_post_registration_redirect(self):
+        """
+        Test that the default post-registration redirect is the public
+        URL of the new user account.
+
+        """
+        new_user = self.backend.register(_mock_request(),
+                                         username='bob',
+                                         email='bob@example.com',
+                                         password1='secret')
+        
+        self.assertEqual(self.backend.post_registration_redirect(_mock_request(), new_user),
+                         (new_user.get_absolute_url(), (), {}))
+
+    def test_registration_signal(self):
+        """
+        Test that registering a user sends the ``user_registered``
+        signal.
+        
+        """
+        def receiver(sender, **kwargs):
+            self.failUnless('user' in kwargs)
+            self.assertEqual(kwargs['user'].username, 'bob')
+            self.failUnless('request' in kwargs)
+            self.failUnless(isinstance(kwargs['request'], WSGIRequest))
+            received_signals.append(kwargs.get('signal'))
+
+        received_signals = []
+        signals.user_registered.connect(receiver, sender=self.backend.__class__)
+
+        self.backend.register(_mock_request(),
+                              username='bob',
+                              email='bob@example.com',
+                              password1='secret')
+
+        self.assertEqual(len(received_signals), 1)
+        self.assertEqual(received_signals, [signals.user_registered])
+
+    def test_activation(self):
+        """
+        Test that activating against this backend is an error.
+        
+        """
+        self.assertRaises(NotImplementedError, self.backend.activate,
+                          request=_mock_request())
+
+    def test_post_activation_redirect(self):
+        """
+        Test that asking for a post-activation redirect from this
+        backend is an error.
+        
+        """
+        self.assertRaises(NotImplementedError, self.backend.post_activation_redirect,
+                          request=_mock_request(), user=User())

build/lib.linux-x86_64-2.7/registration_email/tests/forms.py

+from django.contrib.auth.models import User
+from django.test import TestCase
+
+from registration_html_email import forms
+
+
+class RegistrationFormTests(TestCase):
+    """
+    Test the default registration forms.
+
+    """
+    def test_registration_form(self):
+        """
+        Test that ``RegistrationForm`` enforces username constraints
+        and matching passwords.
+
+        """
+        # Create a user so we can verify that duplicate usernames aren't
+        # permitted.
+        User.objects.create_user('alice', 'alice@example.com', 'secret')
+
+        invalid_data_dicts = [
+            # Non-alphanumeric username.
+            {'data': {'username': 'foo/bar',
+                      'email': 'foo@example.com',
+                      'password1': 'foo',
+                      'password2': 'foo'},
+            'error': ('username', [u"This value may contain only letters, numbers and @/./+/-/_ characters."])},
+            # Already-existing username.
+            {'data': {'username': 'alice',
+                      'email': 'alice@example.com',
+                      'password1': 'secret',
+                      'password2': 'secret'},
+            'error': ('username', [u"A user with that username already exists."])},
+            # Mismatched passwords.
+            {'data': {'username': 'foo',
+                      'email': 'foo@example.com',
+                      'password1': 'foo',
+                      'password2': 'bar'},
+            'error': ('__all__', [u"The two password fields didn't match."])},
+            ]
+
+        for invalid_dict in invalid_data_dicts:
+            form = forms.RegistrationForm(data=invalid_dict['data'])
+            self.failIf(form.is_valid())
+            self.assertEqual(form.errors[invalid_dict['error'][0]],
+                             invalid_dict['error'][1])
+
+        form = forms.RegistrationForm(data={'username': 'foo',
+                                            'email': 'foo@example.com',
+                                            'password1': 'foo',
+                                            'password2': 'foo'})
+        self.failUnless(form.is_valid())
+
+    def test_registration_form_tos(self):
+        """
+        Test that ``RegistrationFormTermsOfService`` requires
+        agreement to the terms of service.
+
+        """
+        form = forms.RegistrationFormTermsOfService(data={'username': 'foo',
+                                                          'email': 'foo@example.com',
+                                                          'password1': 'foo',
+                                                          'password2': 'foo'})
+        self.failIf(form.is_valid())
+        self.assertEqual(form.errors['tos'],
+                         [u"You must agree to the terms to register"])
+
+        form = forms.RegistrationFormTermsOfService(data={'username': 'foo',
+                                                          'email': 'foo@example.com',
+                                                          'password1': 'foo',
+                                                          'password2': 'foo',
+                                                          'tos': 'on'})
+        self.failUnless(form.is_valid())
+
+    def test_registration_form_unique_email(self):
+        """
+        Test that ``RegistrationFormUniqueEmail`` validates uniqueness
+        of email addresses.
+
+        """
+        # Create a user so we can verify that duplicate addresses
+        # aren't permitted.
+        User.objects.create_user('alice', 'alice@example.com', 'secret')
+
+        form = forms.RegistrationFormUniqueEmail(data={'username': 'foo',
+                                                       'email': 'alice@example.com',
+                                                       'password1': 'foo',
+                                                       'password2': 'foo'})
+        self.failIf(form.is_valid())
+        self.assertEqual(form.errors['email'],
+                         [u"This email address is already in use. Please supply a different email address."])
+
+        form = forms.RegistrationFormUniqueEmail(data={'username': 'foo',
+                                                       'email': 'foo@example.com',
+                                                       'password1': 'foo',
+                                                       'password2': 'foo'})
+        self.failUnless(form.is_valid())
+
+    def test_registration_form_no_free_email(self):
+        """
+        Test that ``RegistrationFormNoFreeEmail`` disallows
+        registration with free email addresses.
+
+        """
+        base_data = {'username': 'foo',
+                     'password1': 'foo',
+                     'password2': 'foo'}
+        for domain in forms.RegistrationFormNoFreeEmail.bad_domains:
+            invalid_data = base_data.copy()
+            invalid_data['email'] = u"foo@%s" % domain
+            form = forms.RegistrationFormNoFreeEmail(data=invalid_data)
+            self.failIf(form.is_valid())
+            self.assertEqual(form.errors['email'],
+                             [u"Registration using free email addresses is prohibited. Please supply a different email address."])
+
+        base_data['email'] = 'foo@example.com'
+        form = forms.RegistrationFormNoFreeEmail(data=base_data)
+        self.failUnless(form.is_valid())

build/lib.linux-x86_64-2.7/registration_email/tests/models.py

+import datetime
+import re
+
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.core import mail
+from django.core import management
+from django.test import TestCase
+from django.utils.hashcompat import sha_constructor
+
+from registration_html_email.models import RegistrationProfile
+
+
+class RegistrationModelTests(TestCase):
+    """