Commit 93e9a013 authored by chris's avatar chris
Browse files

Added account and urlauth modules

parent b69208eb
Nuages - Easy poll sharing
Nuages aims to provide a collaborative meeting poll system, similar to doodle or rdvz. It is build in python, using the django framework and a little of javascript. This application was named after Django's famous song. Feedback and collaboration are welcome at nuage@
The project uses the following django modules:
- django-urlauth:
- django-account:
The project uses the following javascripts:
- jquery, jquery-ui:
- jquery-dynamic-formset:
# -*- coding: utf-8
import re
from django import forms
from django.conf import settings
from django.contrib.auth import authenticate, login
from django.utils.translation import ugettext as _
from django.template import loader
from django.contrib.auth.models import User
from django.utils.safestring import mark_safe
from account.util import load_class
UserModel = load_class(settings.ACCOUNT_USER_MODEL)
CaptchaField = load_class(settings.ACCOUNT_CAPTCHA_FIELD)
RE_USERNAME = getattr(settings, 'ACCOUNT_RE_USERNAME',
re.compile(r'[a-z0-9][_a-z0-9]*[a-z0-9]$', re.I))
class PasswordField(forms.CharField):
Form field for password handling.
def __init__(self, *args, **kwargs):
super(PasswordField, self).__init__(*args, **kwargs)
self.widget = forms.PasswordInput(render_value=False)
self.help_text = ''
def clean(self, value):
super(PasswordField, self).clean(value)
if len(value) < PASSWORD_MIN_LENGTH:
raise forms.ValidationError(_(u'Password length is less than %(min)d') % {'min': PASSWORD_MIN_LENGTH})
if len(value) > PASSWORD_MAX_LENGTH:
raise forms.ValidationError(_(u'Password length is more than %(max)d') % {'max': PASSWORD_MAX_LENGTH})
return value
class RegistrationForm(forms.Form):
username = forms.CharField(label=_(u'Login'), help_text=_(u'You can use a-z, 0-9 and underscore. Login length could be from %(min)s to %(max)s chars.') % {'min': USERNAME_MIN_LENGTH, 'max': USERNAME_MAX_LENGTH})
email = forms.EmailField(label=_('Email'))
password = PasswordField(label=_('Password'))
password_dup = PasswordField(label=_('Password (confirmation)'))
default_error_messages = {
'short_login': _(u'Login length is less than %(min)d'),
'long_login': _(u'Login length is more than %(max)d'),
'invalid_login': _(u'Login contains restricted symbols'),
'taken_login': _(u'This login already registered'),
'taken_email': _(u'This email already registered.'),
'mismatched_passwords': _('Passwords do not match'),
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['captcha'] = CaptchaField(label=settings.ACCOUNT_CAPTCHA_LABEL)
def clean_username(self):
if 'username' in self.cleaned_data:
value = self.cleaned_data['username']
if len(value) < USERNAME_MIN_LENGTH:
raise forms.ValidationError(self.default_error_messages['short_login'] % {'min': USERNAME_MIN_LENGTH})
if len(value) > USERNAME_MAX_LENGTH:
raise forms.ValidationError(self.default_error_messages['long_login'] % {'max': USERNAME_MAX_LENGTH})
if not RE_USERNAME.match(value):
raise forms.ValidationError(self.default_error_messages['invalid_login'])
except UserModel.DoesNotExist:
return value
raise forms.ValidationError(self.default_error_messages['taken_login'])
def clean_email(self):
#return self.cleaned_data.get('email','')
if 'email' in self.cleaned_data:
email = self.cleaned_data['email']
except UserModel.DoesNotExist:
return email
raise forms.ValidationError(self.default_error_messages['taken_email'])
def clean(self):
pwd1 = self.cleaned_data.get('password')
pwd2 = self.cleaned_data.get('password_dup')
if pwd1 and pwd2:
if pwd1 != pwd2:
# show error on top of password_dup field
self._errors['password_dup'] = [self.default_error_messages['mismatched_passwords']]
return self.cleaned_data
def save(self):
username = self.cleaned_data['username']
email = self.cleaned_data['email']
password = self.cleaned_data['password']
user = UserModel.objects.create_user(username, email, password)
user.is_active = False
return user
class PasswordResetForm(forms.Form):
email = forms.EmailField(label=_('Email'))
default_error_messages = {'taken_email': _(u'This email is not registered')}
def clean_email(self):
if 'email' in self.cleaned_data:
email = self.cleaned_data['email']
if UserModel.objects.filter(email=email).count():
return email
raise forms.ValidationError(self.default_error_messages['taken_email'])
class LoginForm(forms.Form):
username = forms.CharField(label=_('Username'))
password = forms.CharField(label=_('Password'), widget=forms.PasswordInput)
default_error_messages = {
'invalid_account': _(u'Incorrect login or password'),
'inactive_account': _(u'Sorry. You account is not active. Maybe you didn\'t activate it.'),
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
self.base_fields['username'].help_text = ''
#self.base_fields['password'].widget = forms.PasswordInput()
self.base_fields['password'].help_text = ''
super(LoginForm, self).__init__(*args, **kwargs)
def clean(self):
super(LoginForm, self).clean()
if self.is_valid():
user = authenticate(
if not user is None:
if user.is_active:
login(self.request, user)
return self.cleaned_data
raise forms.ValidationError(self.default_error_messages['inactive_account'])
raise forms.ValidationError(self.default_error_messages['invalid_account'])
class PasswordChangeForm(forms.Form):
Form for changing user's password.
old_password = PasswordField(label=_(u'Old password'))
password = PasswordField(label=_(u'Password'))
password_confirmation = PasswordField(label=_(u'Password (confirmation)'))
authkey = forms.CharField(widget=forms.HiddenInput, required=False)
uid = forms.CharField(widget=forms.HiddenInput, required=False)
default_error_messages = {
'invalid_password': _('Incorrect old password'),
'mismatched_passwords': _(u'The passwords do not match'),
def __init__(self, *args, **kwargs):
self.require_old = kwargs.pop('require_old', True)
self.user = kwargs.pop('user')
if not self.require_old:
self.base_fields['old_password'] = forms.Field(widget=forms.HiddenInput, required=False)
super(PasswordChangeForm, self).__init__(*args, **kwargs)
def clean(self):
if 'old_password' in self.cleaned_data and 'uid' in self.cleaned_data:
password = self.cleaned_data['old_password']
uid = self.cleaned_data['uid']
if password:
test_user = authenticate(username=self.user.username, password=password)
if not test_user:
del self.cleaned_data['old_password']
self._errors['old_password'] = [self.default_error_messages['invalid_password']]
return self.cleaned_data
def clean_password_confirmation(self):
pass1 = self.cleaned_data['password']
pass2 = self.cleaned_data['password_confirmation']
if pass1 != pass2:
raise forms.ValidationError(self.default_error_messages['mismatched_passwords'])
return pass1
def clean_uid(self):
if self.require_old:
return self.cleaned_data['uid']
user = User.objects.get(pk=self.cleaned_data['uid'])
self.user = user
return self.cleaned_data['uid']
except User.DoesNotExist:
raise forms.ValidationError(_(u'Invalid username'))
def save(self):
return self.user
class EmailChangeForm(forms.Form):
Form for email chanage.
email = forms.EmailField(label=_(u'New email'))
# This file is distributed under the same license as the PACKAGE package.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-01-24 08:32+0600\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Login"
msgstr "Логин"
#, python-format
msgid ""
"You can use a-z, 0-9 and underscore. Login length should be between %(min)s "
"and %(max)s"
msgstr ""
"Вы можете использовать символы a-z, 0-9 и подчёркивание. Длина логина должна "
"быть между %(min)s и %(max)s"
#, python-format
msgid "Login length is less than %(min)d"
msgstr "Длина логина меньше, чем %(min)d"
#, python-format
msgid "Login length is more than %(max)d"
msgstr "Длина логина больше, чем %(max)d"
msgid "Login contains restricted symbols"
msgstr "Логин содержит запрещённые символы"
msgid "This login already registered"
msgstr "Этот логин уже занят"
#, python-format
msgid "Password length is less than %(min)d"
msgstr "Длина пароля меньше, чем %(min)d"
#, python-format
msgid "Password length is more than %(max)d"
msgstr "Длина пароля больше, чем %(max)d"
msgid "Email"
msgstr "Электронная почта"
msgid "Password"
msgstr "Пароль"
msgid "Password (confirmation)"
msgstr "Пароль (подтверждение)"
msgid "This email already registered"
msgstr "Этот email уже зарегистрирован"
msgid "Passwords do not match"
msgstr "Пароли не совпадают"
msgid "This email is not registered"
msgstr "Этот email не зарегистрирован"
msgid "Username"
msgstr "Логин"
msgid "Incorrect login or password"
msgstr "Неправильный логин или пароль"
msgid "Sorry. You account is not active. Maybe you didn't activate it."
msgstr ""
"Извините. Ваша учётная запись неактивна. Возможно, вы забыли активировать её."
msgid "Old password"
msgstr "Старый пароль"
msgid "Incorrect old password"
msgstr "Неправильный старый пароль"
msgid "The passwords do not match"
msgstr "Пароли не совпадают"
msgid "Invalid username"
msgstr ""
msgid "New email"
msgstr ""
msgid "You have to logout before registration"
msgstr "Вы должны выйти перед регистрацией"
msgid "Sorry. Registration is disabled."
msgstr "Извините. Регистрация закрыта."
msgid ""
"The error was occuried while sending email with activation code. Account was "
"not created. Please, try later."
msgstr ""
"Во время отсылки email возникла ошибка. Учётная запись не создана. "
"Пожалуйста, попробуйте позже."
#: templates/account/password_changed.html:6
msgid "Password has been changed."
msgstr "Пароль был изменён."
msgid "Check the mail please"
msgstr "Проверьте почту, пожалуйста"
msgid ""
"Unfortunately we could not send you email in current time. Please, try later"
msgstr ""
"К сожалению, мы не можем выслать вам сейчас email. Пожалуйста, попробуйте "
msgid "You are already authenticated"
msgstr "Вы уже вошли"
#, python-format
msgid "Your email has been changed to %s"
msgstr ""
msgid "You have successfully loged out"
msgstr "Вы успешно вышли"
#: templates/account/change_email.html:5
msgid "Change email"
msgstr ""
#: templates/account/change_email.html:8 templates/account/new_password.html:8
msgid "Save"
msgstr "Сохранить"
#: templates/account/created.html:5
msgid ""
"You have successfully registered in our site. Now you should activate your "
"account. Check email please."
msgstr ""
"Вы успешно зарегистрировались. Теперь вам нужно активировать вашу учётную "
"запись. Пожалуйста, проверьте почту."
#: templates/account/login.html:5
msgid "Authorization"
msgstr "Авторизация"
#: templates/account/login.html:6
msgid ""
"You should login to gain access to all site features. If you have not account"
msgstr ""
"Вы должны авторизоваться, чтобы получить доступ ко всем возможностям сайта. "
"Если у вас нет учётной записи,"
#: templates/account/login.html:6
msgid "register"
msgstr "пройдите регистрацию,"
#: templates/account/login.html:6
msgid "please"
msgstr "пожалуйста"
#: templates/account/login.html:9
msgid "login"
msgstr "войти"
#: templates/account/login.html:12
msgid "I forgot password"
msgstr "Я забыл пароль"
#: templates/account/message.html:5
msgid "Important information"
msgstr "Важная информация"
#: templates/account/new_password.html:5
msgid "New password"
msgstr "Новый пароль"
#: templates/account/password_changed.html:8
#, python-format
msgid ""
"Password has been changed. You can now <a href=\"%(login_url)s\">log on</a> "
msgstr ""
#: templates/account/registration.html:6
msgid "Registration"
msgstr "Регистрация"
#: templates/account/registration.html:9
#: templates/account/reset_password.html:8
msgid "Continue"
msgstr "Продолжить"
#: templates/account/reset_password.html:5
msgid "Password restore"
msgstr "Восстановление пароля"
#: templates/account/welcome.html:5
msgid "Welcome"
msgstr "Добро пожаловать"
#: templates/account/welcome.html:7
msgid ""
"Congratulations! You have successfully complete the registration on our site."
msgstr "Поздравляем! Вы успешно завершили регистрацию на нашем сайте."
#: templates/account/mail/reset_password.html:1
#, python-format
msgid "Password restore on %(domain)s"
msgstr "Восстановление пароля на сайте %(domain)s"
#: templates/account/mail/reset_password.html:2
#, python-format
msgid "You have requested password restore on %(domain)s"
msgstr "Вы запросили смену пароля на сайте %(domain)s"
#: templates/account/mail/reset_password.html:3
#, python-format
msgid ""
"After visiting the %(url)s, the new password will be set for your account"
msgstr ""
"После посещения ссылки %(url)s для вашего аккаунта будет установлен новый "
#: templates/account/mail/reset_password.html:4
#, python-format
msgid "New password: %(password)s"
msgstr "Новый пароль: %(password)s"
#~ msgid "\"Email change on %(domain)s"
#~ msgstr "Смена email на сайте %(domain)s"
#~ msgid "You have requested the change of email on %(domain)s"
#~ msgstr "Вы запросили смену email на сайте %(domain)s"
#~ msgid ""
#~ "After visiting the %(url)s, the new email will be set for your accont"
#~ msgstr ""
#~ "После посещения ссылки %(url)s для вашего аккаунта будет установлен новый "
#~ "пароль"
#~ msgid "New email: %(email)s"
#~ msgstr "Новый пароль: %(email)s"
#~ msgid "Registration on %(domain)s"
#~ msgstr "Регистрация на сайте %(domain)s"
#~ msgid "You have begun registration on %(domain)s"
#~ msgstr "Вы начали регистрация на сайте %(domain)s"
#~ msgid ""
#~ "If you want activate you account %(login)s, follow the link please: %(url)"
#~ "s"
#~ msgstr "Для активации аккаунта %(login)s откройте ссылку %(url)s"
#~ msgid "Successfuly registration on %(domain)s"
#~ msgstr "Успешная регистрация на сайте %(domain)s"
#~ msgid "You have registered on %(domain)s"
#~ msgstr "Вы зарегистрировались на сайте %(domain)s"
#~ msgid "Your login is %(login)s"
#~ msgstr "Ваш логин: %(login)s"
#~ msgid "foo %(min)s and %(max)s"
#~ msgstr "фуу %(min)s баррр %(max)s"
from datetime import datetime
from import BaseCommand
from account.models import AuthKey
class Command(BaseCommand):
help = 'Purge expired AuthKey objects'
def handle(self, *args, **kwargs):
qs = AuthKey.objects.filter(
count = qs.count()
print '%d keys deleted' % count
import account.signals
from django.utils.translation import ugettext_lazy as _
ACCOUNT_USER_MODEL = 'django.contrib.auth.models.User'
ACCOUNT_LOGIN_FORM = 'account.forms.LoginForm'
ACCOUNT_REGISTRATION_FORM = 'account.forms.RegistrationForm'
ACCOUNT_PASSWORD_RESET_FORM = 'account.forms.PasswordResetForm'
ACCOUNT_PASSWORD_CHANGE_FORM = 'account.forms.PasswordChangeForm'
ACCOUNT_CAPTCHA_FIELD = 'captcha.fields.CaptchaField'
ACCOUNT_CAPTCHA_LABEL = _('Enter the text on the image')
from django.contrib import auth
from django.dispatch import Signal
from django.contrib.sites.models import Site
from urlauth.signals import key_user_found
from urlauth.models import AuthKey
from account.util import email_template
account_created = Signal(providing_args=['user', 'request'])
def key_user_found_handler(key, user, **kwargs):
extra = key.extra
action = extra.get('action')
if 'activation' == action:
if not user.is_active:
user.is_active = True
email_template(, 'account/mail/welcome',
user=user, domain=Site.objects.get_current().domain)
if user.is_active:
if 'new_email' == action:
if 'email' in