Anonymous avatar Anonymous committed 95d74b9

Initial checkin

Comments (0)

Files changed (13)

+Copyright (c) 2007, James Bennett
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of the author nor the names of its contributors
+      may be used to endorse or promote products derived from this
+      software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+This is a fairly simple user-registration application for Django,
+designed to make allowing user signups as painless as possible. It
+requires a recent Subversion checkout of Django, since it uses
+newforms and a couple other post-0.95 additions. Aside from that, it
+has no external dependencies.
+
+Here's how it works:
+
+First, use a call to ``include()`` in your project's root URLConf file
+to include this application's URLs under '/accounts/'. For example,
+this line in your root URLConf should do the trick:
+
+    (r'^accounts/', include('registration.urls')),
+
+User registration is handled by means of a model called
+``RegistrationProfile``; you can, if you like, use it as the value of
+your ``AUTH_PROFILE_MODULE`` setting, but you don't have to for it to
+work and I personally recommend developing a separate user profile
+module customized to your site if you want the functionality of
+``AUTH_PROFILE_MODULE``.
+
+Each ``RegistrationProfile`` is tied to an instance of ``auth.User``,
+and carries two piece of metadata: an activation key for their account
+and the date on which the key was generated.
+
+You'll need to add a new setting to your project's settings file,
+called ``ACCOUNT_ACTIVATION_DAYS``; this should be the number of days
+after which an unused activation key will expire. Each
+``RegistrationProfile`` has a method called ``activation_key_expired``
+which will check this setting, its own key-generation date, and return
+whether the key is still valid.
+
+There's a custom manager on ``RegistrationProfile`` which has a couple
+convenience methods:
+
+  * ``create_inactive_user`` does exactly what it sounds like. Given a
+    username, a password and an email address, it will save a new
+    ``User`` instance and set ``is_active=False`` on that
+    ``User``. Then it will generate a new ``RegistrationProfile`` with
+    an activation key, and email an activation link to the user to
+    activate their account.
+
+ * ``activate_user`` also does exactly what it sounds like; given an
+   activation key, it looks up the ``User`` and activates their
+   account so they can log in.
+
+Included with this app (in the "templates" directory) is a sample set
+of templates; here's what they do:
+
+  * ``registration_form.html`` is for user signup, and shows the
+    registration form.
+
+  * ``registration_complete.html`` is a simple page to display telling
+    the user they've signed up and will be getting an activation
+    email.
+
+ * ``activation_email.txt`` is the template the app will use to
+   generate the text of the activation email; it has access to three
+   variables:
+   
+     ``current_domain`` -- the domain name of the site the new user
+     registered for.
+     
+     ``activation_key`` -- their activation key.
+     
+     ``expiration_days`` -- the number of days for which the key will
+     be valid.
+
+  * ``activate.html`` is the template for the account activation view.
+
+  * ``login.html`` is a simple login template.
+
+  * ``logout.html`` is a simple template to display after a user logs
+    out.

Empty file added.

+from django import newforms as forms
+from django.contrib.auth.models import User
+
+# 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.
+attrs_dict = { 'class': 'required' }
+
+class RegistrationForm(forms.Form):
+    """
+    Form for registering a new user account.
+    
+    Validates that the password is entered twice and matches,
+    and that the username is not already taken.
+    
+    """
+    username = forms.CharField(max_length=30, widget=forms.TextInput(attrs=attrs_dict))
+    email = forms.EmailField(widget=forms.TextInput(attrs=attrs_dict))
+    password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict))
+    password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict))
+
+    def clean_username(self):
+        """
+        Validates that the username is not already in use.
+        
+        """
+        if self.clean_data.get('username'):
+            try:
+                user = User.objects.get(username__exact=self.clean_data['username'])
+            except User.DoesNotExist:
+                return self.clean_data['username']
+            raise forms.ValidationError(u'The username "%s" is already taken. Please choose another.' % self.clean_data['username'])
+    
+    def clean_password2(self):
+        """
+        Validates that the two password inputs match.
+        
+        """
+        if self.clean_data.get('password1') and self.clean_data.get('password2') and \
+           self.clean_data['password1'] == self.clean_data['password2']:
+            return self.clean_data['password2']
+        raise forms.ValidationError(u'You must type the same password each time')
+
+import datetime, random, sha
+from django.db import models
+from django.core.mail import send_mail
+from django.template import Context, loader
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.conf import settings
+
+
+class RegistrationManager(models.Manager):
+    """
+    Custom manager for the Profile model,
+    making it easier to manage profiles.
+    
+    """
+    def create_inactive_user(self, username, password, email, send_email=True):
+        """
+        Creates a new User and a new Profile
+        for that User, generates an activation key, and mails
+        it.
+
+        Pass send_email=False to disable sending the email.
+        
+        """
+        # Create the user.
+        new_user = User.objects.create_user(username, email, password)
+        new_user.is_active = False
+        new_user.save()
+        
+        # Generate a salted SHA1 hash to use as a key.
+        salt = sha.new(str(random.random())).hexdigest()[:5]
+        activation_key = sha.new(salt+new_user.username).hexdigest()
+
+        # Create the profile.
+        new_profile = self.create(user=new_user,
+                                  activation_key=activation_key)
+
+        if send_email:
+            # Send the activation email.
+            current_domain = Site.objects.get_current().domain
+            subject = "Activate your new account at %s" % current_domain
+            message_template = loader.get_template('registration/activation_email.txt')
+            message_context = Context({ 'site_url': 'http://%s/' % current_domain,
+                                        'activation_key': activation_key,
+                                        'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS })
+            message = message_template.render(message_context)
+            send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [new_user.email])
+        return new_user
+
+    def activate_user(self, activation_key):
+        """
+        Given the activation key, makes a User's account active if
+        the activation key is valid and has not expired.
+        
+        Returns the User if successful, or False if the account was
+        not found or the key had expired.
+        
+        """
+        try:
+            user_profile = self.get(activation_key=activation_key)
+        except Profile.DoesNotExist:
+            return False
+        if not user_profile.activation_key_expired():
+            # Account exists and has a non-expired key. Activate it.
+            user = user_profile.user
+            user.is_active = True
+            user.save()
+            return user
+        return False
+
+
+class RegistrationProfile(models.Model):
+    """
+    Simple profile model for a User, storing a registration
+    date and an activation key for the account.
+
+    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 is intended solely to store some data
+    needed for user registration, and can do that regardless of
+    what you set in ``AUTH_PROFILE_MODULE``, so if you want to
+    use user profiles in a project, it's far better to develop
+    a customized model for that purpose and just let this one
+    handle registration.
+    
+    """
+    user = models.ForeignKey(User, unique=True)
+    activation_key = models.CharField(maxlength=40)
+    key_generated = models.DateTimeField()
+    
+    objects = RegistrationManager()
+
+    class Admin:
+        pass
+    
+    def save(self):
+        if not self.id:
+            self.key_generated = datetime.datetime.now()
+        super(RegistrationProfile, self).save()
+    
+    def __str__(self):
+        return "User profile for %s" % self.user.username
+    
+    def activation_key_expired(self):
+        """
+        Determines whether this Profile's activation key has expired, based
+        on the value of the setting ``ACCOUNT_ACTIVATION_DAYS``.
+        
+        """
+        return self.key_generated + datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS) <= datetime.datetime.now()

templates/registration/activate.html

+{% extends "base.html" %}
+
+{% block title %}Account activation{% endblock %}
+
+{% block content %}
+{% load humanize %}
+<h2>Account activation</h2>
+{% if account %}
+<p>Thanks for signing up! Now you can <a href="/accounts/login/">log in</a> and start contributing!</p>
+{% else %}
+<p>Either your activation link was incorrect, or the activation key for your account has expired; activation keys are only
+valid for {{ expiration_days|apnumber }} days after registration.</p>
+{% endif %}
+{% endblock %}

templates/registration/activation_email.txt

+{% load humanize %}
+Someone, hopefully you, signed up for a new account at {{ site_url }} using this email address. If it was you, and you'd like to activate and used your account, click the link below or copy and paste it into your web browser's address bar:
+
+{{ site_url }}accounts/activate/{{ activation_key }}/
+
+If you didn't request this, you don't need to do anything; you won't receive any more email from us, and the account will expire automatically in {{ expiration_days|apnumber }} days.
+

templates/registration/login.html

+{% extends "base.html" %}
+
+{% block title %}Log in{% endblock %}
+
+{% block content %}
+<h2>Log in</h2>
+<form method="post" action="?next=/snippets/">
+<dl>
+<dt><label for="id_username">Username:</label>{% if form.username.errors %} {{ form.username.errors|join:", " }}{% endif %}</dt>
+<dd>{{ form.username }}</dd>
+<dt><label for="id_password">Password:</label>{% if form.password.errors %} {{ form.password.errors|join:", " }}{% endif %}</dt>
+<dd>{{ form.password }}</dd>
+<dt><input type="submit" value="Log in" /></dt>
+</dl>
+</form>
+{% endblock %}

templates/registration/logout.html

+{% extends "base.html" %}
+
+{% block title %}Logged out{% endblock %}
+
+{% block content %}
+<h2>Logged out</h2>
+
+<p>You've been logged out.</p>
+{% endblock %}
Add a comment to this file

templates/registration/registration_complete.html

Empty file added.

templates/registration/registration_form.html

+{% extends "base.html" %}
+
+{% block title %}Sign up{% endblock %}
+
+{% block content %}
+<h2>Sign up</h2>
+<p>Fill out the form below, and your account will be created; you'll be sent an email
+with instructions on how to finish your registration.</p>
+<form method="post" action="">
+<dl>
+<dt><label for="id_username">Username:</label>{% if form.username.errors %} {{ form.username.errors|join:", " }}{% endif %}</dt>
+<dd>{{ form.username }}</dd>
+<dt><label for="id_email">Email address:</label>{% if form.email.errors %} {{ form.email.errors|join:", " }}{% endif %}</dd>
+<dd>{{ form.email }}</dd>
+<dt><label for="id_password1">Password:</label>{% if form.password1.errors %} {{ form.password2.errors|join:", " }}{% endif %}</dt>
+<dd>{{ form.password1 }}</dd>
+<dt><label for="id_password2">Password (type again to catch any typos):</label>{% if form.password2.errors %} {{ form.password2.errors|join:", " }}{% endif %}</dt>
+<dd>{{ form.password2 }}</dd>
+<dt><input type="submit" value="Register" /></dt>
+</dl>
+</form>
+{% endblock %}
+"""
+Recommended usage is to use a call to ``include()`` in your
+project's root URLConf to include this URLConf for any URL
+begninning with '/accounts/'.
+
+"""
+
+from django.conf.urls.defaults import *
+from django.views.generic.simple import direct_to_template
+from django.contrib.auth.models import User
+from django.contrib.auth.views import login, logout
+from views import activate, register
+
+urlpatterns = patterns('',
+                       (r'^activate/(?P<activation_key>\w+)/$', activate),
+                       (r'^login/$', login, {'template_name': 'registration/login.html'}),
+                       (r'^logout/$', logout, {'template_name': 'registration/logout.html'}),
+                       (r'^register/$', register),
+                       (r'^register/complete/$', direct_to_template, {'template': 'registration/registration_complete.html'}),
+                       )
+from django.http import HttpResponseRedirect
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.conf import settings
+from models import RegistrationProfile
+from forms import RegistrationForm
+
+def activate(request, activation_key):
+    """
+    Activates a user's account, if their key is valid
+    and hasn't expired.
+
+    Context::
+        account
+            The ``User`` object corresponding to the account,
+            if the activation was successful.
+
+        expiration_days
+            The number of days for which activation keys stay valid.
+
+    Template::
+        registration/activate.html
+    
+    """
+    account = RegistrationProfile.objects.activate_user(activation_key)
+    return render_to_response('registration/activate.html',
+                              {'account': account,
+                               'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS },
+                              context_instance=RequestContext(request))
+
+def register(request, success_url='/accounts/register/complete/'):
+    """
+    Allows a new user to register an account.
+
+    On successful registration, an email will be sent to the new
+    user with an activation link to click to make the account
+    active. This view will then redirect to ``success_url``,
+    which defaults to '/accounts/register/complete/'. This
+    application has a URL pattern for that URL and routes it to
+    the ``direct_to_template`` generic view to display a short
+    message telling the user to check their email for the
+    account activation link.
+    
+    Context::
+        form
+            The registration form
+    
+    Template::
+        registration/registration_form.html
+    
+    """
+    if request.method == 'POST':
+        form = RegistrationForm(request.POST)
+        if form.is_valid():
+            new_user = RegistrationProfile.objects.create_inactive_user(username=form.clean_data['username'],
+                                                            password=form.clean_data['password1'],
+                                                            email=form.clean_data['email'])
+            return HttpResponseRedirect(success_url)
+    else:
+        form = RegistrationForm()
+    return render_to_response('registration/registration_form.html',
+                              { 'form': form },
+                              context_instance=RequestContext(request))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.