Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Initial commit
  • Loading branch information
mbrochh committed Mar 16, 2012
0 parents commit dd1eefe
Show file tree
Hide file tree
Showing 31 changed files with 547 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
@@ -0,0 +1,5 @@
*.pyc
build/
dist/
*egg-info/

7 changes: 7 additions & 0 deletions AUTHORS
@@ -0,0 +1,7 @@
Current or previous core committers

* Martin Brochhaus

Contributors (in alphabetical order)

* Your name could stand here :)
3 changes: 3 additions & 0 deletions DESCRIPTION
@@ -0,0 +1,3 @@
Provides a custom authentication backend so that users can signup to the site
via their email address instead of a username. Also provides a set of standard
templates and sane URL mappings for the whole registration workflow.
24 changes: 24 additions & 0 deletions LICENSE
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org/>
3 changes: 3 additions & 0 deletions MANIFEST.in
@@ -0,0 +1,3 @@
recursive-include registration_email *
include README.md DESCRIPTION
exclude *.orig *.pyc *.wpr
19 changes: 19 additions & 0 deletions README.md
@@ -0,0 +1,19 @@
django-registration-email
==========================

We use
[django-registration](https://bitbucket.org/ubernostrum/django-registration/overview)
in almost all our projects. However, we don't like Django's limited username
and would like to allow our users to sign up via email.

This project provides a custom authentication backend which allows a user to
authenticate via his email. We also provide an EmailRegistrationForm which
checks if an email has already been taken.

Since we still have to store a username and since emails can easily be longer
than 30 characters, the username will be computed as a md5 hexdigest of the
email address.

We included a ``urls.py`` that overrides all URLs of django-registration
and Django's auth with a clean and sane structure and you will find a default
set of all necessary templates.
7 changes: 7 additions & 0 deletions registration_email/__init__.py
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
VERSION = (0, 0, 1, 'final')

if VERSION[-1] != "final": # pragma: no cover
__version__ = '.'.join(map(str, VERSION))
else: # pragma: no cover
__version__ = '.'.join(map(str, VERSION[:-1]))
41 changes: 41 additions & 0 deletions registration_email/auth.py
@@ -0,0 +1,41 @@
"""
Custom authentication backends.
Inspired by http://djangosnippets.org/snippets/2463/
"""
from django.contrib.auth.models import User
from django.core.validators import email_re


class EmailBackend:
"""
Custom authentication backend that allows to login with an email address.
"""

supports_object_permissions = False
supports_anonymous_user = False
supports_inactive_user = False

def authenticate(self, username=None, password=None):
if email_re.search(username):
try:
user = User.objects.get(email=username)
except User.DoesNotExist:
return None
else:
#We have a non-email address username we should try username
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return None
if user.check_password(password):
return user
return None

def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Empty file.
Empty file.
84 changes: 84 additions & 0 deletions registration_email/backends/default/urls.py
@@ -0,0 +1,84 @@
"""Custom urls.py for django-registration."""
from django.conf.urls.defaults import url, patterns
from django.contrib.auth import views as auth_views
from django.views.generic.simple import direct_to_template

from registration.views import activate, register
from registration_email.forms import EmailRegistrationForm


urlpatterns = patterns(
'',
# django-registration views
url(r'^activate/complete/$',
direct_to_template,
{'template': 'registration/activation_complete.html'},
name='registration_activation_complete',
),
url(r'^activate/(?P<activation_key>\w+)/$',
activate,
{'backend': 'registration.backends.default.DefaultBackend',
'template_name': 'registration/activate.html'},
name='registration_activate',
),
url(r'^register/$',
register,
{'backend': 'registration.backends.default.DefaultBackend',
'template_name': 'registration/registration_form.html',
'form_class': EmailRegistrationForm,
},
name='registration_register',
),
url(r'^register/complete/$',
direct_to_template,
{'template': 'registration/registration_complete.html'},
name='registration_complete',
),
url(r'^register/closed/$',
direct_to_template,
{'template': 'registration/registration_closed.html'},
name='registration_disallowed',
),

# django auth urls
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,
{'template_name': 'registration/password_change_form_custom.html'},
name='auth_password_change',
),
url(r'^password/change/done/$',
auth_views.password_change_done,
{'template_name': 'registration/password_change_done_custom.html'},
name='auth_password_change_done',
),
url(r'^password/reset/$',
auth_views.password_reset,
{'template_name': 'registration/password_reset_form.html'},
name='auth_password_reset',
),
url(r'^password/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
auth_views.password_reset_confirm,
{'template_name': 'registration/password_reset_confirm.html'},
name='auth_password_reset_confirm',
),
url(r'^password/reset/complete/$',
auth_views.password_reset_complete,
{'template_name': 'registration/password_reset_complete.html'}, # NOQA
name='auth_password_reset_complete',
),
url(r'^password/reset/done/$',
auth_views.password_reset_done,
{'template_name': 'registration/password_reset_done.html'},
name='auth_password_reset_done',
),
)
72 changes: 72 additions & 0 deletions registration_email/forms.py
@@ -0,0 +1,72 @@
"""Custom registration forms that expects an email address as a username."""
import md5

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


# I put this on all required fields, because it's easier to pick up
# on them with CSS or JavaScript if they have a class of "required"
# in the HTML. Your mileage may vary. If/when Django ticket #3515
# lands in trunk, this will no longer be necessary.
attrs_dict = {'class': 'required'}


class EmailRegistrationForm(forms.Form):
"""
Form for registering a new user account.
Validates that the requested username is not already in use, and
requires the password to be entered twice to catch typos.
Subclasses should feel free to add any additional validation they
need, but should avoid defining a ``save()`` method -- the actual
saving of collected user data is delegated to the active
registration backend.
"""
email = forms.EmailField(
widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=75)),
label=_("Email")
)
password1 = forms.CharField(
widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
label=_("Password")
)
password2 = forms.CharField(
widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
label=_("Password (repeat)"))

def clean_email(self):
"""
Validate that the username is alphanumeric and is not already
in use.
"""
email = self.cleaned_data['email']
try:
User.objects.get(email__iexact=email)
except User.DoesNotExist:
return email
raise forms.ValidationError(
_('A user with that email already exists.'))

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.
"""
data = self.cleaned_data
if ('password1' in data and 'password2' in data):

if data['password1'] != data['password2']:
raise forms.ValidationError(
_("The two password fields didn't match."))

self.cleaned_data['username'] = md5.new(
data['email']).hexdigest()[0:30]
return self.cleaned_data
Empty file added registration_email/models.py
Empty file.
13 changes: 13 additions & 0 deletions registration_email/templates/registration/activate.html
@@ -0,0 +1,13 @@
{% extends "base.html" %}
{% load url from future %}
{% load i18n %}

{% block main %}
<div class="container">
<div class="sixteen columns alpha">
<h2>{% trans "Activation failure" %}</h2>
<p>{% trans "We are sorry, something went wrong!" %}</p>
<p>{% trans "The activation link you provided is broken or was already used." %}</p>
</div>
</div>
{% endblock %}
17 changes: 17 additions & 0 deletions registration_email/templates/registration/activation_complete.html
@@ -0,0 +1,17 @@
{% extends "base.html" %}
{% load url from future %}
{% load i18n %}

{% block main %}
<div class="container">
<div class="sixteen columns alpha">
<h2>{% trans "Activation complete" %}</h2>
<p>{% trans "You may now login with your username and password." %}</p>
{% if not user.is_authenticated %}
<a href="{% url "auth_login" %}">{% trans "Login now" %}</a>
{% else %}
<a href="/">{% trans "Home" %}</a>
{% endif %}
</div>
</div>
{% endblock %}
16 changes: 16 additions & 0 deletions registration_email/templates/registration/activation_email.txt
@@ -0,0 +1,16 @@
{% load i18n %}


{% blocktrans with site.name as site_name and site.domain as site_domain %}
You (or someone pretending to be you) have asked to register an account at
{{ site_name }}. If this wasn't you, please ignore this email
and your address will be removed from our records.

To activate this account, please click the following link within the next
{{ expiration_days }} days:

http://{{ site_domain }}/accounts/activate/{{ activation_key }}

Sincerely,
{{ site_name }}
{% endblocktrans %}
@@ -0,0 +1,2 @@
{% load i18n %}
{{ site.name }} {% trans "Account Request" %}
31 changes: 31 additions & 0 deletions registration_email/templates/registration/login.html
@@ -0,0 +1,31 @@
{% extends "base.html" %}
{% load url from future %}
{% load i18n %}

{% block main %}
<div class="container">
<div class="sixteen columns alpha">
<h2>{% trans "Login" %}</h2>
<form method="post" action=".">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.username.errors }}
<label for="id_username">{% trans "Email" %}</label>
{{ form.username }}
</div>
<div class="fieldWrapper">
{{ form.password.errors }}
{{ form.password.label_tag }}
{{ form.password }}
</div>
<div class="fieldWrapper">
<a href="{% url "auth_password_reset" %}">{% trans "Forgot your password?" %}</a>
<a href="{% url "registration_register" %}">{% trans "Register a new account" %}</a>?
</div>
<input type="hidden" name="next" value="{{ next }}" />
<input type="submit" value="{% trans "Login" %}">
</form>
</div>
</div>
{% endblock %}
12 changes: 12 additions & 0 deletions registration_email/templates/registration/logout.html
@@ -0,0 +1,12 @@
{% extends "base.html" %}
{% load url from future %}
{% load i18n %}

{% block main %}
<div class="container">
<div class="sixteen columns alpha">
<h2>{% trans "Logout" %}</h2>
<p>{% trans "You have been logged out." %}</p>
</div>
</div>
{% endblock %}
@@ -0,0 +1,13 @@
{% extends "base.html" %}
{% load url from future %}
{% load i18n %}

{% block main %}
<div class="container">
<div class="sixteen columns alpha">
<h2>{% trans "Password change successful" %}</h2>
<p>{% trans "Your password has been changed." %}</p>
<a href="{% url "auth_login" %}">{% trans "Back to dashboard" %}</a>
</div>
</div>
{% endblock %}

0 comments on commit dd1eefe

Please sign in to comment.