[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#686204: marked as done (unblock: python-django-registration/0.8-1)



Your message dated Fri, 31 Aug 2012 20:26:50 +0100
with message-id <1346441210.7606.15.camel@jacala.jungle.funky-badger.org>
and subject line Re: Bug#686204: unblock: python-django-registration/0.8-1
has caused the Debian Bug report #686204,
regarding unblock: python-django-registration/0.8-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
686204: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=686204
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package python-django-registration

The version in wheezy is not really compatible with Django 1.4 (RC
bug #686104) and upstream released version 0.8 which adresses the
compatibility issue but which is also not backwards compatible.

When I uploaded 0.8 to unstable I thought that the package was a leaf
package (only used by people like me who have django website using it)
but I discovered a bit late that it gained reverse dependencies. Thanks
to some pestering by Paul van der Vlis we investigated how they were
affected and it turns out that the only reverse dependency in wheezy
(mumble-django) could quickly be updated with non-invasive changes.

I have not investigated whether it would have been possible to fix
0.7 to be 100% compatible with python-django 1.4 and I really believe
that such an effort is not worth it. People just expect to have
django-registration 0.8 available. It's entirely my fault if I have
missed the freeze deadline, and I can understand that you're not happy
with this late request.

But I would welcome a freeze exception for python-django-registration
and a supplementary unblock for mumble-django too. The user base is small
and it's almost a leaf package so I don't expect any further problem due
to those unblocks.

unblock python-django-registration/0.8-1
unblock mumble-django/2.7-6

The debdiff is rather massive as it's a new upstream version with lots of
docs, tests and translations. I have extracted a shorter debdiff which
contains only code changes but I don't see much value in reviewing it
since it contains many backwards incompatible changes which are expected.

$ diffstat /tmp/debdiff-django-registration-filtered 
 MANIFEST.in                                |   11 -
 PKG-INFO                                   |    7 -
 debian/changelog                           |   21 +++
 debian/control                             |    6 
 debian/python-django-registration.doc-base |    7 -
 debian/python-django-registration.docs     |    1 
 debian/rules                               |   15 --
 registration/__init__.py                   |   30 ++++
 registration/admin.py                      |   39 +++++
 registration/auth_urls.py                  |   58 ++++++++
 registration/backends/__init__.py          |   32 ++++
 registration/backends/default/__init__.py  |  139 ++++++++++++++++++++
 registration/backends/default/urls.py      |   54 +++++++
 registration/backends/simple/__init__.py   |   64 +++++++++
 registration/backends/simple/urls.py       |   38 +++++
 registration/forms.py                      |   61 +++-----
 registration/models.py                     |  169 +++++++++++++-----------
 registration/signals.py                    |    8 +
 registration/urls.py                       |   75 +---------
 registration/views.py                      |  200 +++++++++++++++++------------
 setup.py                                   |   10 +
 21 files changed, 751 insertions(+), 294 deletions(-)

Concerning mumble-django the debdiff is minimal, it only adds one patch which
creates a supplementary file and modifies another one:

$ diffstat /tmp/debdiff-mumble-django
 changelog                 |    8 ++++++++
 patches/03-templates.diff |   29 +++++++++++++++++++++++++++++
 patches/series            |    1 +
 3 files changed, 38 insertions(+)

-- System Information:
Debian Release: wheezy/sid
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'proposed-updates'), (500, 'unstable'), (500, 'testing'), (500, 'stable'), (150, 'experimental')
Architecture: i386 (x86_64)
Foreign Architectures: amd64

Kernel: Linux 3.4-trunk-amd64 (SMP w/2 CPU cores)
Locale: LANG=fr_FR.utf8, LC_CTYPE=fr_FR.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
diff -Nru python-django-registration-0.7/debian/changelog python-django-registration-0.8/debian/changelog
--- python-django-registration-0.7/debian/changelog	2011-11-02 12:25:30.000000000 +0100
+++ python-django-registration-0.8/debian/changelog	2012-07-10 16:15:34.000000000 +0200
@@ -1,3 +1,24 @@
+python-django-registration (0.8-1) unstable; urgency=low
+
+  * New upstream release.
+    - Now fully compatible with Django 1.4 which is in Debian Wheezy.
+    - No longer use the deprecated "sha" module. Closes: #581721
+  * Update Standards-Version to 3.9.3.
+  * Replace python-docutil build-dependency by one on python-sphinx to be able
+    to generate the HTML documentation with the new upstream makefile.
+  * Update the dh_auto_build override to include the "make html" call
+    that generates the documentation and create
+    debian/python-django-registration.docs to install the generated
+    documentation.
+  * Create a dh_auto_clean override to remove the (upstream provided and)
+    generated documentation in docs/_build/html.
+  * Use dh_sphinxdoc to get rid of embedded javascript files that sphinx-build
+    generates. Add accordingly ${sphinxdoc:Depends} to Depends.
+  * Update the doc-base registration to use the new path for the HTML
+    documentation.
+
+ -- Raphaël Hertzog <hertzog@debian.org>  Tue, 10 Jul 2012 16:04:20 +0200
+
 python-django-registration (0.7-3) unstable; urgency=low
 
   * Provide README.Debian in place of upstream's README.txt. Closes: #646589
diff -Nru python-django-registration-0.7/debian/control python-django-registration-0.8/debian/control
--- python-django-registration-0.7/debian/control	2011-11-02 12:24:49.000000000 +0100
+++ python-django-registration-0.8/debian/control	2012-07-10 16:06:33.000000000 +0200
@@ -3,16 +3,16 @@
 Priority: optional
 Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org>
 Uploaders: Raphaël Hertzog <hertzog@debian.org>, David Spreen <netzwurm@debian.org>, Stephan Peijnik <debian@sp.or.at>
-Standards-Version: 3.9.2
+Standards-Version: 3.9.3
 Build-Depends: debhelper (>= 8), python (>= 2.6.6-3~)
-Build-Depends-Indep: python-docutils, python-django (>= 1.0)
+Build-Depends-Indep: python-django (>= 1.0), python-sphinx (>= 1.0.7+dfsg) | python3-sphinx
 Homepage: http://www.bitbucket.org/ubernostrum/django-registration/wiki/
 Vcs-Svn: svn://svn.debian.org/python-modules/packages/python-django-registration/trunk/
 Vcs-Browser: http://anonscm.debian.org/viewvc/python-modules/packages/python-django-registration/trunk/
 
 Package: python-django-registration
 Architecture: all
-Depends: ${python:Depends}, ${misc:Depends}, python-django (>= 1.0)
+Depends: ${python:Depends}, ${misc:Depends}, ${sphinxdoc:Depends}, python-django (>= 1.0)
 Description: User-registration application for Django
  This is a fairly simple user-registration application for Django,
  designed to make allowing user signups as painless as possible.
diff -Nru python-django-registration-0.7/debian/python-django-registration.doc-base python-django-registration-0.8/debian/python-django-registration.doc-base
--- python-django-registration-0.7/debian/python-django-registration.doc-base	2010-01-27 16:28:55.000000000 +0100
+++ python-django-registration-0.8/debian/python-django-registration.doc-base	2012-07-10 15:39:51.000000000 +0200
@@ -6,8 +6,5 @@
 Section: Programming/Python
 
 Format: HTML
-Index: /usr/share/doc/python-django-registration/overview.html
-Files: /usr/share/doc/python-django-registration/*.html
-
-Format: Text
-Files: /usr/share/doc/python-django-registration/overview.txt* /usr/share/doc/python-django-registration/views.txt* /usr/share/doc/python-django-registration/models.txt* /usr/share/doc/python-django-registration/forms.txt*
+Index: /usr/share/doc/python-django-registration/html/index.html
+Files: /usr/share/doc/python-django-registration/html/*.html
diff -Nru python-django-registration-0.7/debian/python-django-registration.docs python-django-registration-0.8/debian/python-django-registration.docs
--- python-django-registration-0.7/debian/python-django-registration.docs	1970-01-01 01:00:00.000000000 +0100
+++ python-django-registration-0.8/debian/python-django-registration.docs	2012-07-10 15:36:17.000000000 +0200
@@ -0,0 +1 @@
+docs/_build/html
diff -Nru python-django-registration-0.7/debian/rules python-django-registration-0.8/debian/rules
--- python-django-registration-0.7/debian/rules	2011-11-02 11:45:40.000000000 +0100
+++ python-django-registration-0.8/debian/rules	2012-07-10 15:50:02.000000000 +0200
@@ -3,21 +3,18 @@
 PKG = $(shell dh_listpackages)
 
 %:
-	dh $@ --with python2
+	dh $@ --with python2,sphinxdoc
+
+override_dh_auto_clean:
+	rm -rf docs/_build/html
+	dh_auto_clean
 
 override_dh_auto_build:
 	cd registration && /usr/bin/django-admin compilemessages
+	cd docs && $(MAKE) html
 	dh_auto_build
 
 override_dh_auto_install:
 	dh_auto_install
 	# Drop .po files and keep only .mo files (compiled)
 	find debian/$(PKG)/ -name '*.po' -exec rm {} \;
-
-override_dh_installdocs:
-	dh_installdocs
-	# Generate documentation
-	rst2html <docs/overview.txt >debian/$(PKG)/usr/share/doc/$(PKG)/overview.html
-	rst2html <docs/views.txt >debian/$(PKG)/usr/share/doc/$(PKG)/views.html
-	rst2html <docs/models.txt >debian/$(PKG)/usr/share/doc/$(PKG)/models.html
-	rst2html <docs/forms.txt >debian/$(PKG)/usr/share/doc/$(PKG)/forms.html
diff -Nru python-django-registration-0.7/MANIFEST.in python-django-registration-0.8/MANIFEST.in
--- python-django-registration-0.7/MANIFEST.in	2008-09-24 23:43:46.000000000 +0200
+++ python-django-registration-0.8/MANIFEST.in	2009-11-22 10:06:45.000000000 +0100
@@ -1,9 +1,8 @@
-include CHANGELOG.txt
-include INSTALL.txt
-include LICENSE.txt
+include CHANGELOG
+include INSTALL
+include LICENSE
 include MANIFEST.in
-include README.txt
-include AUTHORS.txt
+include README
+include AUTHORS
 recursive-include docs *
-recursive-include registration/bin *
 recursive-include registration/locale *
diff -Nru python-django-registration-0.7/PKG-INFO python-django-registration-0.8/PKG-INFO
--- python-django-registration-0.7/PKG-INFO	2008-11-06 10:25:31.000000000 +0100
+++ python-django-registration-0.8/PKG-INFO	2012-03-24 19:49:22.000000000 +0100
@@ -1,19 +1,20 @@
 Metadata-Version: 1.0
 Name: django-registration
-Version: 0.7
+Version: 0.8
 Summary: An extensible user-registration application for Django
 Home-page: http://www.bitbucket.org/ubernostrum/django-registration/wiki/
 Author: James Bennett
 Author-email: james@b-list.org
 License: UNKNOWN
-Download-URL: http://www.bitbucket.org/ubernostrum/django-registration/get/v0.7.gz
+Download-URL: http://www.bitbucket.org/ubernostrum/django-registration/get/v0.8.gz
 Description: UNKNOWN
 Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
+Classifier: Development Status :: 4 - Beta
 Classifier: Environment :: Web Environment
 Classifier: Framework :: Django
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Utilities
diff -Nru python-django-registration-0.7/registration/admin.py python-django-registration-0.8/registration/admin.py
--- python-django-registration-0.7/registration/admin.py	2008-09-24 23:43:46.000000000 +0200
+++ python-django-registration-0.8/registration/admin.py	2011-08-04 21:48:55.000000000 +0200
@@ -1,11 +1,46 @@
 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.models import RegistrationProfile
 
 
 class RegistrationAdmin(admin.ModelAdmin):
-    list_display = ('__unicode__', 'activation_key_expired')
-    search_fields = ('user__username', 'user__first_name')
+    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)
diff -Nru python-django-registration-0.7/registration/auth_urls.py python-django-registration-0.8/registration/auth_urls.py
--- python-django-registration-0.7/registration/auth_urls.py	1970-01-01 01:00:00.000000000 +0100
+++ python-django-registration-0.8/registration/auth_urls.py	2009-10-09 13:31:23.000000000 +0200
@@ -0,0 +1,58 @@
+"""
+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'),
+)
diff -Nru python-django-registration-0.7/registration/backends/default/__init__.py python-django-registration-0.8/registration/backends/default/__init__.py
--- python-django-registration-0.7/registration/backends/default/__init__.py	1970-01-01 01:00:00.000000000 +0100
+++ python-django-registration-0.8/registration/backends/default/__init__.py	2009-12-28 14:01:06.000000000 +0100
@@ -0,0 +1,139 @@
+from django.conf import settings
+from django.contrib.sites.models import RequestSite
+from django.contrib.sites.models import Site
+
+from registration import signals
+from registration.forms import RegistrationForm
+from registration.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.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.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.
+
+        """
+        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.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.
+        
+        """
+        return RegistrationForm
+
+    def post_registration_redirect(self, request, user):
+        """
+        Return the name of the URL to redirect to after successful
+        user registration.
+        
+        """
+        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', (), {})
diff -Nru python-django-registration-0.7/registration/backends/default/urls.py python-django-registration-0.8/registration/backends/default/urls.py
--- python-django-registration-0.7/registration/backends/default/urls.py	1970-01-01 01:00:00.000000000 +0100
+++ python-django-registration-0.8/registration/backends/default/urls.py	2010-02-28 08:24:08.000000000 +0100
@@ -0,0 +1,54 @@
+"""
+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.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.views import activate
+from registration.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.backends.default.DefaultBackend'},
+                           name='registration_activate'),
+                       url(r'^register/$',
+                           register,
+                           {'backend': 'registration.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.auth_urls')),
+                       )
diff -Nru python-django-registration-0.7/registration/backends/__init__.py python-django-registration-0.8/registration/backends/__init__.py
--- python-django-registration-0.7/registration/backends/__init__.py	1970-01-01 01:00:00.000000000 +0100
+++ python-django-registration-0.8/registration/backends/__init__.py	2011-08-04 22:01:32.000000000 +0200
@@ -0,0 +1,32 @@
+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 backend %s: "%s"' % (module, e))
+    try:
+        backend_class = getattr(mod, attr)
+    except AttributeError:
+        raise ImproperlyConfigured('Module "%s" does not define a registration backend named "%s"' % (module, attr))
+    return backend_class()
diff -Nru python-django-registration-0.7/registration/backends/simple/__init__.py python-django-registration-0.8/registration/backends/simple/__init__.py
--- python-django-registration-0.7/registration/backends/simple/__init__.py	1970-01-01 01:00:00.000000000 +0100
+++ python-django-registration-0.8/registration/backends/simple/__init__.py	2009-12-28 14:03:18.000000000 +0100
@@ -0,0 +1,64 @@
+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 import signals
+from registration.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
diff -Nru python-django-registration-0.7/registration/backends/simple/urls.py python-django-registration-0.8/registration/backends/simple/urls.py
--- python-django-registration-0.7/registration/backends/simple/urls.py	1970-01-01 01:00:00.000000000 +0100
+++ python-django-registration-0.8/registration/backends/simple/urls.py	2010-02-28 09:57:09.000000000 +0100
@@ -0,0 +1,38 @@
+"""
+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.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.views import activate
+from registration.views import register
+
+
+urlpatterns = patterns('',
+                       url(r'^register/$',
+                           register,
+                           {'backend': 'registration.backends.simple.SimpleBackend'},
+                           name='registration_register'),
+                       url(r'^register/closed/$',
+                           direct_to_template,
+                           {'template': 'registration/registration_closed.html'},
+                           name='registration_disallowed'),
+                       (r'', include('registration.auth_urls')),
+                       )
diff -Nru python-django-registration-0.7/registration/forms.py python-django-registration-0.8/registration/forms.py
--- python-django-registration-0.7/registration/forms.py	2008-09-28 10:42:13.000000000 +0200
+++ python-django-registration-0.8/registration/forms.py	2012-03-24 19:19:46.000000000 +0100
@@ -4,18 +4,16 @@
 """
 
 
+from django.contrib.auth.models import User
 from django import forms
 from django.utils.translation import ugettext_lazy as _
-from django.contrib.auth.models import User
-
-from registration.models import RegistrationProfile
 
 
 # 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' }
+attrs_dict = {'class': 'required'}
 
 
 class RegistrationForm(forms.Form):
@@ -26,23 +24,23 @@
     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()`` which accepts the ``profile_callback`` keyword
-    argument and passes it through to
-    ``RegistrationProfile.objects.create_inactive_user()``.
+    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+$',
+    username = forms.RegexField(regex=r'^[\w.@+-]+$',
                                 max_length=30,
                                 widget=forms.TextInput(attrs=attrs_dict),
-                                label=_(u'username'))
+                                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=_(u'email address'))
+                             label=_("E-mail"))
     password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
-                                label=_(u'password'))
+                                label=_("Password"))
     password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
-                                label=_(u'password (again)'))
+                                label=_("Password (again)"))
     
     def clean_username(self):
         """
@@ -50,11 +48,11 @@
         in use.
         
         """
-        try:
-            user = User.objects.get(username__iexact=self.cleaned_data['username'])
-        except User.DoesNotExist:
+        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']
-        raise forms.ValidationError(_(u'This username is already taken. Please choose another.'))
 
     def clean(self):
         """
@@ -66,26 +64,8 @@
         """
         if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:
             if self.cleaned_data['password1'] != self.cleaned_data['password2']:
-                raise forms.ValidationError(_(u'You must type the same password each time'))
+                raise forms.ValidationError(_("The two password fields didn't match."))
         return self.cleaned_data
-    
-    def save(self, profile_callback=None):
-        """
-        Create the new ``User`` and ``RegistrationProfile``, and
-        returns the ``User``.
-        
-        This is essentially a light wrapper around
-        ``RegistrationProfile.objects.create_inactive_user()``,
-        feeding it the form data and a profile callback (see the
-        documentation on ``create_inactive_user()`` for details) if
-        supplied.
-        
-        """
-        new_user = RegistrationProfile.objects.create_inactive_user(username=self.cleaned_data['username'],
-                                                                    password=self.cleaned_data['password1'],
-                                                                    email=self.cleaned_data['email'],
-                                                                    profile_callback=profile_callback)
-        return new_user
 
 
 class RegistrationFormTermsOfService(RegistrationForm):
@@ -96,7 +76,7 @@
     """
     tos = forms.BooleanField(widget=forms.CheckboxInput(attrs=attrs_dict),
                              label=_(u'I have read and agree to the Terms of Service'),
-                             error_messages={ 'required': u"You must agree to the terms to register" })
+                             error_messages={'required': _("You must agree to the terms to register")})
 
 
 class RegistrationFormUniqueEmail(RegistrationForm):
@@ -112,7 +92,7 @@
         
         """
         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.'))
+            raise forms.ValidationError(_("This email address is already in use. Please supply a different email address."))
         return self.cleaned_data['email']
 
 
@@ -128,7 +108,8 @@
     """
     bad_domains = ['aim.com', 'aol.com', 'email.com', 'gmail.com',
                    'googlemail.com', 'hotmail.com', 'hushmail.com',
-                   'msn.com', 'mail.ru', 'mailinator.com', 'live.com']
+                   'msn.com', 'mail.ru', 'mailinator.com', 'live.com',
+                   'yahoo.com']
     
     def clean_email(self):
         """
@@ -138,5 +119,5 @@
         """
         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.'))
+            raise forms.ValidationError(_("Registration using free email addresses is prohibited. Please supply a different email address."))
         return self.cleaned_data['email']
diff -Nru python-django-registration-0.7/registration/__init__.py python-django-registration-0.8/registration/__init__.py
--- python-django-registration-0.7/registration/__init__.py	2008-09-24 23:43:46.000000000 +0200
+++ python-django-registration-0.8/registration/__init__.py	2012-03-24 19:38:37.000000000 +0100
@@ -0,0 +1,30 @@
+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
diff -Nru python-django-registration-0.7/registration/models.py python-django-registration-0.8/registration/models.py
--- python-django-registration-0.7/registration/models.py	2008-11-06 10:17:38.000000000 +0100
+++ python-django-registration-0.8/registration/models.py	2012-03-24 19:07:51.000000000 +0100
@@ -1,14 +1,19 @@
 import datetime
+import hashlib
 import random
 import re
-import sha
 
 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
 from django.utils.translation import ugettext_lazy as _
-from django.contrib.auth.models import User
-from django.contrib.sites.models import Site
+
+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}$')
@@ -38,9 +43,9 @@
         
         To prevent reactivation of an account which has been
         deactivated by site administrators, the activation key is
-        reset to the string ``ALREADY_ACTIVATED`` after successful
-        activation.
-        
+        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
@@ -59,74 +64,29 @@
                 return user
         return False
     
-    def create_inactive_user(self, username, password, email,
-                             send_email=True, profile_callback=None):
+    def create_inactive_user(self, username, email, password,
+                             site, send_email=True):
         """
-        Create a new, inactive ``User``, generates a
+        Create a new, inactive ``User``, generate a
         ``RegistrationProfile`` and email its activation key to the
         ``User``, returning the new ``User``.
-        
-        To disable the email, call with ``send_email=False``.
-
-        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. It receives one context variable, ``site``, which
-            is the currently-active
-            ``django.contrib.sites.models.Site`` instance. 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. It
-            will receive three context variables: ``activation_key``
-            will be the user's activation key (for use in constructing
-            a URL to activate the account), ``expiration_days`` will
-            be the number of days for which the key will be valid and
-            ``site`` will be the currently-active
-            ``django.contrib.sites.models.Site`` instance.
-        
-        To enable creation of a custom user profile along with the
-        ``User`` (e.g., the model specified in the
-        ``AUTH_PROFILE_MODULE`` setting), define a function which
-        knows how to create and save an instance of that model with
-        appropriate default values, and pass it as the keyword
-        argument ``profile_callback``. This function should accept one
-        keyword argument:
-
-        ``user``
-            The ``User`` to relate the profile to.
+        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 profile_callback is not None:
-            profile_callback(user=new_user)
-        
+
         if send_email:
-            from django.core.mail import send_mail
-            current_site = Site.objects.get_current()
-            
-            subject = render_to_string('registration/activation_email_subject.txt',
-                                       { 'site': current_site })
-            # Email subject *must not* contain newlines
-            subject = ''.join(subject.splitlines())
-            
-            message = render_to_string('registration/activation_email.txt',
-                                       { 'activation_key': registration_profile.activation_key,
-                                         'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
-                                         'site': current_site })
-            
-            send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [new_user.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
@@ -137,8 +97,11 @@
         username and a random salt.
         
         """
-        salt = sha.new(str(random.random())).hexdigest()[:5]
-        activation_key = sha.new(salt+user.username).hexdigest()
+        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)
         
@@ -183,11 +146,14 @@
         
         """
         for profile in self.all():
-            if profile.activation_key_expired():
-                user = profile.user
-                if not user.is_active:
-                    user.delete()
-
+            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):
     """
@@ -202,10 +168,7 @@
     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, and a mechanism for
-    automatically creating an instance of a site-specific profile
-    model is provided via the ``create_inactive_user`` on
-    ``RegistrationManager``.
+    account registration and activation.
     
     """
     ACTIVATED = u"ALREADY_ACTIVATED"
@@ -231,9 +194,9 @@
         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 ``ALREADY_ACTIVATED``. Re-activating is
-           not permitted, and so this method returns ``True`` in this
-           case.
+           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
@@ -246,5 +209,57 @@
         """
         expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS)
         return self.activation_key == self.ACTIVATED or \
-               (self.user.date_joined + expiration_date <= datetime.datetime.now())
+               (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)
+        
+        self.user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
+    
diff -Nru python-django-registration-0.7/registration/signals.py python-django-registration-0.8/registration/signals.py
--- python-django-registration-0.7/registration/signals.py	1970-01-01 01:00:00.000000000 +0100
+++ python-django-registration-0.8/registration/signals.py	2009-10-09 13:31:23.000000000 +0200
@@ -0,0 +1,8 @@
+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 -Nru python-django-registration-0.7/registration/urls.py python-django-registration-0.8/registration/urls.py
--- python-django-registration-0.7/registration/urls.py	2008-09-28 11:32:59.000000000 +0200
+++ python-django-registration-0.8/registration/urls.py	2009-10-09 13:31:23.000000000 +0200
@@ -1,72 +1,15 @@
 """
-URLConf for Django user registration and authentication.
-
-If the default behavior of the registration 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.urls')),
-
-This will also automatically set up the views in
-``django.contrib.auth`` at sensible default locations.
-
-But 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. If you do, it's a
-good idea to use the names ``registration_activate``,
-``registration_complete`` and ``registration_register`` for the
-various steps of the user-signup process.
+Backwards-compatible URLconf for existing django-registration
+installs; this allows the standard ``include('registration.urls')`` to
+continue working, but that usage is deprecated and will be removed for
+django-registration 1.0. For new installs, use
+``include('registration.backends.default.urls')``.
 
 """
 
+import warnings
 
-from django.conf.urls.defaults import *
-from django.views.generic.simple import direct_to_template
-from django.contrib.auth import views as auth_views
-
-from registration.views import activate
-from registration.views import register
-
+warnings.warn("include('registration.urls') is deprecated; use include('registration.backends.default.urls') instead.",
+              PendingDeprecationWarning)
 
-urlpatterns = patterns('',
-                       # 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,
-                           name='registration_activate'),
-                       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'),
-                       url(r'^register/$',
-                           register,
-                           name='registration_register'),
-                       url(r'^register/complete/$',
-                           direct_to_template,
-                           {'template': 'registration/registration_complete.html'},
-                           name='registration_complete'),
-                       )
+from registration.backends.default.urls import *
diff -Nru python-django-registration-0.7/registration/views.py python-django-registration-0.8/registration/views.py
--- python-django-registration-0.7/registration/views.py	2008-09-25 00:44:53.000000000 +0200
+++ python-django-registration-0.8/registration/views.py	2010-02-28 08:25:24.000000000 +0100
@@ -4,129 +4,162 @@
 """
 
 
-from django.conf import settings
-from django.core.urlresolvers import reverse
-from django.http import HttpResponseRedirect
+from django.shortcuts import redirect
 from django.shortcuts import render_to_response
 from django.template import RequestContext
 
-from registration.forms import RegistrationForm
-from registration.models import RegistrationProfile
+from registration.backends import get_backend
 
 
-def activate(request, activation_key,
+def activate(request, backend,
              template_name='registration/activate.html',
-             extra_context=None):
+             success_url=None, extra_context=None, **kwargs):
     """
-    Activate a ``User``'s account from an activation key, if their key
-    is valid and hasn't expired.
-    
-    By default, use the template ``registration/activate.html``; to
-    change this, pass the name of a template as the keyword argument
-    ``template_name``.
-    
-    **Required arguments**
-    
-    ``activation_key``
-       The activation key to validate and use for activating the
-       ``User``.
-    
-    **Optional arguments**
-       
+    Activate a user's account.
+
+    The actual activation of the account will be delegated to the
+    backend specified by the ``backend`` keyword argument (see below);
+    the backend's ``activate()`` method will be called, passing any
+    keyword arguments captured from the URL, and will be assumed to
+    return a ``User`` if activation was successful, or a value which
+    evaluates to ``False`` in boolean context if not.
+
+    Upon successful activation, the backend's
+    ``post_activation_redirect()`` method will be called, passing the
+    ``HttpRequest`` and the activated ``User`` to determine the URL to
+    redirect the user to. To override this, pass the argument
+    ``success_url`` (see below).
+
+    On unsuccessful activation, will render the template
+    ``registration/activate.html`` to display an error message; to
+    override thise, pass the argument ``template_name`` (see below).
+
+    **Arguments**
+
+    ``backend``
+        The dotted Python import path to the backend class to
+        use. Required.
+
     ``extra_context``
         A dictionary of variables to add to the template context. Any
         callable object in this dictionary will be called to produce
-        the end result which appears in the context.
+        the end result which appears in the context. Optional.
+
+    ``success_url``
+        The name of a URL pattern to redirect to on successful
+        acivation. This is optional; if not specified, this will be
+        obtained by calling the backend's
+        ``post_activation_redirect()`` method.
     
     ``template_name``
-        A custom template to use.
+        A custom template to use. This is optional; if not specified,
+        this will default to ``registration/activate.html``.
+
+    ``\*\*kwargs``
+        Any keyword arguments captured from the URL, such as an
+        activation key, which will be passed to the backend's
+        ``activate()`` method.
     
     **Context:**
     
-    ``account``
-        The ``User`` object corresponding to the account, if the
-        activation was successful. ``False`` if the activation was not
-        successful.
-    
-    ``expiration_days``
-        The number of days for which activation keys stay valid after
-        registration.
-    
-    Any extra variables supplied in the ``extra_context`` argument
-    (see above).
+    The context will be populated from the keyword arguments captured
+    in the URL, and any extra variables supplied in the
+    ``extra_context`` argument (see above).
     
     **Template:**
     
     registration/activate.html or ``template_name`` keyword argument.
     
     """
-    activation_key = activation_key.lower() # Normalize before trying anything with it.
-    account = RegistrationProfile.objects.activate_user(activation_key)
+    backend = get_backend(backend)
+    account = backend.activate(request, **kwargs)
+
+    if account:
+        if success_url is None:
+            to, args, kwargs = backend.post_activation_redirect(request, account)
+            return redirect(to, *args, **kwargs)
+        else:
+            return redirect(success_url)
+
     if extra_context is None:
         extra_context = {}
     context = RequestContext(request)
     for key, value in extra_context.items():
         context[key] = callable(value) and value() or value
+
     return render_to_response(template_name,
-                              { 'account': account,
-                                'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS },
+                              kwargs,
                               context_instance=context)
 
 
-def register(request, success_url=None,
-             form_class=RegistrationForm, profile_callback=None,
+def register(request, backend, success_url=None, form_class=None,
+             disallowed_url='registration_disallowed',
              template_name='registration/registration_form.html',
              extra_context=None):
     """
     Allow a new user to register an account.
-    
-    Following successful registration, issue a redirect; by default,
-    this will be whatever URL corresponds to the named URL pattern
-    ``registration_complete``, which will be
-    ``/accounts/register/complete/`` if using the included URLConf. To
-    change this, point that named pattern at another URL, or pass your
-    preferred URL as the keyword argument ``success_url``.
-    
-    By default, ``registration.forms.RegistrationForm`` will be used
-    as the registration form; to change this, pass a different form
-    class as the ``form_class`` keyword argument. The form class you
-    specify must have a method ``save`` which will create and return
-    the new ``User``, and that method must accept the keyword argument
-    ``profile_callback`` (see below).
-    
-    To enable creation of a site-specific user profile object for the
-    new user, pass a function which will create the profile object as
-    the keyword argument ``profile_callback``. See
-    ``RegistrationManager.create_inactive_user`` in the file
-    ``models.py`` for details on how to write this function.
-    
-    By default, use the template
-    ``registration/registration_form.html``; to change this, pass the
-    name of a template as the keyword argument ``template_name``.
+
+    The actual registration of the account will be delegated to the
+    backend specified by the ``backend`` keyword argument (see below);
+    it will be used as follows:
+
+    1. The backend's ``registration_allowed()`` method will be called,
+       passing the ``HttpRequest``, to determine whether registration
+       of an account is to be allowed; if not, a redirect is issued to
+       the view corresponding to the named URL pattern
+       ``registration_disallowed``. To override this, see the list of
+       optional arguments for this view (below).
+
+    2. The form to use for account registration will be obtained by
+       calling the backend's ``get_form_class()`` method, passing the
+       ``HttpRequest``. To override this, see the list of optional
+       arguments for this view (below).
+
+    3. If valid, the form's ``cleaned_data`` will be passed (as
+       keyword arguments, and along with the ``HttpRequest``) to the
+       backend's ``register()`` method, which should return the new
+       ``User`` object.
+
+    4. Upon successful registration, the backend's
+       ``post_registration_redirect()`` method will be called, passing
+       the ``HttpRequest`` and the new ``User``, to determine the URL
+       to redirect the user to. To override this, see the list of
+       optional arguments for this view (below).
     
     **Required arguments**
     
     None.
     
     **Optional arguments**
+
+    ``backend``
+        The dotted Python import path to the backend class to use.
+
+    ``disallowed_url``
+        URL to redirect to if registration is not permitted for the
+        current ``HttpRequest``. Must be a value which can legally be
+        passed to ``django.shortcuts.redirect``. If not supplied, this
+        will be whatever URL corresponds to the named URL pattern
+        ``registration_disallowed``.
     
     ``form_class``
-        The form class to use for registration.
+        The form class to use for registration. If not supplied, this
+        will be retrieved from the registration backend.
     
     ``extra_context``
         A dictionary of variables to add to the template context. Any
         callable object in this dictionary will be called to produce
         the end result which appears in the context.
-    
-    ``profile_callback``
-        A function which will be used to create a site-specific
-        profile instance for the new ``User``.
-    
+
     ``success_url``
-        The URL to redirect to on successful registration.
+        URL to redirect to after successful registration. Must be a
+        value which can legally be passed to
+        ``django.shortcuts.redirect``. If not supplied, this will be
+        retrieved from the registration backend.
     
     ``template_name``
-        A custom template to use.
+        A custom template to use. If not supplied, this will default
+        to ``registration/registration_form.html``.
     
     **Context:**
     
@@ -142,15 +175,21 @@
     argument.
     
     """
+    backend = get_backend(backend)
+    if not backend.registration_allowed(request):
+        return redirect(disallowed_url)
+    if form_class is None:
+        form_class = backend.get_form_class(request)
+
     if request.method == 'POST':
         form = form_class(data=request.POST, files=request.FILES)
         if form.is_valid():
-            new_user = form.save(profile_callback=profile_callback)
-            # success_url needs to be dynamically generated here; setting a
-            # a default value using reverse() will cause circular-import
-            # problems with the default URLConf for this application, which
-            # imports this file.
-            return HttpResponseRedirect(success_url or reverse('registration_complete'))
+            new_user = backend.register(request, **form.cleaned_data)
+            if success_url is None:
+                to, args, kwargs = backend.post_registration_redirect(request, new_user)
+                return redirect(to, *args, **kwargs)
+            else:
+                return redirect(success_url)
     else:
         form = form_class()
     
@@ -159,6 +198,7 @@
     context = RequestContext(request)
     for key, value in extra_context.items():
         context[key] = callable(value) and value() or value
+
     return render_to_response(template_name,
-                              { 'form': form },
+                              {'form': form},
                               context_instance=context)
diff -Nru python-django-registration-0.7/setup.py python-django-registration-0.8/setup.py
--- python-django-registration-0.7/setup.py	2008-11-06 10:23:48.000000000 +0100
+++ python-django-registration-0.8/setup.py	2012-03-24 19:37:30.000000000 +0100
@@ -1,6 +1,9 @@
 from distutils.core import setup
 import os
 
+from registration import get_version
+
+
 # Compile the list of packages available, because distutils doesn't have
 # an easy way to do this.
 packages, data_files = [], []
@@ -24,21 +27,22 @@
 
 
 setup(name='django-registration',
-      version='0.7',
+      version=get_version().replace(' ', '-'),
       description='An extensible user-registration application for Django',
       author='James Bennett',
       author_email='james@b-list.org',
       url='http://www.bitbucket.org/ubernostrum/django-registration/wiki/',
-      download_url='http://www.bitbucket.org/ubernostrum/django-registration/get/v0.7.gz',
+      download_url='http://www.bitbucket.org/ubernostrum/django-registration/get/v0.8.gz',
       package_dir={'registration': 'registration'},
       packages=packages,
       package_data={'registration': data_files},
-      classifiers=['Development Status :: 5 - Production/Stable',
+      classifiers=['Development Status :: 4 - Beta',
                    'Environment :: Web Environment',
                    'Framework :: Django',
                    'Intended Audience :: Developers',
                    'License :: OSI Approved :: BSD License',
                    'Operating System :: OS Independent',
                    'Programming Language :: Python',
+                   'Topic :: Software Development :: Libraries :: Python Modules',
                    'Topic :: Utilities'],
       )
diff -Nru mumble-django-2.7/debian/changelog mumble-django-2.7/debian/changelog
--- mumble-django-2.7/debian/changelog	2012-07-09 20:35:24.000000000 +0200
+++ mumble-django-2.7/debian/changelog	2012-08-25 16:38:33.000000000 +0200
@@ -1,3 +1,11 @@
+mumble-django (2.7-6) unstable; urgency=low
+
+  * Add 03-templates.diff that adds "activation_complete.html" and removes an
+    obsolete check from "registration_complete.html".
+    Closes: 685141
+
+ -- Michael Ziegler <diese-addy@funzt-halt.net>  Fri, 24 Aug 2012 19:32:22 +0200
+
 mumble-django (2.7-5) unstable; urgency=low
 
   * Add Danish debconf translation (Closes: 680373).
diff -Nru mumble-django-2.7/debian/patches/03-templates.diff mumble-django-2.7/debian/patches/03-templates.diff
--- mumble-django-2.7/debian/patches/03-templates.diff	1970-01-01 01:00:00.000000000 +0100
+++ mumble-django-2.7/debian/patches/03-templates.diff	2012-08-24 19:28:11.000000000 +0200
@@ -0,0 +1,29 @@
+Index: mumble-django-2.7/pyweb/templates/registration/activation_complete.html
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ mumble-django-2.7/pyweb/templates/registration/activation_complete.html	2012-08-24 19:27:44.488360762 +0200
+@@ -0,0 +1,10 @@
++{% extends "index.html" %}
++{% load i18n %}
++
++{% block Headline %}Activation complete{% endblock %}
++
++{% block Content %}
++<p>Thank you for activating your account.
++</p>
++{% endblock %}
++
+Index: mumble-django-2.7/pyweb/templates/registration/registration_complete.html
+===================================================================
+--- mumble-django-2.7.orig/pyweb/templates/registration/registration_complete.html	2012-03-31 20:21:58.000000000 +0200
++++ mumble-django-2.7/pyweb/templates/registration/registration_complete.html	2012-08-24 19:28:01.400601432 +0200
+@@ -5,9 +5,7 @@
+ 
+ {% block Content %}
+ <p>Thank you for registering.
+-{% if verify %}
+ In order to prohibit misuse, you need to activate your account before you can use it. We sent an EMail with further instructions to the address you specified, please follow the link in that email to activate your account.
+-{% endif %}
+ </p>
+ {% endblock %}
+ 
diff -Nru mumble-django-2.7/debian/patches/series mumble-django-2.7/debian/patches/series
--- mumble-django-2.7/debian/patches/series	2010-06-21 18:56:59.000000000 +0200
+++ mumble-django-2.7/debian/patches/series	2012-08-24 19:26:59.000000000 +0200
@@ -1,2 +1,3 @@
 01-settings.diff
 02-munin.diff
+03-templates.diff

--- End Message ---
--- Begin Message ---
On Wed, 2012-08-29 at 21:46 +0200, Raphaël Hertzog wrote:
> Please unblock package python-django-registration
> 
> The version in wheezy is not really compatible with Django 1.4 (RC
> bug #686104) and upstream released version 0.8 which adresses the
> compatibility issue but which is also not backwards compatible.

:-(

[...]
> I have not investigated whether it would have been possible to fix
> 0.7 to be 100% compatible with python-django 1.4 and I really believe
> that such an effort is not worth it. People just expect to have
> django-registration 0.8 available. It's entirely my fault if I have
> missed the freeze deadline, and I can understand that you're not happy
> with this late request.

Not hugely, no.  However, the filtered changes aren't too crazy, so
I've:

> unblock python-django-registration/0.8-1
> unblock mumble-django/2.7-6

Regards,

Adam

--- End Message ---

Reply to: