Factor create_account param validation into a form
This commit is contained in:
@@ -2,16 +2,23 @@
|
||||
Utility functions for validating forms
|
||||
"""
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.forms import PasswordResetForm
|
||||
from django.contrib.auth.hashers import UNUSABLE_PASSWORD
|
||||
from django.contrib.auth.tokens import default_token_generator
|
||||
|
||||
from django.utils.http import int_to_base36
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.template import loader
|
||||
|
||||
from django.conf import settings
|
||||
from microsite_configuration import microsite
|
||||
from util.password_policy_validators import (
|
||||
validate_password_length,
|
||||
validate_password_complexity,
|
||||
validate_password_dictionary,
|
||||
)
|
||||
|
||||
|
||||
class PasswordResetFormNoActive(PasswordResetForm):
|
||||
@@ -70,3 +77,160 @@ class PasswordResetFormNoActive(PasswordResetForm):
|
||||
subject = subject.replace('\n', '')
|
||||
email = loader.render_to_string(email_template_name, context)
|
||||
send_mail(subject, email, from_email, [user.email])
|
||||
|
||||
|
||||
class TrueField(forms.BooleanField):
|
||||
"""
|
||||
A boolean field that only accepts "true" (case-insensitive) as true
|
||||
"""
|
||||
def to_python(self, value):
|
||||
# CheckboxInput converts string to bool by case-insensitive match to "true" or "false"
|
||||
if value is True:
|
||||
return value
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
_USERNAME_TOO_SHORT_MSG = _("Username must be minimum of two characters long")
|
||||
_EMAIL_INVALID_MSG = _("A properly formatted e-mail is required")
|
||||
_PASSWORD_INVALID_MSG = _("A valid password is required")
|
||||
_NAME_TOO_SHORT_MSG = _("Your legal name must be a minimum of two characters long")
|
||||
|
||||
|
||||
class AccountCreationForm(forms.Form):
|
||||
"""
|
||||
A form to for account creation data. It is currently only used for
|
||||
validation, not rendering.
|
||||
"""
|
||||
# TODO: Resolve repetition
|
||||
username = forms.SlugField(
|
||||
min_length=2,
|
||||
max_length=30,
|
||||
error_messages={
|
||||
"required": _USERNAME_TOO_SHORT_MSG,
|
||||
"invalid": _("Username should only consist of A-Z and 0-9, with no spaces."),
|
||||
"min_length": _USERNAME_TOO_SHORT_MSG,
|
||||
"max_length": _("Username cannot be more than %(limit_value)s characters long"),
|
||||
}
|
||||
)
|
||||
email = forms.EmailField(
|
||||
max_length=75, # Limit per RFCs is 254, but User's email field in django 1.4 only takes 75
|
||||
error_messages={
|
||||
"required": _EMAIL_INVALID_MSG,
|
||||
"invalid": _EMAIL_INVALID_MSG,
|
||||
"max_length": _("Email cannot be more than %(limit_value)s characters long"),
|
||||
}
|
||||
)
|
||||
password = forms.CharField(
|
||||
min_length=2,
|
||||
error_messages={
|
||||
"required": _PASSWORD_INVALID_MSG,
|
||||
"min_length": _PASSWORD_INVALID_MSG,
|
||||
}
|
||||
)
|
||||
name = forms.CharField(
|
||||
min_length=2,
|
||||
error_messages={
|
||||
"required": _NAME_TOO_SHORT_MSG,
|
||||
"min_length": _NAME_TOO_SHORT_MSG,
|
||||
}
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data=None,
|
||||
extra_fields=None,
|
||||
extended_profile_fields=None,
|
||||
enforce_username_neq_password=False,
|
||||
enforce_password_policy=False,
|
||||
tos_required=True
|
||||
):
|
||||
super(AccountCreationForm, self).__init__(data)
|
||||
|
||||
extra_fields = extra_fields or {}
|
||||
self.extended_profile_fields = extended_profile_fields or {}
|
||||
self.enforce_username_neq_password = enforce_username_neq_password
|
||||
self.enforce_password_policy = enforce_password_policy
|
||||
if tos_required:
|
||||
self.fields["terms_of_service"] = TrueField(
|
||||
error_messages={"required": _("You must accept the terms of service.")}
|
||||
)
|
||||
|
||||
# TODO: These messages don't say anything about minimum length
|
||||
error_message_dict = {
|
||||
"level_of_education": _("A level of education is required"),
|
||||
"gender": _("Your gender is required"),
|
||||
"year_of_birth": _("Your year of birth is required"),
|
||||
"mailing_address": _("Your mailing address is required"),
|
||||
"goals": _("A description of your goals is required"),
|
||||
"city": _("A city is required"),
|
||||
"country": _("A country is required")
|
||||
}
|
||||
for field_name, field_value in extra_fields.items():
|
||||
if field_name not in self.fields:
|
||||
if field_name == "honor_code":
|
||||
if field_value == "required":
|
||||
self.fields[field_name] = TrueField(
|
||||
error_messages={
|
||||
"required": _("To enroll, you must follow the honor code.")
|
||||
}
|
||||
)
|
||||
else:
|
||||
required = field_value == "required"
|
||||
min_length = 1 if field_name in ("gender", "level_of_education") else 2
|
||||
error_message = error_message_dict.get(
|
||||
field_name,
|
||||
_("You are missing one or more required fields")
|
||||
)
|
||||
self.fields[field_name] = forms.CharField(
|
||||
required=required,
|
||||
min_length=min_length,
|
||||
error_messages={
|
||||
"required": error_message,
|
||||
"min_length": error_message,
|
||||
}
|
||||
)
|
||||
|
||||
for field in self.extended_profile_fields:
|
||||
if field not in self.fields:
|
||||
self.fields[field] = forms.CharField(required=False)
|
||||
|
||||
def clean_password(self):
|
||||
"""Enforce password policies (if applicable)"""
|
||||
password = self.cleaned_data["password"]
|
||||
if (
|
||||
self.enforce_username_neq_password and
|
||||
"username" in self.cleaned_data and
|
||||
self.cleaned_data["username"] == password
|
||||
):
|
||||
raise ValidationError(_("Username and password fields cannot match"))
|
||||
if self.enforce_password_policy:
|
||||
try:
|
||||
validate_password_length(password)
|
||||
validate_password_complexity(password)
|
||||
validate_password_dictionary(password)
|
||||
except ValidationError, err:
|
||||
raise ValidationError(_("Password: ") + "; ".join(err.messages))
|
||||
return password
|
||||
|
||||
def clean_year_of_birth(self):
|
||||
"""
|
||||
Parse year_of_birth to an integer, but just use None instead of raising
|
||||
an error if it is malformed
|
||||
"""
|
||||
try:
|
||||
year_str = self.cleaned_data["year_of_birth"]
|
||||
return int(year_str) if year_str is not None else None
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def cleaned_extended_profile(self):
|
||||
"""
|
||||
Return a dictionary containing the extended_profile_fields and values
|
||||
"""
|
||||
return {
|
||||
key: value
|
||||
for key, value in self.cleaned_data.items()
|
||||
if key in self.extended_profile_fields and value is not None
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user