Source code for mezzanine.accounts.forms
from __future__ import unicode_literals
from django.contrib.auth import authenticate, get_user_model
from django.contrib.auth.tokens import default_token_generator
from django.db.models import Q
from django.db.models.manager import Manager
from django import forms
from django.utils.http import int_to_base36
from django.utils.translation import ugettext, ugettext_lazy as _
from mezzanine.accounts import (get_profile_model, get_profile_user_fieldname,
get_profile_for_user, ProfileNotConfigured)
from mezzanine.conf import settings
from mezzanine.core.forms import Html5Mixin
from mezzanine.utils.urls import slugify, unique_slug
User = get_user_model()
_exclude_fields = tuple(getattr(settings,
"ACCOUNTS_PROFILE_FORM_EXCLUDE_FIELDS", ()))
# If a profile model has been configured with the ``ACCOUNTS_PROFILE_MODEL``
# setting, create a model form for it that will have its fields added to
# ``ProfileForm``.
try:
class ProfileFieldsForm(forms.ModelForm):
class Meta:
model = get_profile_model()
exclude = (get_profile_user_fieldname(),) + _exclude_fields
except ProfileNotConfigured:
pass
if settings.ACCOUNTS_NO_USERNAME:
_exclude_fields += ("username",)
username_label = _("Email address")
else:
username_label = _("Username or email address")
[docs]class LoginForm(Html5Mixin, forms.Form):
"""
Fields for login.
"""
username = forms.CharField(label=username_label)
password = forms.CharField(label=_("Password"),
widget=forms.PasswordInput(render_value=False))
[docs] def clean(self):
"""
Authenticate the given username/email and password. If the fields
are valid, store the authenticated user for returning via save().
"""
username = self.cleaned_data.get("username")
password = self.cleaned_data.get("password")
self._user = authenticate(username=username, password=password)
if self._user is None:
raise forms.ValidationError(
ugettext("Invalid username/email and password"))
elif not self._user.is_active:
raise forms.ValidationError(ugettext("Your account is inactive"))
return self.cleaned_data
[docs] def save(self):
"""
Just return the authenticated user - used for logging in.
"""
return getattr(self, "_user", None)
[docs]class ProfileForm(Html5Mixin, forms.ModelForm):
"""
ModelForm for auth.User - used for signup and profile update.
If a Profile model is defined via ``ACCOUNTS_PROFILE_MODEL``, its
fields are injected into the form.
"""
password1 = forms.CharField(label=_("Password"),
widget=forms.PasswordInput(render_value=False))
password2 = forms.CharField(label=_("Password (again)"),
widget=forms.PasswordInput(render_value=False))
class Meta:
model = User
fields = ("first_name", "last_name", "email", "username")
exclude = _exclude_fields
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
self._signup = self.instance.id is None
user_fields = set([f.name for f in User._meta.get_fields()])
try:
self.fields["username"].help_text = ugettext(
"Only letters, numbers, dashes or underscores please")
except KeyError:
pass
for field in self.fields:
# Make user fields required.
if field in user_fields:
self.fields[field].required = True
# Disable auto-complete for password fields.
# Password isn't required for profile update.
if field.startswith("password"):
self.fields[field].widget.attrs["autocomplete"] = "off"
self.fields[field].widget.attrs.pop("required", "")
if not self._signup:
self.fields[field].required = False
if field == "password1":
self.fields[field].help_text = ugettext(
"Leave blank unless you want "
"to change your password")
# Add any profile fields to the form.
try:
profile_fields_form = self.get_profile_fields_form()
profile_fields = profile_fields_form().fields
self.fields.update(profile_fields)
if not self._signup:
user_profile = get_profile_for_user(self.instance)
for field in profile_fields:
value = getattr(user_profile, field)
# Check for multiple initial values, i.e. a m2m field
if isinstance(value, Manager):
value = value.all()
self.initial[field] = value
except ProfileNotConfigured:
pass
[docs] def clean_username(self):
"""
Ensure the username doesn't exist or contain invalid chars.
We limit it to slugifiable chars since it's used as the slug
for the user's profile view.
"""
username = self.cleaned_data.get("username")
if username.lower() != slugify(username).lower():
raise forms.ValidationError(
ugettext("Username can only contain letters, numbers, dashes "
"or underscores."))
lookup = {"username__iexact": username}
try:
User.objects.exclude(id=self.instance.id).get(**lookup)
except User.DoesNotExist:
return username
raise forms.ValidationError(
ugettext("This username is already registered"))
[docs] def clean_password2(self):
"""
Ensure the password fields are equal, and match the minimum
length defined by ``ACCOUNTS_MIN_PASSWORD_LENGTH``.
"""
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1:
errors = []
if password1 != password2:
errors.append(ugettext("Passwords do not match"))
if len(password1) < settings.ACCOUNTS_MIN_PASSWORD_LENGTH:
errors.append(
ugettext("Password must be at least %s characters") %
settings.ACCOUNTS_MIN_PASSWORD_LENGTH)
if errors:
self._errors["password1"] = self.error_class(errors)
return password2
[docs] def clean_email(self):
"""
Ensure the email address is not already registered.
"""
email = self.cleaned_data.get("email")
qs = User.objects.exclude(id=self.instance.id).filter(email=email)
if len(qs) == 0:
return email
raise forms.ValidationError(
ugettext("This email is already registered"))
[docs] def save(self, *args, **kwargs):
"""
Create the new user. If no username is supplied (may be hidden
via ``ACCOUNTS_PROFILE_FORM_EXCLUDE_FIELDS`` or
``ACCOUNTS_NO_USERNAME``), we generate a unique username, so
that if profile pages are enabled, we still have something to
use as the profile's slug.
"""
kwargs["commit"] = False
user = super(ProfileForm, self).save(*args, **kwargs)
try:
self.cleaned_data["username"]
except KeyError:
if not self.instance.username:
try:
username = ("%(first_name)s %(last_name)s" %
self.cleaned_data).strip()
except KeyError:
username = ""
if not username:
username = self.cleaned_data["email"].split("@")[0]
qs = User.objects.exclude(id=self.instance.id)
user.username = unique_slug(qs, "username", slugify(username))
password = self.cleaned_data.get("password1")
if password:
user.set_password(password)
elif self._signup:
try:
user.set_unusable_password()
except AttributeError:
# This could happen if using a custom user model that
# doesn't inherit from Django's AbstractBaseUser.
pass
user.save()
try:
profile = get_profile_for_user(user)
profile_form = self.get_profile_fields_form()
profile_form(self.data, self.files, instance=profile).save()
except ProfileNotConfigured:
pass
if self._signup:
if (settings.ACCOUNTS_VERIFICATION_REQUIRED or
settings.ACCOUNTS_APPROVAL_REQUIRED):
user.is_active = False
user.save()
else:
token = default_token_generator.make_token(user)
user = authenticate(uidb36=int_to_base36(user.id),
token=token,
is_active=True)
return user
def get_profile_fields_form(self):
try:
return ProfileFieldsForm
except NameError:
raise ProfileNotConfigured
[docs]class PasswordResetForm(Html5Mixin, forms.Form):
"""
Validates the user's username or email for sending a login
token for authenticating to change their password.
"""
username = forms.CharField(label=username_label)
def clean(self):
username = self.cleaned_data.get("username")
username_or_email = Q(username=username) | Q(email=username)
try:
user = User.objects.get(username_or_email, is_active=True)
except User.DoesNotExist:
raise forms.ValidationError(
ugettext("Invalid username/email"))
else:
self._user = user
return self.cleaned_data
[docs] def save(self):
"""
Just return the authenticated user - used for sending login
email.
"""
return getattr(self, "_user", None)