Skip to content
Browse files

Merge remote-tracking branch 'pinax/master'

  • Loading branch information...
2 parents 989aadf + 67ce6b1 commit b368472602251e1080e0a23c391217c4af12223e Matt George committed May 23, 2012
View
7 .tx/config
@@ -0,0 +1,7 @@
+[main]
+host = https://www.transifex.net
+
+[django-user-accounts.djangopo]
+file_filter = account/locale/<lang>/LC_MESSAGES/django.po
+source_lang = en
+source_file = account/locale/en/LC_MESSAGES/django.po
View
1 README.rst
@@ -9,6 +9,7 @@ Requirements
* Django 1.4
* django-appconf (included in ``install_requires``)
+* pytz (included in ``install_requires``)
Documentation
=============
View
12 account/admin.py
@@ -0,0 +1,12 @@
+from django.contrib import admin
+
+from account.models import SignupCode
+
+
+class SignupCodeAdmin(admin.ModelAdmin):
+ list_display = ["code", "max_uses", "use_count", "expiry", "created"]
+ search_fields = ["code", "email"]
+ list_filter = ["created"]
+
+
+admin.site.register(SignupCode, SignupCodeAdmin)
View
4 account/conf.py
@@ -15,15 +15,17 @@ class AccountAppConf(AppConf):
LOGOUT_REDIRECT_URL = "/"
PASSWORD_CHANGE_REDIRECT_URL = "account_password"
PASSWORD_RESET_REDIRECT_URL = "account_login"
+ REMEMBER_ME_EXPIRY = 60*60*24*365*10
USER_DISPLAY = lambda user: user.username
EMAIL_UNIQUE = True
EMAIL_CONFIRMATION_REQUIRED = False
+ EMAIL_CONFIRMATION_EMAIL = True
EMAIL_CONFIRMATION_EXPIRE_DAYS = 3
EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = "account_login"
EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = None
SETTINGS_REDIRECT_URL = "account_settings"
CONTACT_EMAIL = "support@example.com"
- TIMEZONE_CHOICES = list(zip(pytz.all_timezones, pytz.all_timezones))
+ TIMEZONES = zip(pytz.all_timezones, pytz.all_timezones)
LANGUAGES = [
(code, get_language_info(code).get("name_local"))
for code, lang in settings.LANGUAGES
View
2 account/fields.py
@@ -11,7 +11,7 @@ def __init__(self, *args, **kwargs):
defaults = {
"max_length": 100,
"default": settings.TIME_ZONE,
- "choices": settings.ACCOUNT_TIMEZONE_CHOICES
+ "choices": settings.ACCOUNT_TIMEZONES
}
defaults.update(kwargs)
return super(TimeZoneField, self).__init__(*args, **defaults)
View
57 account/forms.py
@@ -21,11 +21,11 @@ class SignupForm(forms.Form):
widget=forms.TextInput(),
required=True
)
- password1 = forms.CharField(
+ password = forms.CharField(
label=_("Password"),
widget=forms.PasswordInput(render_value=False)
)
- password2 = forms.CharField(
+ password_confirm = forms.CharField(
label=_("Password (again)"),
widget=forms.PasswordInput(render_value=False)
)
@@ -52,8 +52,8 @@ def clean_email(self):
raise forms.ValidationError(_("A user is registered with this email address."))
def clean(self):
- if "password1" in self.cleaned_data and "password2" in self.cleaned_data:
- if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
+ if "password" in self.cleaned_data and "password_confirm" in self.cleaned_data:
+ if self.cleaned_data["password"] != self.cleaned_data["password_confirm"]:
raise forms.ValidationError(_("You must type the same password each time."))
return self.cleaned_data
@@ -121,15 +121,15 @@ def user_credentials(self):
class ChangePasswordForm(forms.Form):
- oldpassword = forms.CharField(
+ password_current = forms.CharField(
label=_("Current Password"),
widget=forms.PasswordInput(render_value=False)
)
- password1 = forms.CharField(
+ password_new = forms.CharField(
label=_("New Password"),
widget=forms.PasswordInput(render_value=False)
)
- password2 = forms.CharField(
+ password_new_confirm = forms.CharField(
label=_("New Password (again)"),
widget=forms.PasswordInput(render_value=False)
)
@@ -138,19 +138,19 @@ def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user")
super(ChangePasswordForm, self).__init__(*args, **kwargs)
- def clean_oldpassword(self):
- if not self.user.check_password(self.cleaned_data.get("oldpassword")):
+ def clean_password_current(self):
+ if not self.user.check_password(self.cleaned_data.get("password_current")):
raise forms.ValidationError(_("Please type your current password."))
- return self.cleaned_data["oldpassword"]
+ return self.cleaned_data["password_current"]
- def clean_password2(self):
- if "password1" in self.cleaned_data and "password2" in self.cleaned_data:
- if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
+ def clean_password_new_confirm(self):
+ if "password_new" in self.cleaned_data and "password_new_confirm" in self.cleaned_data:
+ if self.cleaned_data["password_new"] != self.cleaned_data["password_new_confirm"]:
raise forms.ValidationError(_("You must type the same password each time."))
- return self.cleaned_data["password2"]
+ return self.cleaned_data["password_new_confirm"]
def save(self, user):
- user.set_password(self.cleaned_data["password1"])
+ user.set_password(self.cleaned_data["password_new"])
user.save()
@@ -170,38 +170,41 @@ def clean_email(self):
class PasswordResetTokenForm(forms.Form):
- password1 = forms.CharField(
+ password = forms.CharField(
label = _("New Password"),
widget = forms.PasswordInput(render_value=False)
)
- password2 = forms.CharField(
+ password_confirm = forms.CharField(
label = _("New Password (again)"),
widget = forms.PasswordInput(render_value=False)
)
- def clean_password2(self):
- if "password1" in self.cleaned_data and "password2" in self.cleaned_data:
- if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
+ def clean_password_confirm(self):
+ if "password" in self.cleaned_data and "password_confirm" in self.cleaned_data:
+ if self.cleaned_data["password"] != self.cleaned_data["password_confirm"]:
raise forms.ValidationError(_("You must type the same password each time."))
- return self.cleaned_data["password2"]
+ return self.cleaned_data["password_confirm"]
class SettingsForm(forms.Form):
email = forms.EmailField(label=_("Email"), required=True)
timezone = forms.ChoiceField(
label=_("Timezone"),
- choices=settings.ACCOUNT_TIMEZONE_CHOICES,
- required=False
- )
- language = forms.ChoiceField(
- label=_("Language"),
- choices=settings.ACCOUNT_LANGUAGES,
+ choices=settings.ACCOUNT_TIMEZONES,
required=False
)
+ if settings.USE_I18N:
+ language = forms.ChoiceField(
+ label=_("Language"),
+ choices=settings.ACCOUNT_LANGUAGES,
+ required=False
+ )
def clean_email(self):
value = self.cleaned_data["email"]
+ if self.initial.get("email") == value:
+ return value
qs = EmailAddress.objects.filter(email__iexact=value)
if not qs.exists() or not settings.ACCOUNT_EMAIL_UNIQUE:
return value
View
BIN account/locale/en/LC_MESSAGES/django.mo
Binary file not shown.
View
154 account/locale/en/LC_MESSAGES/django.po
@@ -0,0 +1,154 @@
+# 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: 2012-05-15 13:38-0600\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"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:19 forms.py:92
+msgid "Username"
+msgstr ""
+
+#: forms.py:25 forms.py:64
+msgid "Password"
+msgstr ""
+
+#: forms.py:29
+msgid "Password (again)"
+msgstr ""
+
+#: forms.py:41
+msgid "Usernames can only contain letters, numbers and underscores."
+msgstr ""
+
+#: forms.py:45
+msgid "This username is already taken. Please choose another."
+msgstr ""
+
+#: forms.py:52 forms.py:211
+msgid "A user is registered with this email address."
+msgstr ""
+
+#: forms.py:57 forms.py:149 forms.py:185
+msgid "You must type the same password each time."
+msgstr ""
+
+#: forms.py:68
+msgid "Remember Me"
+msgstr ""
+
+#: forms.py:81
+msgid "This account is inactive."
+msgstr ""
+
+#: forms.py:93
+msgid "The username and/or password you specified are not correct."
+msgstr ""
+
+#: forms.py:108 forms.py:159 forms.py:191
+msgid "Email"
+msgstr ""
+
+#: forms.py:109
+msgid "The email address and/or password you specified are not correct."
+msgstr ""
+
+#: forms.py:125
+msgid "Current Password"
+msgstr ""
+
+#: forms.py:129 forms.py:174
+msgid "New Password"
+msgstr ""
+
+#: forms.py:133 forms.py:178
+msgid "New Password (again)"
+msgstr ""
+
+#: forms.py:143
+msgid "Please type your current password."
+msgstr ""
+
+#: forms.py:164
+msgid "Email address not verified for any user account"
+msgstr ""
+
+#: forms.py:167
+msgid "Email address not found for any user account"
+msgstr ""
+
+#: forms.py:193
+msgid "Timezone"
+msgstr ""
+
+#: forms.py:199
+msgid "Language"
+msgstr ""
+
+#: models.py:29
+msgid "user"
+msgstr ""
+
+#: models.py:30
+msgid "timezone"
+msgstr ""
+
+#: models.py:31
+msgid "language"
+msgstr ""
+
+#: models.py:209
+msgid "email address"
+msgstr ""
+
+#: models.py:210
+msgid "email addresses"
+msgstr ""
+
+#: models.py:246
+msgid "email confirmation"
+msgstr ""
+
+#: models.py:247
+msgid "email confirmations"
+msgstr ""
+
+#: views.py:36
+#, python-format
+msgid "Confirmation email sent to %(email)s."
+msgstr ""
+
+#: views.py:40
+#, python-format
+msgid "Successfully logged in as %(user)s."
+msgstr ""
+
+#: views.py:44
+#, python-format
+msgid "The code %(code)s is invalid."
+msgstr ""
+
+#: views.py:267
+#, python-format
+msgid "You have confirmed %(email)s."
+msgstr ""
+
+#: views.py:340 views.py:441
+msgid "Password successfully changed."
+msgstr ""
+
+#: views.py:502
+msgid "Account settings updated."
+msgstr ""
View
BIN account/locale/es/LC_MESSAGES/django.mo
Binary file not shown.
View
155 account/locale/es/LC_MESSAGES/django.po
@@ -0,0 +1,155 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Erik Rivera <erik.river@gmail.com>, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-user-accounts\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-05-15 13:38-0600\n"
+"PO-Revision-Date: 2012-05-15 18:11+0000\n"
+"Last-Translator: Erik Rivera <erik.river@gmail.com>\n"
+"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/pinax/language/es/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: es\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: forms.py:19 forms.py:92
+msgid "Username"
+msgstr "Nombre de usuario"
+
+#: forms.py:25 forms.py:64
+msgid "Password"
+msgstr "Contraseña"
+
+#: forms.py:29
+msgid "Password (again)"
+msgstr "Contraseña (repetir)"
+
+#: forms.py:41
+msgid "Usernames can only contain letters, numbers and underscores."
+msgstr "Los nombres de usuario solo pueden contener letras, números y subguiones"
+
+#: forms.py:45
+msgid "This username is already taken. Please choose another."
+msgstr "Este nombre de usuario ya está en uso. Por favor elija otro."
+
+#: forms.py:52 forms.py:211
+msgid "A user is registered with this email address."
+msgstr "Un usuario se ha registrado con esta dirección de correo electrónico."
+
+#: forms.py:57 forms.py:149 forms.py:185
+msgid "You must type the same password each time."
+msgstr "Debe escribir la misma contraseña cada vez."
+
+#: forms.py:68
+msgid "Remember Me"
+msgstr "Recordarme"
+
+#: forms.py:81
+msgid "This account is inactive."
+msgstr "Esta cuenta está inactiva."
+
+#: forms.py:93
+msgid "The username and/or password you specified are not correct."
+msgstr "El nombre de usuario y/o la contraseña que ha especificado no son correctas."
+
+#: forms.py:108 forms.py:159 forms.py:191
+msgid "Email"
+msgstr "Correo electrónico"
+
+#: forms.py:109
+msgid "The email address and/or password you specified are not correct."
+msgstr "La dirección de correo electrónico y/o la contraseña que ha especificado no son correctas."
+
+#: forms.py:125
+msgid "Current Password"
+msgstr "Contraseña actual"
+
+#: forms.py:129 forms.py:174
+msgid "New Password"
+msgstr "Contraseña nueva"
+
+#: forms.py:133 forms.py:178
+msgid "New Password (again)"
+msgstr "Contraseña nueva (repetir)"
+
+#: forms.py:143
+msgid "Please type your current password."
+msgstr "Por favor escriba su contraseña actual."
+
+#: forms.py:164
+msgid "Email address not verified for any user account"
+msgstr "Correo electrónico no verificado para ninguna cuenta de usuario."
+
+#: forms.py:167
+msgid "Email address not found for any user account"
+msgstr "Correo electrónico no encontrado para ninguna cuenta de usuario."
+
+#: forms.py:193
+msgid "Timezone"
+msgstr "Zona horaria"
+
+#: forms.py:199
+msgid "Language"
+msgstr "Idioma"
+
+#: models.py:29
+msgid "user"
+msgstr "usuario"
+
+#: models.py:30
+msgid "timezone"
+msgstr "zona horaria"
+
+#: models.py:31
+msgid "language"
+msgstr "idioma"
+
+#: models.py:209
+msgid "email address"
+msgstr "correo electrónico"
+
+#: models.py:210
+msgid "email addresses"
+msgstr "correos electrónicos"
+
+#: models.py:246
+msgid "email confirmation"
+msgstr "confirmación de correo electrónico"
+
+#: models.py:247
+msgid "email confirmations"
+msgstr "confirmaciones de correos electrónicos"
+
+#: views.py:36
+#, python-format
+msgid "Confirmation email sent to %(email)s."
+msgstr "La confirmación de correo electrónico se ha enviado a %(email)s."
+
+#: views.py:40
+#, python-format
+msgid "Successfully logged in as %(user)s."
+msgstr "Inicio la sesión satisfactoriamente como %(user)s."
+
+#: views.py:44
+#, python-format
+msgid "The code %(code)s is invalid."
+msgstr "El código %(code)s no es válido."
+
+#: views.py:267
+#, python-format
+msgid "You have confirmed %(email)s."
+msgstr "Has confirmado %(email)s."
+
+#: views.py:340 views.py:441
+msgid "Password successfully changed."
+msgstr "La contraseña se ha cambiado con éxito."
+
+#: views.py:502
+msgid "Account settings updated."
+msgstr "Los ajustes de la cuenta actualizados."
View
BIN account/locale/it/LC_MESSAGES/django.mo
Binary file not shown.
View
154 account/locale/it/LC_MESSAGES/django.po
@@ -0,0 +1,154 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: django-user-accounts\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-05-15 13:38-0600\n"
+"PO-Revision-Date: 2012-05-15 19:23+0000\n"
+"Last-Translator: Massimiliano Ravelli <massimiliano.ravelli@gmail.com>\n"
+"Language-Team: Italian (http://www.transifex.net/projects/p/pinax/language/it/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: it\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: forms.py:19 forms.py:92
+msgid "Username"
+msgstr "Nome utente"
+
+#: forms.py:25 forms.py:64
+msgid "Password"
+msgstr "Password"
+
+#: forms.py:29
+msgid "Password (again)"
+msgstr "Password (di nuovo)"
+
+#: forms.py:41
+msgid "Usernames can only contain letters, numbers and underscores."
+msgstr "Il nome utente può contenere solo lettere, numeri e trattini bassi."
+
+#: forms.py:45
+msgid "This username is already taken. Please choose another."
+msgstr "Questo nome utente è già utilizzato. Scegline un'altro."
+
+#: forms.py:52 forms.py:211
+msgid "A user is registered with this email address."
+msgstr "Esiste già un utente con questo indirizzo email."
+
+#: forms.py:57 forms.py:149 forms.py:185
+msgid "You must type the same password each time."
+msgstr "La password deve essere inserita due volte."
+
+#: forms.py:68
+msgid "Remember Me"
+msgstr "Ricordami"
+
+#: forms.py:81
+msgid "This account is inactive."
+msgstr "Questo account non è attivo."
+
+#: forms.py:93
+msgid "The username and/or password you specified are not correct."
+msgstr "Il nome utente e/o la password non sono corretti."
+
+#: forms.py:108 forms.py:159 forms.py:191
+msgid "Email"
+msgstr "Email"
+
+#: forms.py:109
+msgid "The email address and/or password you specified are not correct."
+msgstr "L'indirizzo email e/o la password non sono corretti."
+
+#: forms.py:125
+msgid "Current Password"
+msgstr "Password Attuale"
+
+#: forms.py:129 forms.py:174
+msgid "New Password"
+msgstr "Nuova Password"
+
+#: forms.py:133 forms.py:178
+msgid "New Password (again)"
+msgstr "Nuova Password (di nuovo)"
+
+#: forms.py:143
+msgid "Please type your current password."
+msgstr "Inserisci la password attuale."
+
+#: forms.py:164
+msgid "Email address not verified for any user account"
+msgstr "Indirizzo email non verificato"
+
+#: forms.py:167
+msgid "Email address not found for any user account"
+msgstr "Indirizzo email non trovato"
+
+#: forms.py:193
+msgid "Timezone"
+msgstr "Fuso orario"
+
+#: forms.py:199
+msgid "Language"
+msgstr "Lingua"
+
+#: models.py:29
+msgid "user"
+msgstr "utente"
+
+#: models.py:30
+msgid "timezone"
+msgstr "fuso orario"
+
+#: models.py:31
+msgid "language"
+msgstr "lingua"
+
+#: models.py:209
+msgid "email address"
+msgstr "indirizzo email"
+
+#: models.py:210
+msgid "email addresses"
+msgstr "indirizzi email"
+
+#: models.py:246
+msgid "email confirmation"
+msgstr "conferma indirizzo email"
+
+#: models.py:247
+msgid "email confirmations"
+msgstr "conferme indirizzi email"
+
+#: views.py:36
+#, python-format
+msgid "Confirmation email sent to %(email)s."
+msgstr "Email di conferma inviata a %(email)s."
+
+#: views.py:40
+#, python-format
+msgid "Successfully logged in as %(user)s."
+msgstr "Accesso effettuato come %(user)s."
+
+#: views.py:44
+#, python-format
+msgid "The code %(code)s is invalid."
+msgstr "Il codice %(code)s non è valido."
+
+#: views.py:267
+#, python-format
+msgid "You have confirmed %(email)s."
+msgstr "Hai confermato %(email)s."
+
+#: views.py:340 views.py:441
+msgid "Password successfully changed."
+msgstr "Password cambiata con successo."
+
+#: views.py:502
+msgid "Account settings updated."
+msgstr "Configurazione account aggiornata."
View
5 account/managers.py
@@ -1,5 +1,7 @@
from django.db import models, IntegrityError
+from account.conf import settings
+
class EmailAddressManager(models.Manager):
@@ -9,7 +11,8 @@ def add_email(self, user, email, **kwargs):
except IntegrityError:
return None
else:
- email_address.send_confirmation()
+ if settings.ACCOUNT_EMAIL_CONFIRMATION_EMAIL:
+ email_address.send_confirmation()
return email_address
def get_primary(self, user):
View
0 middleware.py → account/middleware.py
File renamed without changes.
View
53 account/models.py
@@ -7,14 +7,15 @@
from django.db import models
from django.db.models import Q
from django.db.models.signals import post_save
-from django.dispatch import receiver
from django.template.loader import render_to_string
-from django.utils import timezone
-from django.utils.translation import get_language_from_request, gettext_lazy as _
+from django.utils import timezone, translation
+from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User, AnonymousUser
from django.contrib.sites.models import Site
+import pytz
+
from account import signals
from account.conf import settings
from account.fields import TimeZoneField
@@ -26,12 +27,11 @@
class Account(models.Model):
user = models.OneToOneField(User, related_name="account", verbose_name=_("user"))
-
timezone = TimeZoneField(_("timezone"))
language = models.CharField(_("language"),
- max_length = 10,
- choices = settings.ACCOUNT_LANGUAGES,
- default = settings.LANGUAGE_CODE
+ max_length=10,
+ choices=settings.ACCOUNT_LANGUAGES,
+ default=settings.LANGUAGE_CODE
)
@classmethod
@@ -45,19 +45,38 @@ def for_request(cls, request):
account = AnonymousAccount(request)
return account
+ @classmethod
+ def create(cls, request=None, **kwargs):
+ account = cls(**kwargs)
+ if "language" not in kwargs:
+ if request is None:
+ account.language = settings.LANGUAGE_CODE
+ else:
+ account.language = translation.get_language_from_request(request, check_path=True)
+ account.save()
+ return account
+
def __unicode__(self):
return self.user.username
+
+ def now(self):
+ """
+ Returns a timezone aware datetime localized to the account's timezone.
+ """
+ naive = datetime.datetime.now()
+ aware = naive.replace(tzinfo=pytz.timezone(settings.TIME_ZONE))
+ return aware.astimezone(pytz.timezone(self.timezone))
class AnonymousAccount(object):
def __init__(self, request=None):
self.user = AnonymousUser()
self.timezone = settings.TIME_ZONE
- if request is not None:
- self.language = get_language_from_request(request)
- else:
+ if request is None:
self.language = settings.LANGUAGE_CODE
+ else:
+ self.language = translation.get_language_from_request(request, check_path=True)
def __unicode__(self):
return "AnonymousAccount"
@@ -144,9 +163,9 @@ def use(self, user):
result.save()
signup_code_used.send(sender=result.__class__, signup_code_result=result)
- def send(self):
+ def send(self, **kwargs):
protocol = getattr(settings, "DEFAULT_HTTP_PROTOCOL", "http")
- current_site = Site.objects.get_current()
+ current_site = kwargs["site"] if "site" in kwargs else Site.objects.get_current()
signup_url = u"%s://%s%s?%s" % (
protocol,
unicode(current_site.domain),
@@ -249,8 +268,8 @@ def confirm(self):
signals.email_confirmed.send(sender=self.__class__, email_address=email_address)
return email_address
- def send(self):
- current_site = Site.objects.get_current()
+ def send(self, **kwargs):
+ current_site = kwargs["site"] if "site" in kwargs else Site.objects.get_current()
protocol = getattr(settings, "DEFAULT_HTTP_PROTOCOL", "http")
activate_url = u"%s://%s%s" % (
protocol,
@@ -270,9 +289,3 @@ def send(self):
self.sent = timezone.now()
self.save()
signals.email_confirmation_sent.send(sender=self.__class__, confirmation=self)
-
-
-@receiver(post_save, sender=User)
-def create_account(sender, **kwargs):
- if kwargs["created"]:
- Account.objects.create(user=kwargs["instance"])
View
2 account/signals.py
@@ -1,7 +1,7 @@
import django.dispatch
-user_signed_up = django.dispatch.Signal(providing_args=["user"])
+user_signed_up = django.dispatch.Signal(providing_args=["user", "form"])
user_sign_up_attempt = django.dispatch.Signal(providing_args=["username", "email", "result"])
signup_code_sent = django.dispatch.Signal(providing_args=["signup_code"])
signup_code_used = django.dispatch.Signal(providing_args=["signup_code_result"])
View
36 account/tests/test_views.py
@@ -2,17 +2,23 @@
from django.test.client import RequestFactory
from django.utils import unittest
-from django.contrib.auth.models import AnonymousUser
+from django.contrib.auth.models import AnonymousUser, User
from account.views import SignupView, LoginView
-class SignupDisabledView(SignupView):
+class SignupEnabledView(SignupView):
- def disabled(self):
+ def is_open(self):
return True
+class SignupDisabledView(SignupView):
+
+ def is_open(self):
+ return False
+
+
class LoginDisabledView(LoginView):
def disabled(self):
@@ -26,18 +32,33 @@ def setUp(self):
def test_get(self):
request = self.factory.get(reverse("account_signup"))
- response = SignupView.as_view()(request)
+ request.user = AnonymousUser()
+ response = SignupEnabledView.as_view()(request)
self.assertEqual(response.status_code, 200)
def test_get_disabled(self):
request = self.factory.get(reverse("account_signup"))
+ request.user = AnonymousUser()
response = SignupDisabledView.as_view()(request)
- self.assertEqual(response.status_code, 404)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.template_name, "account/signup_closed.html")
def test_post_disabled(self):
request = self.factory.post(reverse("account_signup"))
+ request.user = AnonymousUser()
response = SignupDisabledView.as_view()(request)
- self.assertEqual(response.status_code, 404)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.template_name, "account/signup_closed.html")
+
+ def test_post_successful(self):
+ post = {"username": "user", "password": "pwd",
+ "password_confirm": "pwd", "email": "info@example.com"}
+ request = self.factory.post(reverse("account_signup"), post)
+ request.user = AnonymousUser()
+ response = SignupEnabledView.as_view()(request)
+ self.assertEqual(response.status_code, 302)
+ user = User.objects.get(username="user")
+ self.asserEqual(user.email, "info@example.com")
class LoginViewTestCase(unittest.TestCase):
@@ -50,7 +71,8 @@ def test_get(self):
request.user = AnonymousUser()
response = LoginView.as_view()(request)
self.assertEqual(response.status_code, 200)
-
+ self.assertEqual(response.template_name, ["account/login.html"])
+
def test_get_disabled(self):
request = self.factory.get(reverse("account_login"))
request.user = AnonymousUser()
View
99 account/views.py
@@ -5,7 +5,7 @@
from django.template.loader import render_to_string
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
-from django.views.generic.base import TemplateResponseMixin, View, TemplateView
+from django.views.generic.base import TemplateResponseMixin, View
from django.views.generic.edit import FormView
from django.contrib import auth, messages
@@ -33,15 +33,15 @@ class SignupView(FormView):
messages = {
"email_confirmation_sent": {
"level": messages.INFO,
- "text": _("Confirmation email sent to %(email)s")
+ "text": _("Confirmation email sent to %(email)s.")
},
"logged_in": {
"level": messages.SUCCESS,
- "text": _("Successfully logged in as %(user)s")
+ "text": _("Successfully logged in as %(user)s.")
},
"invalid_signup_code": {
"level": messages.WARNING,
- "text": _("The code %(code)s is invalid")
+ "text": _("The code %(code)s is invalid.")
}
}
@@ -56,10 +56,17 @@ def get(self, *args, **kwargs):
return self.closed()
return super(SignupView, self).get(*args, **kwargs)
+ def post(self, *args, **kwargs):
+ if not self.is_open():
+ return self.closed()
+ return super(SignupView, self).post(*args, **kwargs)
+
def get_initial(self):
initial = super(SignupView, self).get_initial()
if self.signup_code:
initial["code"] = self.signup_code.code
+ if self.signup_code.email:
+ initial["email"] = self.signup_code.email
return initial
def get_context_data(self, **kwargs):
@@ -86,20 +93,21 @@ def form_valid(self, form):
if settings.ACCOUNT_EMAIL_CONFIRMATION_REQUIRED:
new_user.is_active = False
new_user.save()
+ self.create_account(new_user, form)
email_kwargs = {"primary": True}
if self.signup_code:
self.signup_code.use(new_user)
- if self.signup_code.email and form.cleaned_data["email"] == self.signup_code.email:
+ if self.signup_code.email and new_user.email == self.signup_code.email:
email_kwargs["verified"] = True
email_confirmed = True
- EmailAddress.objects.add_email(new_user, form.cleaned_data["email"], **email_kwargs)
+ EmailAddress.objects.add_email(new_user, new_user.email, **email_kwargs)
self.after_signup(new_user, form)
if settings.ACCOUNT_EMAIL_CONFIRMATION_REQUIRED and not email_confirmed:
response_kwargs = {
"request": self.request,
"template": self.template_name_email_confirmation_sent,
"context": {
- "email": form.cleaned_data["email"],
+ "email": new_user.email,
"success_url": self.get_success_url(),
}
}
@@ -122,7 +130,7 @@ def form_valid(self, form):
"user": user_display(new_user)
}
)
- return super(SignupView, self).form_valid(form)
+ return redirect(self.get_success_url())
def get_success_url(self):
return default_redirect(self.request, settings.ACCOUNT_SIGNUP_REDIRECT_URL)
@@ -136,8 +144,8 @@ def create_user(self, form, commit=True, **kwargs):
if username is None:
username = self.generate_username(form)
user.username = username
- user.email = form.cleaned_data["email"].strip().lower()
- password = form.cleaned_data.get("password1")
+ user.email = form.cleaned_data["email"].strip()
+ password = form.cleaned_data.get("password")
if password:
user.set_password(password)
else:
@@ -146,12 +154,15 @@ def create_user(self, form, commit=True, **kwargs):
user.save()
return user
+ def create_account(self, new_user, form):
+ return Account.create(request=self.request, user=new_user)
+
def generate_username(self, form):
raise NotImplementedError("Unable to generate username by default. "
"Override SignupView.generate_username in a subclass.")
def after_signup(self, user, form):
- signals.user_signed_up.send(sender=SignupForm, user=user)
+ signals.user_signed_up.send(sender=SignupForm, user=user, form=form)
def login_user(self, user):
# set backend on User object to bypass needing to call auth.authenticate
@@ -222,9 +233,8 @@ def get_redirect_field_name(self):
def login_user(self, form):
auth.login(self.request, form.user)
- self.request.session.set_expiry(
- 60*60*24*365*10 if form.cleaned_data.get("remember") else 0
- )
+ expiry = settings.ACCOUNT_REMEMBER_ME_EXPIRY if form.cleaned_data.get("remember") else 0
+ self.request.session.set_expiry(expiry)
class LogoutView(TemplateResponseMixin, View):
@@ -254,7 +264,7 @@ class ConfirmEmailView(TemplateResponseMixin, View):
messages = {
"email_confirmed": {
"level": messages.SUCCESS,
- "text": _("You have confirmed %(email)s")
+ "text": _("You have confirmed %(email)s.")
}
}
@@ -273,8 +283,6 @@ def get(self, *args, **kwargs):
def post(self, *args, **kwargs):
self.object = confirmation = self.get_object()
- if confirmation.email_address.user != self.request.user:
- raise Http404()
confirmation.confirm()
user = confirmation.email_address.user
user.is_active = True
@@ -450,7 +458,7 @@ def get_context_data(self, **kwargs):
def form_valid(self, form):
user = self.get_user()
- user.set_password(form.cleaned_data["password1"])
+ user.set_password(form.cleaned_data["password"])
user.save()
if self.messages.get("password_changed"):
messages.add_message(
@@ -504,24 +512,12 @@ def get_initial(self):
initial = super(SettingsView, self).get_initial()
if self.primary_email_address:
initial["email"] = self.primary_email_address.email
- initial["timezone"] = self.request.user.account.timezone
- initial["language"] = self.request.user.account.language
+ initial["timezone"] = self.request.user.account.timezone
+ initial["language"] = self.request.user.account.language
return initial
def form_valid(self, form):
- # @@@ handle multiple emails per user
- if not self.primary_email_address:
- EmailAddress.objects.add_email(self.request.user, form.cleaned_data["email"], primary=True)
- else:
- if form.cleaned_data["email"] != self.primary_email_address.email:
- email_address = EmailAddress.objects.add_email(self.request.user, form.cleaned_data["email"])
- email_address.set_as_primary()
-
- account = self.request.user.account
- account.timezone = form.cleaned_data["timezone"]
- account.language = form.cleaned_data["language"]
- account.save()
-
+ self.update_settings(form)
if self.messages.get("settings_updated"):
messages.add_message(
self.request,
@@ -530,5 +526,38 @@ def form_valid(self, form):
)
return redirect(self.get_success_url())
- def get_success_url(self):
- return default_redirect(self.request, settings.ACCOUNT_SETTINGS_REDIRECT_URL)
+ def update_settings(self, form):
+ self.update_email(form)
+ self.update_account(form)
+
+ def update_email(self, form):
+ user = self.request.user
+ # @@@ handle multiple emails per user
+ email = form.cleaned_data["email"].strip()
+ if not self.primary_email_address:
+ user.email = email
+ EmailAddress.objects.add_email(self.request.user, email, primary=True)
+ user.save()
+ else:
+ if email != self.primary_email_address.email:
+ user.email = email
+ self.primary_email_address.email = email
+ user.save()
+ self.primary_email_address.save()
+
+ def update_account(self, form):
+ fields = {}
+ if "timezone" in form.cleaned_data:
+ fields["timezone"] = form.cleaned_data["timezone"]
+ if "language" in form.cleaned_data:
+ fields["language"] = form.cleaned_data["language"]
+ if fields:
+ account = self.request.user.account
+ for k, v in fields.iteritems():
+ setattr(account, k, v)
+ account.save()
+
+ def get_success_url(self, fallback_url=None):
+ if fallback_url is None:
+ fallback_url = settings.ACCOUNT_SETTINGS_REDIRECT_URL
+ return default_redirect(self.request, fallback_url)
View
14 docs/installation.rst
@@ -4,9 +4,9 @@
Installation
============
-* To install django-user-accounts (no releases have been yet)::
+* Install the development version::
- pip install django-user-accounts
+ pip install --extra-index-url=http://dist.pinaxproject.com/dev/ django-user-accounts
* Add ``account`` to your ``INSTALLED_APPS`` setting::
@@ -53,4 +53,12 @@ django-appconf_
We use django-appconf for app settings. It is listed in ``install_requires``
and will be installed when pip installs.
-.. _django-appconf: https://github.com/jezdez/django-appconf
+.. _django-appconf: https://github.com/jezdez/django-appconf
+
+pytz_
+-----
+
+pytz is used for handling timezones for accounts. This dependency is critical
+due to its extensive dataset for timezones.
+
+.. _pytz: http://pypi.python.org/pypi/pytz/
View
4 docs/usage.rst
@@ -198,6 +198,6 @@ tables for django-user-accounts you will need to migrate the
ALTER TABLE "account_emailaddress" ADD CONSTRAINT "account_emailaddress_user_id_email_key" UNIQUE ("user_id", "email");
ALTER TABLE "account_emailaddress" DROP CONSTRAINT "account_emailaddress_email_key";
-``ACCOUNT_EMAIL_UNIQUE = False`` will prevent duplicate email addresses per
-user.
+``ACCOUNT_EMAIL_UNIQUE = False`` will allow duplicate email addresses per
+user, but not across users.
View
4 setup.py
@@ -3,7 +3,7 @@
setup(
name = "django-user-accounts",
- version = "1.0b1.dev1",
+ version = "1.0b1.dev4",
author = "Brian Rosner",
author_email = "brosner@gmail.com",
description = "a Django user account app",
@@ -13,7 +13,7 @@
packages = find_packages(),
install_requires = [
"django-appconf==0.5",
- "pytz==2012b"
+ "pytz==2012c"
],
classifiers = [
"Development Status :: 4 - Beta",

0 comments on commit b368472

Please sign in to comment.
Something went wrong with that request. Please try again.