summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsubstantialnoninfringinguser <substantialnoninfringinguser@gmail.com>2009-05-13 05:48:10 +0100
committersubstantialnoninfringinguser <substantialnoninfringinguser@gmail.com>2009-05-13 05:48:10 +0100
commited345f25760d8927f834a69202c2b9b2cef71ee0 (patch)
tree652dba01640060cc2106af850955728828bcd8f0
parentcdd4e685ee95e44b9a599b03cf11723a4ce7b7c6 (diff)
downloadtroggle-ed345f25760d8927f834a69202c2b9b2cef71ee0.tar.gz
troggle-ed345f25760d8927f834a69202c2b9b2cef71ee0.tar.bz2
troggle-ed345f25760d8927f834a69202c2b9b2cef71ee0.zip
[svn] Add user registration and user profiles.
Used modified versions of django-registration and django-profiles , both on bitbucket. The Person model is now set up as the profile for auth.User s. I set up a requestcontext so that settings is automatically passed to every template, no need to repeat ourselves in views. However, this needs to be refined: I will soon change it to only pass a subset of settings. E.G. we do not need to be passing the DB login and password! Copied from http://cucc@cucc.survex.com/svn/trunk/expoweb/troggle/, rev. 8231 by aaron @ 1/29/2009 11:02 PM
-rw-r--r--context.py4
-rw-r--r--expo/admin.py5
-rw-r--r--expo/forms.py8
-rw-r--r--expo/models.py4
-rw-r--r--expo/views_logbooks.py6
-rw-r--r--media/css/main2.css2
-rw-r--r--profiles/__init__.py0
-rw-r--r--profiles/urls.py43
-rw-r--r--profiles/utils.py45
-rw-r--r--profiles/views.py340
-rw-r--r--registration/__init__.py0
-rw-r--r--registration/admin.py11
-rw-r--r--registration/forms.py134
-rw-r--r--registration/locale/ar/LC_MESSAGES/django.mobin0 -> 2135 bytes
-rw-r--r--registration/locale/ar/LC_MESSAGES/django.po81
-rw-r--r--registration/locale/bg/LC_MESSAGES/django.mobin0 -> 2302 bytes
-rw-r--r--registration/locale/bg/LC_MESSAGES/django.po78
-rw-r--r--registration/locale/de/LC_MESSAGES/django.mobin0 -> 1909 bytes
-rw-r--r--registration/locale/de/LC_MESSAGES/django.po85
-rw-r--r--registration/locale/el/LC_MESSAGES/django.mobin0 -> 2424 bytes
-rw-r--r--registration/locale/el/LC_MESSAGES/django.po84
-rw-r--r--registration/locale/en/LC_MESSAGES/django.mobin0 -> 367 bytes
-rw-r--r--registration/locale/en/LC_MESSAGES/django.po81
-rw-r--r--registration/locale/es/LC_MESSAGES/django.mobin0 -> 1909 bytes
-rw-r--r--registration/locale/es/LC_MESSAGES/django.po85
-rw-r--r--registration/locale/es_AR/LC_MESSAGES/django.mobin0 -> 1849 bytes
-rw-r--r--registration/locale/es_AR/LC_MESSAGES/django.po83
-rw-r--r--registration/locale/fr/LC_MESSAGES/django.mobin0 -> 1883 bytes
-rw-r--r--registration/locale/fr/LC_MESSAGES/django.po81
-rw-r--r--registration/locale/he/LC_MESSAGES/django.mobin0 -> 1896 bytes
-rw-r--r--registration/locale/he/LC_MESSAGES/django.po86
-rw-r--r--registration/locale/it/LC_MESSAGES/django.mobin0 -> 1864 bytes
-rw-r--r--registration/locale/it/LC_MESSAGES/django.po82
-rw-r--r--registration/locale/ja/LC_MESSAGES/django.mobin0 -> 2035 bytes
-rw-r--r--registration/locale/ja/LC_MESSAGES/django.po78
-rw-r--r--registration/locale/nl/LC_MESSAGES/django.mobin0 -> 1898 bytes
-rw-r--r--registration/locale/nl/LC_MESSAGES/django.po77
-rw-r--r--registration/locale/pl/LC_MESSAGES/django.mobin0 -> 1769 bytes
-rw-r--r--registration/locale/pl/LC_MESSAGES/django.po84
-rw-r--r--registration/locale/pt_BR/LC_MESSAGES/django.mobin0 -> 1796 bytes
-rw-r--r--registration/locale/pt_BR/LC_MESSAGES/django.po81
-rw-r--r--registration/locale/ru/LC_MESSAGES/django.mobin0 -> 2360 bytes
-rw-r--r--registration/locale/ru/LC_MESSAGES/django.po81
-rw-r--r--registration/locale/sr/LC_MESSAGES/django.mobin0 -> 1966 bytes
-rw-r--r--registration/locale/sr/LC_MESSAGES/django.po80
-rw-r--r--registration/locale/sv/LC_MESSAGES/django.mobin0 -> 1687 bytes
-rw-r--r--registration/locale/sv/LC_MESSAGES/django.po81
-rw-r--r--registration/locale/zh_CN/LC_MESSAGES/django.mobin0 -> 1669 bytes
-rw-r--r--registration/locale/zh_CN/LC_MESSAGES/django.po77
-rw-r--r--registration/locale/zh_TW/LC_MESSAGES/django.mobin0 -> 1669 bytes
-rw-r--r--registration/locale/zh_TW/LC_MESSAGES/django.po77
-rw-r--r--registration/management/__init__.py0
-rw-r--r--registration/management/commands/__init__.py0
-rw-r--r--registration/management/commands/cleanupregistration.py19
-rw-r--r--registration/models.py255
-rw-r--r--registration/signals.py8
-rw-r--r--registration/tests.py355
-rw-r--r--registration/urls.py72
-rw-r--r--registration/views.py153
-rw-r--r--settings.py10
-rw-r--r--templates/base.html29
-rw-r--r--templates/personForm.html6
-rw-r--r--templates/profiles/create_profile.html13
-rw-r--r--templates/profiles/profile_list.html0
-rw-r--r--templates/registration/activate.html13
-rw-r--r--templates/registration/activation_email.txt3
-rw-r--r--templates/registration/activation_email_subject.txt1
-rw-r--r--templates/registration/login.html19
-rw-r--r--templates/registration/logout.html4
-rw-r--r--templates/registration/registration_activate.html6
-rw-r--r--templates/registration/registration_complete.html13
-rw-r--r--templates/registration/registration_form.html56
-rw-r--r--urls.py5
73 files changed, 3173 insertions, 11 deletions
diff --git a/context.py b/context.py
new file mode 100644
index 0000000..f0fd208
--- /dev/null
+++ b/context.py
@@ -0,0 +1,4 @@
+import troggle.settings as settings
+
+def settingsContext(request):
+ return { 'settings':settings } \ No newline at end of file
diff --git a/expo/admin.py b/expo/admin.py
index 52138af..d5a7ddd 100644
--- a/expo/admin.py
+++ b/expo/admin.py
@@ -18,8 +18,13 @@ class SurveyAdmin(admin.ModelAdmin):
class LogbookEntryAdmin(admin.ModelAdmin):
search_fields = ('title','expedition__year')
+class PersonExpeditionInline(admin.TabularInline):
+ model = PersonExpedition
+ extra = 1
+
class PersonAdmin(admin.ModelAdmin):
search_fields = ('first_name','last_name')
+ inlines = (PersonExpeditionInline,)
class PersonExpeditionAdmin(admin.ModelAdmin):
search_fields = ('person__first_name','expedition__year')
diff --git a/expo/forms.py b/expo/forms.py
index c8e2842..91f39f0 100644
--- a/expo/forms.py
+++ b/expo/forms.py
@@ -1,6 +1,10 @@
from django.forms import ModelForm
-from models import Cave
+from models import Cave, Person
class CaveForm(ModelForm):
class Meta:
- model = Cave \ No newline at end of file
+ model = Cave
+
+class PersonForm(ModelForm):
+ class Meta:
+ model = Person \ No newline at end of file
diff --git a/expo/models.py b/expo/models.py
index 2b3919c..98b5d2b 100644
--- a/expo/models.py
+++ b/expo/models.py
@@ -4,6 +4,7 @@ from django.forms import ModelForm
from django.db import models
from django.contrib import admin
from django.core.files.storage import FileSystemStorage
+from django.contrib.auth.models import User
import os
import troggle.settings as settings
import datetime
@@ -59,6 +60,7 @@ class Person(models.Model):
mug_shot = models.CharField(max_length=100, blank=True,null=True)
blurb = models.TextField(blank=True,null=True)
href = models.CharField(max_length=200)
+ user = models.ForeignKey(User, unique=True, null=True, blank=True)
class Meta:
verbose_name_plural = "People"
@@ -482,4 +484,4 @@ class Survey(models.Model):
def elevations(self):
return self.scannedimage_set.filter(contents='elevation')
- \ No newline at end of file
+
diff --git a/expo/views_logbooks.py b/expo/views_logbooks.py
index 72c44e8..513701b 100644
--- a/expo/views_logbooks.py
+++ b/expo/views_logbooks.py
@@ -4,6 +4,7 @@ import troggle.settings as settings
from troggle.parsers.logbooks import LoadLogbookForExpedition
from troggle.parsers.people import GetPersonExpeditionNameLookup
+from troggle.expo.forms import PersonForm
import search
import re
@@ -58,3 +59,8 @@ def logbookSearch(request, extra):
return render_to_response('logbooksearch.html',
{ 'query_string': query_string, 'found_entries': found_entries, 'settings': settings})
#context_instance=RequestContext(request))
+
+def personForm(request,pk):
+ person=Person.objects.get(pk=pk)
+ form=PersonForm(instance=person)
+ return render_to_response('personform.html', {'form':form,'settings':settings}) \ No newline at end of file
diff --git a/media/css/main2.css b/media/css/main2.css
index c7c7276..fee993c 100644
--- a/media/css/main2.css
+++ b/media/css/main2.css
@@ -62,6 +62,8 @@ div#footer
margin-right:auto;
}
+#frontPageBanner{ position:relative; width:inherit; height:inherit; }
+
div.logbookentry
{
text-align:left;
diff --git a/profiles/__init__.py b/profiles/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/profiles/__init__.py
diff --git a/profiles/urls.py b/profiles/urls.py
new file mode 100644
index 0000000..05ff9e4
--- /dev/null
+++ b/profiles/urls.py
@@ -0,0 +1,43 @@
+"""
+URLConf for Django user profile management.
+
+Recommended usage is to use a call to ``include()`` in your project's
+root URLConf to include this URLConf for any URL beginning with
+'/profiles/'.
+
+If the default behavior of the profile views is acceptable to you,
+simply use a line like this in your root URLConf to set up the default
+URLs for profiles::
+
+ (r'^profiles/', include('profiles.urls')),
+
+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 keep the name ``profiles_profile_detail`` for the pattern
+which points to the ``profile_detail`` view, since several views use
+``reverse()`` with that name to generate a default post-submission
+redirect. If you don't use that name, remember to explicitly pass
+``success_url`` to those views.
+
+"""
+
+from django.conf.urls.defaults import *
+
+from profiles import views
+
+
+urlpatterns = patterns('',
+ url(r'^create/$',
+ views.create_profile,
+ name='profiles_create_profile'),
+ url(r'^edit/$',
+ views.edit_profile,
+ name='profiles_edit_profile'),
+ url(r'^(?P<username>\w+)/$',
+ views.profile_detail,
+ name='profiles_profile_detail'),
+ url(r'^$',
+ views.profile_list,
+ name='profiles_profile_list'),
+ )
diff --git a/profiles/utils.py b/profiles/utils.py
new file mode 100644
index 0000000..faacfcb
--- /dev/null
+++ b/profiles/utils.py
@@ -0,0 +1,45 @@
+"""
+Utility functions for retrieving and generating forms for the
+site-specific user profile model specified in the
+``AUTH_PROFILE_MODULE`` setting.
+
+"""
+
+from django import forms
+from django.conf import settings
+from django.contrib.auth.models import SiteProfileNotAvailable
+from django.db.models import get_model
+
+
+def get_profile_model():
+ """
+ Return the model class for the currently-active user profile
+ model, as defined by the ``AUTH_PROFILE_MODULE`` setting. If that
+ setting is missing, raise
+ ``django.contrib.auth.models.SiteProfileNotAvailable``.
+
+ """
+ if (not hasattr(settings, 'AUTH_PROFILE_MODULE')) or \
+ (not settings.AUTH_PROFILE_MODULE):
+ raise SiteProfileNotAvailable
+ profile_mod = get_model(*settings.AUTH_PROFILE_MODULE.split('.'))
+ if profile_mod is None:
+ raise SiteProfileNotAvailable
+ return profile_mod
+
+
+def get_profile_form():
+ """
+ Return a form class (a subclass of the default ``ModelForm``)
+ suitable for creating/editing instances of the site-specific user
+ profile model, as defined by the ``AUTH_PROFILE_MODULE``
+ setting. If that setting is missing, raise
+ ``django.contrib.auth.models.SiteProfileNotAvailable``.
+
+ """
+ profile_mod = get_profile_model()
+ class _ProfileForm(forms.ModelForm):
+ class Meta:
+ model = profile_mod
+ exclude = ('user',) # User will be filled in by the view.
+ return _ProfileForm
diff --git a/profiles/views.py b/profiles/views.py
new file mode 100644
index 0000000..c119165
--- /dev/null
+++ b/profiles/views.py
@@ -0,0 +1,340 @@
+"""
+Views for creating, editing and viewing site-specific user profiles.
+
+"""
+
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth.models import User
+from django.core.exceptions import ObjectDoesNotExist
+from django.core.urlresolvers import reverse
+from django.http import Http404
+from django.http import HttpResponseRedirect
+from django.shortcuts import get_object_or_404
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.views.generic.list_detail import object_list
+
+from profiles import utils
+
+import troggle.settings as settings
+
+
+
+def create_profile(request, form_class=None, success_url=None,
+ template_name='profiles/create_profile.html',
+ extra_context=None):
+ """
+ Create a profile for the current user, if one doesn't already
+ exist.
+
+ If the user already has a profile, as determined by
+ ``request.user.get_profile()``, a redirect will be issued to the
+ :view:`profiles.views.edit_profile` view. If no profile model has
+ been specified in the ``AUTH_PROFILE_MODULE`` setting,
+ ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
+ raised.
+
+ **Optional arguments:**
+
+ ``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.
+
+ ``form_class``
+ The form class to use for validating and creating the user
+ profile. This form class must define a method named
+ ``save()``, implementing the same argument signature as the
+ ``save()`` method of a standard Django ``ModelForm`` (this
+ view will call ``save(commit=False)`` to obtain the profile
+ object, and fill in the user before the final save). If the
+ profile object includes many-to-many relations, the convention
+ established by ``ModelForm`` of using a method named
+ ``save_m2m()`` will be used, and so your form class should
+ also define this method.
+
+ If this argument is not supplied, this view will use a
+ ``ModelForm`` automatically generated from the model specified
+ by ``AUTH_PROFILE_MODULE``.
+
+ ``success_url``
+ The URL to redirect to after successful profile creation. If
+ this argument is not supplied, this will default to the URL of
+ :view:`profiles.views.profile_detail` for the newly-created
+ profile object.
+
+ ``template_name``
+ The template to use when displaying the profile-creation
+ form. If not supplied, this will default to
+ :template:`profiles/create_profile.html`.
+
+ **Context:**
+
+ ``form``
+ The profile-creation form.
+
+ **Template:**
+
+ ``template_name`` keyword argument, or
+ :template:`profiles/create_profile.html`.
+
+ """
+ try:
+ profile_obj = request.user.get_profile()
+ return HttpResponseRedirect(reverse('profiles_edit_profile'))
+ except ObjectDoesNotExist:
+ pass
+
+ #
+ # We set up success_url here, rather than as the default value for
+ # the argument. Trying to do it as the argument's default would
+ # mean evaluating the call to reverse() at the time this module is
+ # first imported, which introduces a circular dependency: to
+ # perform the reverse lookup we need access to profiles/urls.py,
+ # but profiles/urls.py in turn imports this module.
+ #
+
+ if success_url is None:
+ success_url = reverse('profiles_profile_detail',
+ kwargs={ 'username': request.user.username })
+ if form_class is None:
+ form_class = utils.get_profile_form()
+ if request.method == 'POST':
+ form = form_class(data=request.POST, files=request.FILES)
+ if form.is_valid():
+ profile_obj = form.save(commit=False)
+ profile_obj.user = request.user
+ profile_obj.save()
+ if hasattr(form, 'save_m2m'):
+ form.save_m2m()
+ return HttpResponseRedirect(success_url)
+ else:
+ form = form_class()
+
+ 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,
+ { 'form': form, 'settings':settings },
+ context_instance=context)
+create_profile = login_required(create_profile)
+
+def edit_profile(request, form_class=None, success_url=None,
+ template_name='profiles/edit_profile.html',
+ extra_context=None):
+ """
+ Edit the current user's profile.
+
+ If the user does not already have a profile (as determined by
+ ``User.get_profile()``), a redirect will be issued to the
+ :view:`profiles.views.create_profile` view; if no profile model
+ has been specified in the ``AUTH_PROFILE_MODULE`` setting,
+ ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
+ raised.
+
+ **Optional arguments:**
+
+ ``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.
+
+ ``form_class``
+ The form class to use for validating and editing the user
+ profile. This form class must operate similarly to a standard
+ Django ``ModelForm`` in that it must accept an instance of the
+ object to be edited as the keyword argument ``instance`` to
+ its constructor, and it must implement a method named
+ ``save()`` which will save the updates to the object. If this
+ argument is not specified, this view will use a ``ModelForm``
+ generated from the model specified in the
+ ``AUTH_PROFILE_MODULE`` setting.
+
+ ``success_url``
+ The URL to redirect to following a successful edit. If not
+ specified, this will default to the URL of
+ :view:`profiles.views.profile_detail` for the profile object
+ being edited.
+
+ ``template_name``
+ The template to use when displaying the profile-editing
+ form. If not specified, this will default to
+ :template:`profiles/edit_profile.html`.
+
+ **Context:**
+
+ ``form``
+ The form for editing the profile.
+
+ ``profile``
+ The user's current profile.
+
+ **Template:**
+
+ ``template_name`` keyword argument or
+ :template:`profiles/edit_profile.html`.
+
+ """
+ try:
+ profile_obj = request.user.get_profile()
+ except ObjectDoesNotExist:
+ return HttpResponseRedirect(reverse('profiles_create_profile'))
+
+ #
+ # See the comment in create_profile() for discussion of why
+ # success_url is set up here, rather than as a default value for
+ # the argument.
+ #
+
+ if success_url is None:
+ success_url = reverse('profiles_profile_detail',
+ kwargs={ 'username': request.user.username })
+ if form_class is None:
+ form_class = utils.get_profile_form()
+ if request.method == 'POST':
+ form = form_class(data=request.POST, files=request.FILES, instance=profile_obj)
+ if form.is_valid():
+ form.save()
+ return HttpResponseRedirect(success_url)
+ else:
+ form = form_class(instance=profile_obj)
+
+ 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,
+ { 'form': form,
+ 'profile': profile_obj, },
+ context_instance=context)
+edit_profile = login_required(edit_profile)
+
+def profile_detail(request, username, public_profile_field=None,
+ template_name='profiles/profile_detail.html',
+ extra_context=None):
+ """
+ Detail view of a user's profile.
+
+ If no profile model has been specified in the
+ ``AUTH_PROFILE_MODULE`` setting,
+ ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
+ raised.
+
+ If the user has not yet created a profile, ``Http404`` will be
+ raised.
+
+ **Required arguments:**
+
+ ``username``
+ The username of the user whose profile is being displayed.
+
+ **Optional arguments:**
+
+ ``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.
+
+ ``public_profile_field``
+ The name of a ``BooleanField`` on the profile model; if the
+ value of that field on the user's profile is ``False``, the
+ ``profile`` variable in the template will be ``None``. Use
+ this feature to allow users to mark their profiles as not
+ being publicly viewable.
+
+ If this argument is not specified, it will be assumed that all
+ users' profiles are publicly viewable.
+
+ ``template_name``
+ The name of the template to use for displaying the profile. If
+ not specified, this will default to
+ :template:`profiles/profile_detail.html`.
+
+ **Context:**
+
+ ``profile``
+ The user's profile, or ``None`` if the user's profile is not
+ publicly viewable (see the description of
+ ``public_profile_field`` above).
+
+ **Template:**
+
+ ``template_name`` keyword argument or
+ :template:`profiles/profile_detail.html`.
+
+ """
+ user = get_object_or_404(User, username=username)
+ try:
+ profile_obj = user.get_profile()
+ except ObjectDoesNotExist:
+ raise Http404
+ if public_profile_field is not None and \
+ not getattr(profile_obj, public_profile_field):
+ profile_obj = None
+
+ 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,
+ { 'profile': profile_obj },
+ context_instance=context)
+
+def profile_list(request, public_profile_field=None,
+ template_name='profiles/profile_list.html', **kwargs):
+ """
+ A list of user profiles.
+
+ If no profile model has been specified in the
+ ``AUTH_PROFILE_MODULE`` setting,
+ ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
+ raised.
+
+ **Optional arguments:**
+
+ ``public_profile_field``
+ The name of a ``BooleanField`` on the profile model; if the
+ value of that field on a user's profile is ``False``, that
+ profile will be excluded from the list. Use this feature to
+ allow users to mark their profiles as not being publicly
+ viewable.
+
+ If this argument is not specified, it will be assumed that all
+ users' profiles are publicly viewable.
+
+ ``template_name``
+ The name of the template to use for displaying the profiles. If
+ not specified, this will default to
+ :template:`profiles/profile_list.html`.
+
+ Additionally, all arguments accepted by the
+ :view:`django.views.generic.list_detail.object_list` generic view
+ will be accepted here, and applied in the same fashion, with one
+ exception: ``queryset`` will always be the ``QuerySet`` of the
+ model specified by the ``AUTH_PROFILE_MODULE`` setting, optionally
+ filtered to remove non-publicly-viewable proiles.
+
+ **Context:**
+
+ Same as the :view:`django.views.generic.list_detail.object_list`
+ generic view.
+
+ **Template:**
+
+ ``template_name`` keyword argument or
+ :template:`profiles/profile_list.html`.
+
+ """
+ profile_model = utils.get_profile_model()
+ queryset = profile_model._default_manager.all()
+ if public_profile_field is not None:
+ queryset = queryset.filter(**{ public_profile_field: True })
+ kwargs['queryset'] = queryset
+ return object_list(request, template_name=template_name, **kwargs)
diff --git a/registration/__init__.py b/registration/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/registration/__init__.py
diff --git a/registration/admin.py b/registration/admin.py
new file mode 100644
index 0000000..3f36c18
--- /dev/null
+++ b/registration/admin.py
@@ -0,0 +1,11 @@
+from django.contrib import admin
+
+from registration.models import RegistrationProfile
+
+
+class RegistrationAdmin(admin.ModelAdmin):
+ list_display = ('__unicode__', 'activation_key_expired')
+ search_fields = ('user__username', 'user__first_name')
+
+
+admin.site.register(RegistrationProfile, RegistrationAdmin)
diff --git a/registration/forms.py b/registration/forms.py
new file mode 100644
index 0000000..f2298ac
--- /dev/null
+++ b/registration/forms.py
@@ -0,0 +1,134 @@
+"""
+Forms and validation code for user registration.
+
+"""
+
+
+from django.contrib.auth.models import User
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+
+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' }
+
+
+class RegistrationForm(forms.Form):
+ """
+ Form for registering a new user account.
+
+ Validates that the requested username is not already in use, and
+ requires the password to be entered twice to catch typos.
+
+ Subclasses should feel free to add any additional validation they
+ need, but should either preserve the base ``save()`` or implement
+ a ``save()`` method which returns a ``User``.
+
+ """
+ username = forms.RegexField(regex=r'^\w+$',
+ max_length=30,
+ widget=forms.TextInput(attrs=attrs_dict),
+ label=_(u'username'))
+ email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
+ maxlength=75)),
+ label=_(u'email address'))
+ password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
+ label=_(u'password'))
+ password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
+ label=_(u'password (again)'))
+
+ def clean_username(self):
+ """
+ Validate that the username is alphanumeric and is not already
+ in use.
+
+ """
+ try:
+ user = User.objects.get(username__iexact=self.cleaned_data['username'])
+ except User.DoesNotExist:
+ return self.cleaned_data['username']
+ raise forms.ValidationError(_(u'This username is already taken. Please choose another.'))
+
+ 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(_(u'You must type the same password each time'))
+ return self.cleaned_data
+
+ def save(self):
+ """
+ Create the new ``User`` and ``RegistrationProfile``, and
+ returns the ``User`` (by calling
+ ``RegistrationProfile.objects.create_inactive_user()``).
+
+ """
+ new_user = RegistrationProfile.objects.create_inactive_user(username=self.cleaned_data['username'],
+ password=self.cleaned_data['password1'],
+ email=self.cleaned_data['email'])
+ return new_user
+
+
+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': u"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(_(u'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']
+
+ def clean_email(self):
+ """
+ Check the supplied email address against a list of known free
+ webmail domains.
+
+ """
+ email_domain = self.cleaned_data['email'].split('@')[1]
+ if email_domain in self.bad_domains:
+ raise forms.ValidationError(_(u'Registration using free email addresses is prohibited. Please supply a different email address.'))
+ return self.cleaned_data['email']
diff --git a/registration/locale/ar/LC_MESSAGES/django.mo b/registration/locale/ar/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..07bc79f
--- /dev/null
+++ b/registration/locale/ar/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/ar/LC_MESSAGES/django.po b/registration/locale/ar/LC_MESSAGES/django.po
new file mode 100644
index 0000000..dd61869
--- /dev/null
+++ b/registration/locale/ar/LC_MESSAGES/django.po
@@ -0,0 +1,81 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "اسم المستخدم"
+
+#: forms.py:41
+msgid "email address"
+msgstr "عنوان البريد الالكتروني"
+
+#: forms.py:43
+msgid "password"
+msgstr "كلمة المرور"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "تأكيد كلمة المرور"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "يمكن أن يحتوي اسم المستخدم على احرف، ارقام وشرطات سطرية فقط"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "اسم المستخدم مسجل مسبقا. يرجى اختيار اسم اخر."
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "يجب ادخال كلمة المرور مطابقة كل مرة"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "أقر بقراءة والموافقة على شروط الخدمة"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "يجب الموافقة على الشروط للتسجيل"
+
+#: forms.py:124
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr "عنوان البريد الالكتروني مسجل مسبقا. يرجى تزويد عنوان بريد الكتروني مختلف."
+
+#: forms.py:149
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr "يمنع التسجيل باستخدام عناوين بريد الكترونية مجانية. يرجى تزويد عنوان بريد الكتروني مختلف."
+
+#: models.py:188
+msgid "user"
+msgstr "مستخدم"
+
+#: models.py:189
+msgid "activation key"
+msgstr "رمز التفعيل"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "ملف التسجيل الشخصي"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "ملفات التسجيل الشخصية"
diff --git a/registration/locale/bg/LC_MESSAGES/django.mo b/registration/locale/bg/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..be9adf4
--- /dev/null
+++ b/registration/locale/bg/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/bg/LC_MESSAGES/django.po b/registration/locale/bg/LC_MESSAGES/django.po
new file mode 100644
index 0000000..5089ec1
--- /dev/null
+++ b/registration/locale/bg/LC_MESSAGES/django.po
@@ -0,0 +1,78 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: 2008-03-05 12:37+0200\n"
+"Last-Translator: Vladislav <vladislav.mitov@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Bookmarks: -1,-1,-1,-1,10,-1,-1,-1,-1,-1\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "Потребителско име "
+
+#: forms.py:41
+msgid "email address"
+msgstr "Електронна поща"
+
+#: forms.py:43
+msgid "password"
+msgstr "Парола"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "Парола (проверка)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "Потребителските имена могат да съдържат букви, цифри и подчертавки"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Потребителското име е заето. Моля изберето друго."
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "Грешка при проверка на паролата."
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "Прочел съм и съм съгласен с условията за експлоатация"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "Трябва да сте съгласни с условията за да се регистрирате."
+
+#: forms.py:124
+msgid "This email address is already in use. Please supply a different email address."
+msgstr "Адреса на електронната поща е използван. Моля въведете друг адрес."
+
+#: forms.py:149
+msgid "Registration using free email addresses is prohibited. Please supply a different email address."
+msgstr "Регистрациите с безплатни адреси е забранен. Моля въведете различен адрес за електронна поща"
+
+#: models.py:188
+msgid "user"
+msgstr "Потребител"
+
+#: models.py:189
+msgid "activation key"
+msgstr "Ключ за активация"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "регистрационен профил"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "регистрационни профили"
+
diff --git a/registration/locale/de/LC_MESSAGES/django.mo b/registration/locale/de/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..b9bc1fa
--- /dev/null
+++ b/registration/locale/de/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/de/LC_MESSAGES/django.po b/registration/locale/de/LC_MESSAGES/django.po
new file mode 100644
index 0000000..96e00ee
--- /dev/null
+++ b/registration/locale/de/LC_MESSAGES/django.po
@@ -0,0 +1,85 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Jannis Leidel <jannis@leidel.info>, 2007.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: django-registration 0.3 \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: 2007-09-29 16:50+0200\n"
+"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
+"Language-Team: Deutsch <de@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "Benutzername"
+
+#: forms.py:41
+msgid "email address"
+msgstr "E-Mail-Adresse"
+
+#: forms.py:43
+msgid "password"
+msgstr "Passwort"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "Passwort (wiederholen)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "Benutzernamen können nur Buchstaben, Zahlen und Unterstriche enthalten"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Dieser Benutzername ist schon vergeben. Bitte einen anderen wählen."
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "Bitte das gleiche Passwort zur Überprüfung nochmal eingeben"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "Ich habe die Nutzungsvereinbarung gelesen und stimme ihr zu"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "Sie müssen der Nutzungsvereinbarung zustimmen, um sich zu registrieren"
+
+#: forms.py:124
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr ""
+"Diese E-Mail-Adresse wird schon genutzt. Bitte geben Sie eine andere "
+"E-Mail-Adresse an."
+
+#: forms.py:149
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr ""
+"Die Registrierung mit einer kostenlosen E-Mail-Adresse ist untersagt. Bitte "
+"geben Sie eine andere E-Mail-Adresse an."
+
+#: models.py:188
+msgid "user"
+msgstr "Benutzer"
+
+#: models.py:189
+msgid "activation key"
+msgstr "Aktivierungsschlüssel"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "Registrierungsprofil"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "Registrierungsprofile"
diff --git a/registration/locale/el/LC_MESSAGES/django.mo b/registration/locale/el/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..acc9726
--- /dev/null
+++ b/registration/locale/el/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/el/LC_MESSAGES/django.po b/registration/locale/el/LC_MESSAGES/django.po
new file mode 100644
index 0000000..cd38eb1
--- /dev/null
+++ b/registration/locale/el/LC_MESSAGES/django.po
@@ -0,0 +1,84 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Panos Laganakos <panos.laganakos@gmail.com>, 2007.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: 2007-11-14 21:50+0200\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "όνομα χρήστη"
+
+#: forms.py:41
+msgid "email address"
+msgstr "διεύθυνση ηλεκτρονικού ταχυδρομείου"
+
+#: forms.py:43
+msgid "password"
+msgstr "συνθηματικό"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "συνθηματικό (ξανά)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "Τα ονόματα χρηστών μπορούν να περιλαμβάνουν μόνο γράμματα, αριθμούς και υπογραμμίσεις"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Αυτό το όνομα χρήστη χρησιμοποίειται ήδη. Παρακαλώ διαλέξτε ένα άλλο."
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "Πρέπει να εισάγετε το ίδιο συνθηματικό κάθε φορά"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "Διάβασα και συμφωνώ με τους Όρους της Υπηρεσίας"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "Πρέπει να συμφωνείται με τους όρους για να εγγραφείτε"
+
+#: forms.py:124
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr ""
+"Η συγκεκριμένη διεύθυνση ηλεκτρονικού ταχυδρομείου χρησιμοποιείται ήδη. "
+"Παρακαλώ δώστε κάποια άλλη."
+
+#: forms.py:149
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr ""
+"Η εγγραφή μέσω δωρεάν διευθύνσεων ηλεκτρονικού ταχυδρομείου απαγορεύεται. ""Παρακαλώ δώστε κάποια άλλη."
+
+#: models.py:188
+msgid "user"
+msgstr "χρήστης"
+
+#: models.py:189
+msgid "activation key"
+msgstr "κλειδί ενεργοποίησης"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "προφίλ εγγραφής"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "προφίλ εγγραφών"
diff --git a/registration/locale/en/LC_MESSAGES/django.mo b/registration/locale/en/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..4728fe0
--- /dev/null
+++ b/registration/locale/en/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/en/LC_MESSAGES/django.po b/registration/locale/en/LC_MESSAGES/django.po
new file mode 100644
index 0000000..37bc9e2
--- /dev/null
+++ b/registration/locale/en/LC_MESSAGES/django.po
@@ -0,0 +1,81 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr ""
+
+#: forms.py:41
+msgid "email address"
+msgstr ""
+
+#: forms.py:43
+msgid "password"
+msgstr ""
+
+#: forms.py:45
+msgid "password (again)"
+msgstr ""
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr ""
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr ""
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr ""
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr ""
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr ""
+
+#: forms.py:124
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr ""
+
+#: forms.py:149
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr ""
+
+#: models.py:188
+msgid "user"
+msgstr ""
+
+#: models.py:189
+msgid "activation key"
+msgstr ""
+
+#: models.py:194
+msgid "registration profile"
+msgstr ""
+
+#: models.py:195
+msgid "registration profiles"
+msgstr ""
diff --git a/registration/locale/es/LC_MESSAGES/django.mo b/registration/locale/es/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..3872adf
--- /dev/null
+++ b/registration/locale/es/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/es/LC_MESSAGES/django.po b/registration/locale/es/LC_MESSAGES/django.po
new file mode 100644
index 0000000..ba0384f
--- /dev/null
+++ b/registration/locale/es/LC_MESSAGES/django.po
@@ -0,0 +1,85 @@
+# Spanish translation for django-registration.
+# Copyright (C) 2007, James Bennet
+# This file is distributed under the same license as the registration package.
+# Ernesto Rico Schmidt <e.rico.schmidt@gmail.com>, 2008.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: django-registration 0.3 \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-03-11 00:19-0400\n"
+"PO-Revision-Date: 2008-03-11 00:19-0400\n"
+"Last-Translator: Ernesto Rico Schmidt <e.rico.schmidt@gmail.com>\n"
+"Language-Team: Español <de@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "nombre de usuario"
+
+#: forms.py:41
+msgid "email address"
+msgstr "dirección de coreo electrónico"
+
+#: forms.py:43
+msgid "password"
+msgstr "contraseña"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "contraseña (otra vez)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "Los nombres de usuarios sólo pueden contener letras, números y guiones bajos"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Este nombre de usuario ya está ocupado. Por favor escoge otro"
+
+#: forms.py:71
+msgid "You must type the same password each time"
+msgstr "Tienes que introducir la misma contraseña cada vez"
+
+#: forms.py:100
+msgid "I have read and agree to the Terms of Service"
+msgstr "He leído y acepto los términos de servicio"
+
+#: forms.py:109
+msgid "You must agree to the terms to register"
+msgstr "Tienes que aceptar los términos para registrarte"
+
+#: forms.py:128
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr ""
+"La dirección de correo electrónico ya está siendo usada. Por favor"
+"proporciona otra dirección."
+
+#: forms.py:153
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr ""
+"El registro usando una dirección de correo electrónico gratis está prohibido."
+"Por favor proporciona otra dirección."
+
+#: models.py:188
+msgid "user"
+msgstr "usuario"
+
+#: models.py:189
+msgid "activation key"
+msgstr "clave de activación"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "perfil de registro"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "perfiles de registro"
diff --git a/registration/locale/es_AR/LC_MESSAGES/django.mo b/registration/locale/es_AR/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..ce8b4e5
--- /dev/null
+++ b/registration/locale/es_AR/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/es_AR/LC_MESSAGES/django.po b/registration/locale/es_AR/LC_MESSAGES/django.po
new file mode 100644
index 0000000..fb746b5
--- /dev/null
+++ b/registration/locale/es_AR/LC_MESSAGES/django.po
@@ -0,0 +1,83 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2008 Leonardo Manuel Rocha
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <l e o m a r o at g m a i l dot c o m>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "nombre de usuario"
+
+#: forms.py:41
+msgid "email address"
+msgstr "dirección de e-mail"
+
+#: forms.py:43
+msgid "password"
+msgstr "contraseña"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "contraseña (nuevamente)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "El nombre de usuario solo puede contener letras, números y guiones bajos"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Ese nombre de usuario ya está asignado. Por favor elija otro."
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "Debe tipear la misma contraseña cada vez"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "He leído y estoy de acuerdo con las Condiciones de Servicio"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "Debe estar de acuerdo con las Condiciones para poder registrarse"
+
+#: forms.py:124
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr "Esa dirección de e-mail ya está en uso. Por favor provea otra "
+"dirección."
+
+#: forms.py:149
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr "La registración con un e-mail gratuito está prohibida. Por favor "
+"de una dirección de e-mail diferente."
+
+#: models.py:188
+msgid "user"
+msgstr "usuario"
+
+#: models.py:189
+msgid "activation key"
+msgstr "clave de activación"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "perfil de registro"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "perfiles de registro"
diff --git a/registration/locale/fr/LC_MESSAGES/django.mo b/registration/locale/fr/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..f8911c7
--- /dev/null
+++ b/registration/locale/fr/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/fr/LC_MESSAGES/django.po b/registration/locale/fr/LC_MESSAGES/django.po
new file mode 100644
index 0000000..34b520b
--- /dev/null
+++ b/registration/locale/fr/LC_MESSAGES/django.po
@@ -0,0 +1,81 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Samuel Adam <samuel.adam@gmail.com>, 2007.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: django-registration 0.3 \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: 2007-09-20 10:30+0100\n"
+"Last-Translator: Samuel Adam <samuel.adam@gmail.com>\n"
+"Language-Team: Français <fr@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "pseudo"
+
+#: forms.py:41
+msgid "email address"
+msgstr "adresse email"
+
+#: forms.py:43
+msgid "password"
+msgstr "mot de passe"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "mot de passe (vérification)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "Le pseudo ne peut contenir que des lettres, chiffres et le caractère souligné."
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Ce pseudo est déjà utilisé. Veuillez en choisir un autre."
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "Veuillez indiquer le même mot de passe dans les deux champs"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "J'ai lu et accepté les Conditions Générales d'Utilisation"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "Vous devez accepter les conditions d'utilisation pour vous inscrire"
+
+#: forms.py:124
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr "Cette adresse email est déjà utilisée. Veuillez en indiquer une autre."
+
+#: forms.py:149
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr "L'inscription avec une adresse email d'un compte gratuit est interdite. Veuillez en indiquer une autre."
+
+#: models.py:188
+msgid "user"
+msgstr "utilisateur"
+
+#: models.py:189
+msgid "activation key"
+msgstr "clé d'activation"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "profil d'inscription"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "profils d'inscription"
diff --git a/registration/locale/he/LC_MESSAGES/django.mo b/registration/locale/he/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..be93650
--- /dev/null
+++ b/registration/locale/he/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/he/LC_MESSAGES/django.po b/registration/locale/he/LC_MESSAGES/django.po
new file mode 100644
index 0000000..5567e08
--- /dev/null
+++ b/registration/locale/he/LC_MESSAGES/django.po
@@ -0,0 +1,86 @@
+# translation of registration.
+# Copyright (C) 2008 THE registration'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the registration package.
+# <>, 2008.
+# , fuzzy
+# <>, 2008.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: registration\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-02-10 02:01+0200\n"
+"PO-Revision-Date: 2008-02-10 02:05+0200\n"
+"Last-Translator: Meir Kriheli <meir@mksoft.co.il>\n"
+"Language-Team: Hebrew\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit"
+
+#: forms.py:38
+msgid "username"
+msgstr "שם משתמש"
+
+#: forms.py:41
+msgid "email address"
+msgstr "דואר אלקטרוני"
+
+#: forms.py:43
+msgid "password"
+msgstr "סיסמה"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "סיסמה (שוב)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "שמות משתמש יכולים להכיל רק אותיות, ספרות וקווים תחתונים"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "שם המשתמש תפוס כבר. נא לבחור אחר."
+
+#: forms.py:64
+msgid "You must type the same password each time"
+msgstr "יש להקליד את אותה הסיסמה פעמיים"
+
+#: forms.py:93
+msgid "I have read and agree to the Terms of Service"
+msgstr "קראתי והסכמתי לתנאי השימוש"
+
+#: forms.py:102
+msgid "You must agree to the terms to register"
+msgstr "עליך להסכים לתנאי השימוש"
+
+#: forms.py:121
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr ""
+"כתובת הדואר האלקטרוני תפוסה כבר. נא לספק כתובת דואר אחרת."
+
+#: forms.py:146
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr ""
+"הרישום בעזרת תיבת דואר אלקטרוני חינמית אסור. נא לספק כתובת אחרת."
+
+#: models.py:188
+msgid "user"
+msgstr "משתמש"
+
+#: models.py:189
+msgid "activation key"
+msgstr "מפתח הפעלה"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "פרופיל רישום"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "פרופילי רישום"
+
diff --git a/registration/locale/it/LC_MESSAGES/django.mo b/registration/locale/it/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..83ec9dd
--- /dev/null
+++ b/registration/locale/it/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/it/LC_MESSAGES/django.po b/registration/locale/it/LC_MESSAGES/django.po
new file mode 100644
index 0000000..00129b0
--- /dev/null
+++ b/registration/locale/it/LC_MESSAGES/django.po
@@ -0,0 +1,82 @@
+# translation of django.po to Italiano
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Nicola Larosa <nico@tekNico.net>, 2008.
+msgid ""
+msgstr ""
+"Project-Id-Version: django\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: 2008-05-27 15:05+0200\n"
+"Last-Translator: Nicola Larosa <nico@tekNico.net>\n"
+"Language-Team: Italiano\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "nome utente"
+
+#: forms.py:41
+msgid "email address"
+msgstr "indirizzo email"
+
+#: forms.py:43
+msgid "password"
+msgstr "password"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "password (di nuovo)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "I nomi utente possono contenere solo lettere, numeri e sottolineature"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Questo nome utente è già usato. Scegline un altro."
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "Bisogna inserire la stessa password ogni volta"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "Dichiaro di aver letto e di approvare le Condizioni di Servizio"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "Per registrarsi bisogna approvare le condizioni"
+
+#: forms.py:124
+msgid "This email address is already in use. Please supply a different email "
+"address."
+msgstr "Questo indirizzo email è già in uso. Inserisci un altro indirizzo email."
+
+#: forms.py:149
+msgid "Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr "La registrazione con indirizzi email gratis non è permessa. "
+"Inserisci un altro indirizzo email."
+
+#: models.py:188
+msgid "user"
+msgstr "utente"
+
+#: models.py:189
+msgid "activation key"
+msgstr "chiave di attivazione"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "profilo di registrazione"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "profili di registrazione"
+
diff --git a/registration/locale/ja/LC_MESSAGES/django.mo b/registration/locale/ja/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..e0332b0
--- /dev/null
+++ b/registration/locale/ja/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/ja/LC_MESSAGES/django.po b/registration/locale/ja/LC_MESSAGES/django.po
new file mode 100644
index 0000000..afaaf94
--- /dev/null
+++ b/registration/locale/ja/LC_MESSAGES/django.po
@@ -0,0 +1,78 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Shinya Okano <xxshss@yahoo.co.jp>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: django-registration 0.4 \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: 2008-01-31 10:20+0900\n"
+"Last-Translator: Shinya Okano <xxshss@yahoo.co.jp>\n"
+"Language-Team: Japanese <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "ユーザ名"
+
+#: forms.py:41
+msgid "email address"
+msgstr "メールアドレス"
+
+#: forms.py:43
+msgid "password"
+msgstr "パスワード"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "パスワード (確認)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "ユーザ名には半角英数とアンダースコアのみが使用できます。"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "このユーザ名は既に使用されています。他のユーザ名を指定してください。"
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "同じパスワードを入力する必要があります。"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "サービス利用規約を読み、同意します。"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "登録するためには規約に同意する必要があります。"
+
+#: forms.py:124
+msgid "This email address is already in use. Please supply a different email address."
+msgstr "このメールアドレスは既に使用されています。他のメールアドレスを指定して下さい。"
+
+#: forms.py:149
+msgid "Registration using free email addresses is prohibited. Please supply a different email address."
+msgstr "自由なメールアドレスを使用した登録は禁止されています。他のメールアドレスを指定してください。"
+
+#: models.py:188
+msgid "user"
+msgstr "ユーザ"
+
+#: models.py:189
+msgid "activation key"
+msgstr "アクティベーションキー"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "登録プロファイル"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "登録プロファイル"
+
diff --git a/registration/locale/nl/LC_MESSAGES/django.mo b/registration/locale/nl/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..9e84eb3
--- /dev/null
+++ b/registration/locale/nl/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/nl/LC_MESSAGES/django.po b/registration/locale/nl/LC_MESSAGES/django.po
new file mode 100644
index 0000000..03cb2e5
--- /dev/null
+++ b/registration/locale/nl/LC_MESSAGES/django.po
@@ -0,0 +1,77 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: registration\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-08-14 13:25+0200\n"
+"PO-Revision-Date: 2008-08-14 13:25+0200\n"
+"Last-Translator: Joost Cassee <joost@cassee.net>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "gebruikersnaam"
+
+#: forms.py:41
+msgid "email address"
+msgstr "e-mail adres"
+
+#: forms.py:43
+msgid "password"
+msgstr "wachtwoord"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "wachtwoord (opnieuw)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "Gebruikersnamen kunnen alleen letters, nummer en liggende streepjes bevatten."
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Deze gebruikersnaam is reeds in gebruik. Kiest u alstublieft een andere gebruikersnaam."
+
+#: forms.py:71
+msgid "You must type the same password each time"
+msgstr "U moet twee maal hetzelfde wachtwoord typen."
+
+#: forms.py:100
+msgid "I have read and agree to the Terms of Service"
+msgstr "Ik heb de servicevoorwaarden gelezen en ga akkoord."
+
+#: forms.py:109
+msgid "You must agree to the terms to register"
+msgstr "U moet akkoord gaan met de servicevoorwaarden om u te registreren."
+
+#: forms.py:125
+msgid "This email address is already in use. Please supply a different email address."
+msgstr "Dit e-mail adres is reeds in gebruik. Kiest u alstublieft een ander e-mail adres."
+
+#: forms.py:151
+msgid "Registration using free email addresses is prohibited. Please supply a different email address."
+msgstr "U kunt u niet registreren met een gratis e-mail adres. Kiest u alstublieft een ander e-mail adres."
+
+#: models.py:191
+msgid "user"
+msgstr "gebruiker"
+
+#: models.py:192
+msgid "activation key"
+msgstr "activatiecode"
+
+#: models.py:197
+msgid "registration profile"
+msgstr "registratieprofiel"
+
+#: models.py:198
+msgid "registration profiles"
+msgstr "registratieprofielen"
diff --git a/registration/locale/pl/LC_MESSAGES/django.mo b/registration/locale/pl/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..1f2a228
--- /dev/null
+++ b/registration/locale/pl/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/pl/LC_MESSAGES/django.po b/registration/locale/pl/LC_MESSAGES/django.po
new file mode 100644
index 0000000..498fd5b
--- /dev/null
+++ b/registration/locale/pl/LC_MESSAGES/django.po
@@ -0,0 +1,84 @@
+# Polish translation for django-registration.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the django-registration package.
+# Jarek Zgoda <jarek.zgoda@gmail.com>, 2007.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: 0.4\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: 2007-12-15 12:45+0100\n"
+"Last-Translator: Jarek Zgoda <jarek.zgoda@gmail.com>\n"
+"Language-Team: Polish <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "nazwa użytkownika"
+
+#: forms.py:41
+msgid "email address"
+msgstr "adres email"
+
+#: forms.py:43
+msgid "password"
+msgstr "hasło"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "hasło (ponownie)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr ""
+"Nazwa użytkownika może zawierać tylko litery, cyfry i znaki podkreślenia"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Ta nazwa użytkownika jest już zajęta. Wybierz inną."
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "Musisz wpisać to samo hasło w obu polach"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "Przeczytałem regulamin i akceptuję go"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "Musisz zaakceptować regulamin, aby się zarejestrować"
+
+#: forms.py:124
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr "Ten adres email jest już używany. Użyj innego adresu email."
+
+#: forms.py:149
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr ""
+"Nie ma możliwości rejestracji przy użyciu darmowego adresu email. Użyj "
+"innego adresu email."
+
+#: models.py:188
+msgid "user"
+msgstr "użytkownik"
+
+#: models.py:189
+msgid "activation key"
+msgstr "klucz aktywacyjny"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "profil rejestracji"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "profile rejestracji"
diff --git a/registration/locale/pt_BR/LC_MESSAGES/django.mo b/registration/locale/pt_BR/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..e81b620
--- /dev/null
+++ b/registration/locale/pt_BR/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/pt_BR/LC_MESSAGES/django.po b/registration/locale/pt_BR/LC_MESSAGES/django.po
new file mode 100644
index 0000000..9e8addb
--- /dev/null
+++ b/registration/locale/pt_BR/LC_MESSAGES/django.po
@@ -0,0 +1,81 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "usuário"
+
+#: forms.py:41
+msgid "email address"
+msgstr "endereço de email"
+
+#: forms.py:43
+msgid "password"
+msgstr ""
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "senha (novamente)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "Nomes de usuário apenas podem conter letras, números, e underscore"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Este nome de usuário já existe. Por favor, escolha outro."
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "Você deve escrever a mesma senha nos dois campos"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "Eu lí e concordo com os Termos de Uso do serviço"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "Você deve concordar com os termos para registrar-se"
+
+#: forms.py:124
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr "Este endereço de email já está em uso. Por favor, informe um endereço de email diferente."
+
+#: forms.py:149
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr "Registrar-se com contas de email gratuitos está proibido. Por favor, informe um endereço de email diferente."
+
+#: models.py:188
+msgid "user"
+msgstr "usuário"
+
+#: models.py:189
+msgid "activation key"
+msgstr "chave de ativação"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "profile de registro"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "profiles de registro"
diff --git a/registration/locale/ru/LC_MESSAGES/django.mo b/registration/locale/ru/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..b6eb3f1
--- /dev/null
+++ b/registration/locale/ru/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/ru/LC_MESSAGES/django.po b/registration/locale/ru/LC_MESSAGES/django.po
new file mode 100644
index 0000000..8b0c309
--- /dev/null
+++ b/registration/locale/ru/LC_MESSAGES/django.po
@@ -0,0 +1,81 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "имя пользователя"
+
+#: forms.py:41
+msgid "email address"
+msgstr "адрес электронной почты"
+
+#: forms.py:43
+msgid "password"
+msgstr "пароль"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "пароль (верификация)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "Имя пользователя может содержать только буквы, цифры и подчеркивания"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Такое имя пользователя уже есть. Пожалуйста, выберите другое."
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "Вы должны вводить один и тот же пароль каждый раз"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "Я прочитал и согласен с Правилами Использования"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "Вы должны согласиться с Правилами для регистрации"
+
+#: forms.py:124
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr "Этот адрес электронной почты уже используется. Пожалуйста, введите другой адрес."
+
+#: forms.py:149
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr "Регистрация с использованием свободных почтовых серверов запрещена. Пожалуйста, введите другой адрес электронной почты."
+
+#: models.py:188
+msgid "user"
+msgstr "пользователь"
+
+#: models.py:189
+msgid "activation key"
+msgstr "ключ активации"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "профиль регистрации"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "профили регистрации"
diff --git a/registration/locale/sr/LC_MESSAGES/django.mo b/registration/locale/sr/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..1699326
--- /dev/null
+++ b/registration/locale/sr/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/sr/LC_MESSAGES/django.po b/registration/locale/sr/LC_MESSAGES/django.po
new file mode 100644
index 0000000..4fa699c
--- /dev/null
+++ b/registration/locale/sr/LC_MESSAGES/django.po
@@ -0,0 +1,80 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: django-registration trunk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-04-05 13:51+0200\n"
+"PO-Revision-Date: 2008-04-05 14:00+0100\n"
+"Last-Translator: Nebojsa Djordjevic <djnesh@gmail.com>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Poedit-Language: Serbian\n"
+"X-Poedit-Country: YUGOSLAVIA\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "korisničko ime"
+
+#: forms.py:41
+msgid "email address"
+msgstr "email adresa"
+
+#: forms.py:43
+msgid "password"
+msgstr "šifra"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "šifra (ponovo)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "Korisničko ime može da se sastoji samo od slova, brojeva i donje crte (\"_\")"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Korisničko ime je već zauzeto. Izaberite drugo."
+
+#: forms.py:71
+msgid "You must type the same password each time"
+msgstr "Unete šifre se ne slažu"
+
+#: forms.py:100
+msgid "I have read and agree to the Terms of Service"
+msgstr "Pročitao sam i slažem se sa uslovima korišćenja"
+
+#: forms.py:109
+msgid "You must agree to the terms to register"
+msgstr "Morate se složiti sa uslovima korišćenja da bi ste se registrovali"
+
+#: forms.py:128
+msgid "This email address is already in use. Please supply a different email address."
+msgstr "Ova e-mail adresa je već u upotrebi. Morate koristiti drugu e-mail adresu."
+
+#: forms.py:153
+msgid "Registration using free email addresses is prohibited. Please supply a different email address."
+msgstr "Registracija korišćenjem besplatnig e-mail adresa je zabranjena. Morate uneti drugu e-mail adresu."
+
+#: models.py:188
+msgid "user"
+msgstr "korisnik"
+
+#: models.py:189
+msgid "activation key"
+msgstr "aktivacioni ključ"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "registracioni profil"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "registracioni profili"
+
diff --git a/registration/locale/sv/LC_MESSAGES/django.mo b/registration/locale/sv/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..50eca67
--- /dev/null
+++ b/registration/locale/sv/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/sv/LC_MESSAGES/django.po b/registration/locale/sv/LC_MESSAGES/django.po
new file mode 100644
index 0000000..dec76e2
--- /dev/null
+++ b/registration/locale/sv/LC_MESSAGES/django.po
@@ -0,0 +1,81 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-03-23 18:59+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Emil Stenström <em@kth.se>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: .\forms.py:38
+msgid "username"
+msgstr "Användarnamn"
+
+#: .\forms.py:41
+msgid "email address"
+msgstr "E-postadress"
+
+#: .\forms.py:43
+msgid "password"
+msgstr "Lösenord"
+
+#: .\forms.py:45
+msgid "password (again)"
+msgstr "Lösenord (igen)"
+
+#: .\forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "Användarnamn får bara innehålla bokstäver, siffror och understreck"
+
+#: .\forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "Det användarnamnet är upptaget. Prova ett annat."
+
+#: .\forms.py:71
+msgid "You must type the same password each time"
+msgstr "Båda lösenord måste vara lika"
+
+#: .\forms.py:100
+msgid "I have read and agree to the Terms of Service"
+msgstr "Jag har läst och accepterar avtalet"
+
+#: .\forms.py:109
+msgid "You must agree to the terms to register"
+msgstr "Du måste acceptera avtalet för att registrera dig"
+
+#: .\forms.py:128
+msgid ""
+"This email address is already in use. Please supply a different email "
+"address."
+msgstr "Den e-postadressen är upptagen, använd an annan adress."
+
+#: .\forms.py:153
+msgid ""
+"Registration using free email addresses is prohibited. Please supply a "
+"different email address."
+msgstr "Gratis e-postadresser är inte tillåtna, använd en annan adress."
+
+#: .\models.py:188
+msgid "user"
+msgstr "Användare"
+
+#: .\models.py:189
+msgid "activation key"
+msgstr "Aktiveringsnyckel"
+
+#: .\models.py:194
+msgid "registration profile"
+msgstr "Profil"
+
+#: .\models.py:195
+msgid "registration profiles"
+msgstr "Profiler"
diff --git a/registration/locale/zh_CN/LC_MESSAGES/django.mo b/registration/locale/zh_CN/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..ece5cc9
--- /dev/null
+++ b/registration/locale/zh_CN/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/zh_CN/LC_MESSAGES/django.po b/registration/locale/zh_CN/LC_MESSAGES/django.po
new file mode 100644
index 0000000..7c609c3
--- /dev/null
+++ b/registration/locale/zh_CN/LC_MESSAGES/django.po
@@ -0,0 +1,77 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: 2008-03-20 23:22+0800\n"
+"Last-Translator: hutuworm <hutuworm@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "用户名"
+
+#: forms.py:41
+msgid "email address"
+msgstr "Email 地址"
+
+#: forms.py:43
+msgid "password"
+msgstr "密码"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "密码(重复)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "用户名只能包含字母、数字和下划线"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "该用户名已被占用,请另选一个。"
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "您必须输入两遍同样的密码"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "我已阅读并同意该服务条款"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "您必须同意注册条款"
+
+#: forms.py:124
+msgid "This email address is already in use. Please supply a different email address."
+msgstr "该 Email 地址已有人使用,请提供一个另外的 Email 地址。"
+
+#: forms.py:149
+msgid "Registration using free email addresses is prohibited. Please supply a different email address."
+msgstr "禁止使用免费 Email 地址注册,请提供一个另外的 Email 地址。"
+
+#: models.py:188
+msgid "user"
+msgstr "用户"
+
+#: models.py:189
+msgid "activation key"
+msgstr "激活密钥"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "注册信息"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "注册信息"
+
diff --git a/registration/locale/zh_TW/LC_MESSAGES/django.mo b/registration/locale/zh_TW/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..24a3534
--- /dev/null
+++ b/registration/locale/zh_TW/LC_MESSAGES/django.mo
Binary files differ
diff --git a/registration/locale/zh_TW/LC_MESSAGES/django.po b/registration/locale/zh_TW/LC_MESSAGES/django.po
new file mode 100644
index 0000000..7cc090d
--- /dev/null
+++ b/registration/locale/zh_TW/LC_MESSAGES/django.po
@@ -0,0 +1,77 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-19 19:30-0500\n"
+"PO-Revision-Date: 2008-03-20 23:22+0800\n"
+"Last-Translator: hutuworm <hutuworm@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:38
+msgid "username"
+msgstr "用戶名"
+
+#: forms.py:41
+msgid "email address"
+msgstr "Email 地址"
+
+#: forms.py:43
+msgid "password"
+msgstr "密碼"
+
+#: forms.py:45
+msgid "password (again)"
+msgstr "密碼(重復)"
+
+#: forms.py:54
+msgid "Usernames can only contain letters, numbers and underscores"
+msgstr "用戶名只能包含字母、數字和下劃線"
+
+#: forms.py:59
+msgid "This username is already taken. Please choose another."
+msgstr "該用戶名已被佔用,請另選一個。"
+
+#: forms.py:68
+msgid "You must type the same password each time"
+msgstr "您必須輸入兩遍同樣的密碼"
+
+#: forms.py:96
+msgid "I have read and agree to the Terms of Service"
+msgstr "我已閱讀並同意該服務條款"
+
+#: forms.py:105
+msgid "You must agree to the terms to register"
+msgstr "您必須同意注冊條款"
+
+#: forms.py:124
+msgid "This email address is already in use. Please supply a different email address."
+msgstr "該 Email 地址已有人使用,請提供一個另外的 Email 地址。"
+
+#: forms.py:149
+msgid "Registration using free email addresses is prohibited. Please supply a different email address."
+msgstr "禁止使用免費 Email 地址注冊,請提供一個另外的 Email 地址。"
+
+#: models.py:188
+msgid "user"
+msgstr "用戶"
+
+#: models.py:189
+msgid "activation key"
+msgstr "激活密鑰"
+
+#: models.py:194
+msgid "registration profile"
+msgstr "注冊信息"
+
+#: models.py:195
+msgid "registration profiles"
+msgstr "注冊信息"
+
diff --git a/registration/management/__init__.py b/registration/management/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/registration/management/__init__.py
diff --git a/registration/management/commands/__init__.py b/registration/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/registration/management/commands/__init__.py
diff --git a/registration/management/commands/cleanupregistration.py b/registration/management/commands/cleanupregistration.py
new file mode 100644
index 0000000..abec5ae
--- /dev/null
+++ b/registration/management/commands/cleanupregistration.py
@@ -0,0 +1,19 @@
+"""
+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.models import RegistrationProfile
+
+
+class Command(NoArgsCommand):
+ help = "Delete expired user registrations from the database"
+
+ def handle_noargs(self, **options):
+ RegistrationProfile.objects.delete_expired_users()
diff --git a/registration/models.py b/registration/models.py
new file mode 100644
index 0000000..4a211c1
--- /dev/null
+++ b/registration/models.py
@@ -0,0 +1,255 @@
+import datetime
+import random
+import re
+import sha
+
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+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 _
+
+
+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.
+
+ To execute customized logic when a ``User`` is activated,
+ connect a function to the signal
+ ``registration.signals.user_activated``; this signal will be
+ sent (with the ``User`` as the value of the keyword argument
+ ``user``) after a successful activation.
+
+ """
+ from registration.signals import user_activated
+
+ # 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()
+ user_activated.send(sender=self.model, user=user)
+ return user
+ return False
+
+ def create_inactive_user(self, username, password, email,
+ send_email=True):
+ """
+ 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 execute customized logic once the new ``User`` has been
+ created, connect a function to the signal
+ ``registration.signals.user_registered``; this signal will be
+ sent (with the new ``User`` as the value of the keyword
+ argument ``user``) after the ``User`` and
+ ``RegistrationProfile`` have been created, and the email (if
+ any) has been sent..
+
+ """
+ from registration.signals import user_registered
+
+ 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:
+ 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])
+ user_registered.send(sender=self.model, user=new_user)
+ 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 = sha.new(str(random.random())).hexdigest()[:5]
+ activation_key = sha.new(salt+user.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():
+ if profile.activation_key_expired():
+ user = profile.user
+ if not user.is_active:
+ user.delete()
+
+
+class RegistrationProfile(models.Model):
+ """
+ A simple profile which stores an activation key for use during
+ user account registration.
+
+ 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.datetime.now())
+ activation_key_expired.boolean = True
diff --git a/registration/signals.py b/registration/signals.py
new file mode 100644
index 0000000..2a6eed9
--- /dev/null
+++ b/registration/signals.py
@@ -0,0 +1,8 @@
+from django.dispatch import Signal
+
+
+# A new user has registered.
+user_registered = Signal(providing_args=["user"])
+
+# A user has activated his or her account.
+user_activated = Signal(providing_args=["user"])
diff --git a/registration/tests.py b/registration/tests.py
new file mode 100644
index 0000000..0f26553
--- /dev/null
+++ b/registration/tests.py
@@ -0,0 +1,355 @@
+"""
+Unit tests for django-registration.
+
+These tests assume that you've completed all the prerequisites for
+getting django-registration running in the default setup, to wit:
+
+1. You have ``registration`` in your ``INSTALLED_APPS`` setting.
+
+2. You have created all of the templates mentioned in this
+ application's documentation.
+
+3. You have added the setting ``ACCOUNT_ACTIVATION_DAYS`` to your
+ settings file.
+
+4. You have URL patterns pointing to the registration and activation
+ views, with the names ``registration_register`` and
+ ``registration_activate``, respectively, and a URL pattern named
+ 'registration_complete'.
+
+"""
+
+import datetime
+import sha
+
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.core import mail
+from django.core import management
+from django.core.urlresolvers import reverse
+from django.test import TestCase
+
+from registration import forms
+from registration.models import RegistrationProfile
+from registration import signals
+
+
+class RegistrationTestCase(TestCase):
+ """
+ Base class for the test cases; this sets up two users -- one
+ expired, one not -- which are used to exercise various parts of
+ the application.
+
+ """
+ def setUp(self):
+ self.sample_user = RegistrationProfile.objects.create_inactive_user(username='alice',
+ password='secret',
+ email='alice@example.com')
+ self.expired_user = RegistrationProfile.objects.create_inactive_user(username='bob',
+ password='swordfish',
+ email='bob@example.com')
+ self.expired_user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS + 1)
+ self.expired_user.save()
+
+
+class RegistrationModelTests(RegistrationTestCase):
+ """
+ Tests for the model-oriented functionality of django-registration,
+ including ``RegistrationProfile`` and its custom manager.
+
+ """
+ def test_new_user_is_inactive(self):
+ """
+ Test that a newly-created user is inactive.
+
+ """
+ self.failIf(self.sample_user.is_active)
+
+ def test_registration_profile_created(self):
+ """
+ Test that a ``RegistrationProfile`` is created for a new user.
+
+ """
+ self.assertEqual(RegistrationProfile.objects.count(), 2)
+
+ def test_activation_email(self):
+ """
+ Test that user signup sends an activation email.
+
+ """
+ self.assertEqual(len(mail.outbox), 2)
+
+ def test_activation_email_disable(self):
+ """
+ Test that activation email can be disabled.
+
+ """
+ RegistrationProfile.objects.create_inactive_user(username='noemail',
+ password='foo',
+ email='nobody@example.com',
+ send_email=False)
+ self.assertEqual(len(mail.outbox), 2)
+
+ def test_activation(self):
+ """
+ Test that user activation actually activates the user and
+ properly resets the activation key, and fails for an
+ already-active or expired user, or an invalid key.
+
+ """
+ # Activating a valid user returns the user.
+ self.failUnlessEqual(RegistrationProfile.objects.activate_user(RegistrationProfile.objects.get(user=self.sample_user).activation_key).pk,
+ self.sample_user.pk)
+
+ # The activated user must now be active.
+ self.failUnless(User.objects.get(pk=self.sample_user.pk).is_active)
+
+ # The activation key must now be reset to the "already activated" constant.
+ self.failUnlessEqual(RegistrationProfile.objects.get(user=self.sample_user).activation_key,
+ RegistrationProfile.ACTIVATED)
+
+ # Activating an expired user returns False.
+ self.failIf(RegistrationProfile.objects.activate_user(RegistrationProfile.objects.get(user=self.expired_user).activation_key))
+
+ # Activating from a key that isn't a SHA1 hash returns False.
+ self.failIf(RegistrationProfile.objects.activate_user('foo'))
+
+ # Activating from a key that doesn't exist returns False.
+ self.failIf(RegistrationProfile.objects.activate_user(sha.new('foo').hexdigest()))
+
+ def test_account_expiration_condition(self):
+ """
+ Test that ``RegistrationProfile.activation_key_expired()``
+ returns ``True`` for expired users and for active users, and
+ ``False`` otherwise.
+
+ """
+ # Unexpired user returns False.
+ self.failIf(RegistrationProfile.objects.get(user=self.sample_user).activation_key_expired())
+
+ # Expired user returns True.
+ self.failUnless(RegistrationProfile.objects.get(user=self.expired_user).activation_key_expired())
+
+ # Activated user returns True.
+ RegistrationProfile.objects.activate_user(RegistrationProfile.objects.get(user=self.sample_user).activation_key)
+ self.failUnless(RegistrationProfile.objects.get(user=self.sample_user).activation_key_expired())
+
+ def test_expired_user_deletion(self):
+ """
+ Test that
+ ``RegistrationProfile.objects.delete_expired_users()`` deletes
+ only inactive users whose activation window has expired.
+
+ """
+ RegistrationProfile.objects.delete_expired_users()
+ self.assertEqual(RegistrationProfile.objects.count(), 1)
+
+ def test_management_command(self):
+ """
+ Test that ``manage.py cleanupregistration`` functions
+ correctly.
+
+ """
+ management.call_command('cleanupregistration')
+ self.assertEqual(RegistrationProfile.objects.count(), 1)
+
+ def test_signals(self):
+ """
+ Test that the ``user_registered`` and ``user_activated``
+ signals are sent, and that they send the ``User`` as an
+ argument.
+
+ """
+ def receiver(sender, **kwargs):
+ self.assert_('user' in kwargs)
+ self.assertEqual(kwargs['user'].username, u'signal_test')
+ received_signals.append(kwargs.get('signal'))
+
+ received_signals = []
+ expected_signals = [signals.user_registered, signals.user_activated]
+ for signal in expected_signals:
+ signal.connect(receiver)
+
+ RegistrationProfile.objects.create_inactive_user(username='signal_test',
+ password='foo',
+ email='nobody@example.com',
+ send_email=False)
+ RegistrationProfile.objects.activate_user(RegistrationProfile.objects.get(user__username='signal_test').activation_key)
+
+ self.assertEqual(received_signals, expected_signals)
+
+
+class RegistrationFormTests(RegistrationTestCase):
+ """
+ Tests for the forms and custom validation logic included in
+ django-registration.
+
+ """
+ def test_registration_form(self):
+ """
+ Test that ``RegistrationForm`` enforces username constraints
+ and matching passwords.
+
+ """
+ invalid_data_dicts = [
+ # Non-alphanumeric username.
+ {
+ 'data':
+ { 'username': 'foo/bar',
+ 'email': 'foo@example.com',
+ 'password1': 'foo',
+ 'password2': 'foo' },
+ 'error':
+ ('username', [u"Enter a valid value."])
+ },
+ # Already-existing username.
+ {
+ 'data':
+ { 'username': 'alice',
+ 'email': 'alice@example.com',
+ 'password1': 'secret',
+ 'password2': 'secret' },
+ 'error':
+ ('username', [u"This username is already taken. Please choose another."])
+ },
+ # Mismatched passwords.
+ {
+ 'data':
+ { 'username': 'foo',
+ 'email': 'foo@example.com',
+ 'password1': 'foo',
+ 'password2': 'bar' },
+ 'error':
+ ('__all__', [u"You must type the same password each time"])
+ },
+ ]
+
+ 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.
+
+ """
+ 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 ('aim.com', 'aol.com', 'email.com', 'gmail.com',
+ 'googlemail.com', 'hotmail.com', 'hushmail.com',
+ 'msn.com', 'mail.ru', 'mailinator.com', 'live.com'):
+ 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())
+
+
+class RegistrationViewTests(RegistrationTestCase):
+ """
+ Tests for the views included in django-registration.
+
+ """
+ def test_registration_view(self):
+ """
+ Test that the registration view rejects invalid submissions,
+ and creates a new user and redirects after a valid submission.
+
+ """
+ # Invalid data fails.
+ response = self.client.post(reverse('registration_register'),
+ data={ 'username': 'alice', # Will fail on username uniqueness.
+ 'email': 'foo@example.com',
+ 'password1': 'foo',
+ 'password2': 'foo' })
+ self.assertEqual(response.status_code, 200)
+ self.failUnless(response.context['form'])
+ self.failUnless(response.context['form'].errors)
+
+ response = self.client.post(reverse('registration_register'),
+ data={ 'username': 'foo',
+ 'email': 'foo@example.com',
+ 'password1': 'foo',
+ 'password2': 'foo' })
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(response['Location'], 'http://testserver%s' % reverse('registration_complete'))
+ self.assertEqual(RegistrationProfile.objects.count(), 3)
+
+ def test_activation_view(self):
+ """
+ Test that the activation view activates the user from a valid
+ key and fails if the key is invalid or has expired.
+
+ """
+ # Valid user puts the user account into the context.
+ response = self.client.get(reverse('registration_activate',
+ kwargs={ 'activation_key': RegistrationProfile.objects.get(user=self.sample_user).activation_key }))
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context['account'].pk, self.sample_user.pk)
+
+ # Expired user sets the account to False.
+ response = self.client.get(reverse('registration_activate',
+ kwargs={ 'activation_key': RegistrationProfile.objects.get(user=self.expired_user).activation_key }))
+ self.failIf(response.context['account'])
+
+ # Invalid key gets to the view, but sets account to False.
+ response = self.client.get(reverse('registration_activate',
+ kwargs={ 'activation_key': 'foo' }))
+ self.failIf(response.context['account'])
+
+ # Nonexistent key sets the account to False.
+ response = self.client.get(reverse('registration_activate',
+ kwargs={ 'activation_key': sha.new('foo').hexdigest() }))
+ self.failIf(response.context['account'])
diff --git a/registration/urls.py b/registration/urls.py
new file mode 100644
index 0000000..25cf2b0
--- /dev/null
+++ b/registration/urls.py
@@ -0,0 +1,72 @@
+"""
+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.
+
+"""
+
+from django.conf import settings
+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
+
+
+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','extra_context':{'settings':settings}},
+ name='registration_complete'),
+ )
diff --git a/registration/views.py b/registration/views.py
new file mode 100644
index 0000000..aac17c5
--- /dev/null
+++ b/registration/views.py
@@ -0,0 +1,153 @@
+"""
+Views which allow users to create and activate accounts.
+
+"""
+
+
+from django.conf import settings
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+
+from registration.forms import RegistrationForm
+from registration.models import RegistrationProfile
+
+
+def activate(request, activation_key,
+ template_name='registration/activate.html',
+ extra_context=None):
+ """
+ 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**
+
+ ``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.
+
+ ``template_name``
+ A custom template to use.
+
+ **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).
+
+ **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)
+ 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, 'settings':settings},
+ context_instance=context)
+
+
+def register(request, success_url=None,
+ form_class=RegistrationForm,
+ 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``.
+
+ By default, use the template
+ ``registration/registration_form.html``; to change this, pass the
+ name of a template as the keyword argument ``template_name``.
+
+ **Required arguments**
+
+ None.
+
+ **Optional arguments**
+
+ ``form_class``
+ The form class to use for registration.
+
+ ``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.
+
+ ``success_url``
+ The URL to redirect to on successful registration.
+
+ ``template_name``
+ A custom template to use.
+
+ **Context:**
+
+ ``form``
+ The registration form.
+
+ Any extra variables supplied in the ``extra_context`` argument
+ (see above).
+
+ **Template:**
+
+ registration/registration_form.html or ``template_name`` keyword
+ argument.
+
+ """
+ if request.method == 'POST':
+ form = form_class(data=request.POST, files=request.FILES)
+ if form.is_valid():
+ new_user = form.save()
+ # 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'))
+ else:
+ form = form_class()
+
+ 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,
+ { 'form': form,'settings':settings },
+ context_instance=context)
diff --git a/settings.py b/settings.py
index 353809d..9ad299d 100644
--- a/settings.py
+++ b/settings.py
@@ -46,6 +46,10 @@ TEMPLATE_LOADERS = (
# 'django.template.loaders.eggs.load_template_source',
)
+TEMPLATE_CONTEXT_PROCESSORS = ( "django.core.context_processors.auth", "troggle.context.settingsContext")
+
+LOGIN_REDIRECT_URL = '/'
+
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
@@ -56,6 +60,10 @@ MIDDLEWARE_CLASSES = (
ROOT_URLCONF = 'troggle.urls'
+ACCOUNT_ACTIVATION_DAYS=3
+
+AUTH_PROFILE_MODULE = 'expo.person'
+
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
@@ -63,5 +71,7 @@ INSTALLED_APPS = (
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.redirects',
+ 'troggle.registration',
+ 'troggle.profiles',
'troggle.expo'
)
diff --git a/templates/base.html b/templates/base.html
index b1fc47f..c88ac19 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
-<link rel="stylesheet" type="text/css" href="{{ settings.MEDIA_URL }}css/main2.css" />
+<link rel="stylesheet" type="text/css" href="{{ settings.MEDIA_URL }}css/main3.css" />
<title>{% block title %}THE TITLE{% endblock %}</title>
@@ -11,21 +11,34 @@
</head>
<body>
-<a href="/">
-<div style="float:none">
- <div id="expoHeader" style="background:#222"> <img src="{{ settings.MEDIA_URL }}loserBanner.jpg" style="position:relative;width:inherit;height:inherit;"/>
+
+<div>
+ <div id="expoHeader"> <img id="frontPageBanner" src="{{ settings.MEDIA_URL }}loserBanner.jpg"/>
<div id="expoHeaderText">
- <h1>CUCC Expeditions to Austria: 1976 - 2008</h1>
+ <h1>CUCC Expeditions to Austria: 1976 - </h1>
+ <div id="expoFinalDate">
+ <h1>2009</h1>
+ </div>
</div>
</div>
<hr/>
- <div id="editLink" style="right:0px; top:0px; text-align: right; position: absolute; top:0; right:0; z-index:1; background:#999">
+ <div id="editLink">
+ {% block loginInfo %}
+ {% if user %}
+ You are logged in as {{ user.username }}.
+ | <a href="{{ settings.URL_ROOT }}/accounts/logout">Log out</a>
+ {% else %}
+ <a href="{{ settings.URL_ROOT }}/accounts/register">Sign up</a>
+ | <a href="{{ settings.URL_ROOT }}/accounts/login">Log in</a>
+ {% endif %}
+ {% endblock%}
+ | <a href="{{ settings.URL_ROOT }}">Home </a>
{% block editLink %}
- not editable
+
{% endblock %}
</div>
</div>
-</a>
+
{% block nav %}
diff --git a/templates/personForm.html b/templates/personForm.html
new file mode 100644
index 0000000..84ee7b7
--- /dev/null
+++ b/templates/personForm.html
@@ -0,0 +1,6 @@
+{% extends "base.html" %}
+{% block content %}
+
+{{ form }}
+
+{% endblock %} \ No newline at end of file
diff --git a/templates/profiles/create_profile.html b/templates/profiles/create_profile.html
new file mode 100644
index 0000000..2eed3e4
--- /dev/null
+++ b/templates/profiles/create_profile.html
@@ -0,0 +1,13 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+<form method="post">
+{{ form }}
+<input type="submit" />
+</form>
+
+{% if form.errors %}
+ <p class="errornote">Please correct the errors below</p>
+{% endif %}
+{% endblock %} \ No newline at end of file
diff --git a/templates/profiles/profile_list.html b/templates/profiles/profile_list.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/templates/profiles/profile_list.html
diff --git a/templates/registration/activate.html b/templates/registration/activate.html
new file mode 100644
index 0000000..f87d519
--- /dev/null
+++ b/templates/registration/activate.html
@@ -0,0 +1,13 @@
+{% extends "base.html" %}
+
+{% block title %}
+registration_form.html | {{ block.super }}
+{% endblock %}
+
+{% block header %}
+<h1>activate.html</h1>
+{% endblock %}
+
+{% block content %}
+You are now activated.
+{% endblock %} \ No newline at end of file
diff --git a/templates/registration/activation_email.txt b/templates/registration/activation_email.txt
new file mode 100644
index 0000000..3047d8f
--- /dev/null
+++ b/templates/registration/activation_email.txt
@@ -0,0 +1,3 @@
+Activate your account in {{ expiration_days }} days...
+{{ URL_ROOT }}{% url registration_activate activation_key %}
+
diff --git a/templates/registration/activation_email_subject.txt b/templates/registration/activation_email_subject.txt
new file mode 100644
index 0000000..78ae905
--- /dev/null
+++ b/templates/registration/activation_email_subject.txt
@@ -0,0 +1 @@
+[CUCC Expo] Activation email \ No newline at end of file
diff --git a/templates/registration/login.html b/templates/registration/login.html
new file mode 100644
index 0000000..04b5074
--- /dev/null
+++ b/templates/registration/login.html
@@ -0,0 +1,19 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+{% if form.errors %}
+<p>Your username and password didn't match. Please try again.</p>
+{% endif %}
+
+<form method="post" action=".">
+<table>
+<tr><td>{{ form.username.label_tag }}</td><td>{{ form.username }}</td></tr>
+<tr><td>{{ form.password.label_tag }}</td><td>{{ form.password }}</td></tr>
+</table>
+
+<input type="submit" value="login" />
+<input type="hidden" name="next" value="{{ next }}" />
+</form>
+
+{% endblock %}
diff --git a/templates/registration/logout.html b/templates/registration/logout.html
new file mode 100644
index 0000000..9e40c20
--- /dev/null
+++ b/templates/registration/logout.html
@@ -0,0 +1,4 @@
+{% extends "base.html" %}
+{% block content %}
+ You have been logged out.
+{% endblock %} \ No newline at end of file
diff --git a/templates/registration/registration_activate.html b/templates/registration/registration_activate.html
new file mode 100644
index 0000000..cbd540e
--- /dev/null
+++ b/templates/registration/registration_activate.html
@@ -0,0 +1,6 @@
+{% extends “base.html” %}
+{% block body %}
+Hello {{ account }}!
+
+Check your email to confirm the activation. There are {{ expiration_days }} days left to do it.
+{% endblock %} \ No newline at end of file
diff --git a/templates/registration/registration_complete.html b/templates/registration/registration_complete.html
new file mode 100644
index 0000000..552fa04
--- /dev/null
+++ b/templates/registration/registration_complete.html
@@ -0,0 +1,13 @@
+{% extends "base.html" %}
+
+{% block title %}
+registration_complete.html | {{ block.super }}
+{% endblock %}
+
+{% block header %}
+<h1>registration_complete.html</h1>
+{% endblock %}
+
+{% block content %}
+Thank you for signing up. An email with the activation code has been sent to your inbox.
+{% endblock %} \ No newline at end of file
diff --git a/templates/registration/registration_form.html b/templates/registration/registration_form.html
new file mode 100644
index 0000000..17bebf2
--- /dev/null
+++ b/templates/registration/registration_form.html
@@ -0,0 +1,56 @@
+{% extends "base.html" %}
+
+{% block title %}
+registration_form.html | {{ block.super }}
+{% endblock %}
+
+{% block header %}
+<h1>registration_form.html</h1>
+{% endblock %}
+
+{% block content %}
+<form action="{% url registration_register %}" method="POST">
+<table>
+<tr>
+ <td align="right" valign="top">Username:</td>
+ <td>
+ {{ form.username }} <br/>
+ {% for error in form.username.errors %}
+ <span style="color:red">{{ error }}</span>
+ {% endfor %}
+ </td>
+</tr>
+<tr>
+ <td align="right" valign="top">Email:</td>
+ <td>
+ {{ form.email }} <br/>
+ {% for error in form.email.errors %}
+ <span style="color:red">{{ error }}</span>
+ {% endfor %}
+ </td>
+</tr>
+<tr>
+ <td align="right" valign="top">Password:</td>
+ <td>
+ {{ form.password1 }} <br/>
+ {% for error in form.password1.errors %}
+ <span style="color:red">{{ error }}</span>
+ {% endfor %}
+ </td>
+</tr>
+<tr>
+ <td align="right" valign="top">Password (again):</td>
+ <td>
+ {{ form.password2 }} <br/>
+ {% for error in form.password2.errors %}
+ <span style="color:red">{{ error }}</span>
+ {% endfor %}
+ </td>
+</tr>
+<tr>
+ <td>&nbsp;</td>
+ <td><input type="submit" value="Register" /></td>
+</tr>
+</table>
+</form>
+{% endblock %} \ No newline at end of file
diff --git a/urls.py b/urls.py
index 36c502e..8c2810c 100644
--- a/urls.py
+++ b/urls.py
@@ -45,6 +45,11 @@ urlpatterns = patterns('',
(r'^admin/doc/?', include('django.contrib.admindocs.urls')),
(r'^admin/(.*)', admin.site.root),
+ (r'^accounts/', include('registration.urls')),
+ (r'^profiles/', include('profiles.urls')),
+
+ (r'^personform/(.*)$', personForm),
+
(r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),