forms.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. from __future__ import unicode_literals
  2. from django.contrib.auth import authenticate, get_user_model
  3. from django.contrib.auth.tokens import default_token_generator
  4. from django.db.models import Q
  5. from django.db.models.manager import Manager
  6. from django import forms
  7. from django.utils.http import int_to_base36
  8. from django.utils.translation import ugettext, ugettext_lazy as _
  9. from mezzanine.accounts import (get_profile_model, get_profile_user_fieldname,
  10. get_profile_for_user, ProfileNotConfigured)
  11. from mezzanine.conf import settings
  12. from mezzanine.core.forms import Html5Mixin
  13. from mezzanine.utils.urls import slugify, unique_slug
  14. User = get_user_model()
  15. _exclude_fields = tuple(getattr(settings,
  16. "ACCOUNTS_PROFILE_FORM_EXCLUDE_FIELDS", ()))
  17. # If a profile model has been configured with the ``ACCOUNTS_PROFILE_MODEL``
  18. # setting, create a model form for it that will have its fields added to
  19. # ``ProfileForm``.
  20. try:
  21. class ProfileFieldsForm(forms.ModelForm):
  22. class Meta:
  23. model = get_profile_model()
  24. exclude = (get_profile_user_fieldname(),) + _exclude_fields
  25. except ProfileNotConfigured:
  26. pass
  27. if settings.ACCOUNTS_NO_USERNAME:
  28. _exclude_fields += ("username")
  29. username_label = _("Email address")
  30. else:
  31. #username_label = _("Username or email address")
  32. username_label = _("C.I. para estudiantes, (docentes ver mas abajo)")
  33. class LoginForm(Html5Mixin, forms.Form):
  34. """
  35. Fields for login.
  36. """
  37. username = forms.CharField(label=username_label)
  38. password = forms.CharField(label=_("Password:"),
  39. widget=forms.PasswordInput(render_value=False))
  40. def clean(self):
  41. """
  42. Authenticate the given username/email and password. If the fields
  43. are valid, store the authenticated user for returning via save().
  44. """
  45. username = self.cleaned_data.get("username")
  46. password = self.cleaned_data.get("password")
  47. self._user = authenticate(username=username, password=password)
  48. if self._user is None:
  49. raise forms.ValidationError(
  50. ugettext("Invalid username/email and password"))
  51. elif not self._user.is_active:
  52. raise forms.ValidationError(ugettext("Your account is inactive"))
  53. return self.cleaned_data
  54. def save(self):
  55. """
  56. Just return the authenticated user - used for logging in.
  57. """
  58. return getattr(self, "_user", None)
  59. class ProfileForm(Html5Mixin, forms.ModelForm):
  60. """
  61. ModelForm for auth.User - used for signup and profile update.
  62. If a Profile model is defined via ``ACCOUNTS_PROFILE_MODEL``, its
  63. fields are injected into the form.
  64. """
  65. password1 = forms.CharField(label=_("Password"),
  66. widget=forms.PasswordInput(render_value=False))
  67. password2 = forms.CharField(label=_("Password (again)"),
  68. widget=forms.PasswordInput(render_value=False))
  69. class Meta:
  70. model = User
  71. #fields = ("first_name", "last_name", "email", "username")
  72. fields = ("email", "first_name")
  73. #fields = ("email", "")
  74. exclude = _exclude_fields
  75. def __init__(self, *args, **kwargs):
  76. super(ProfileForm, self).__init__(*args, **kwargs)
  77. self._signup = self.instance.id is None
  78. user_fields = set([f.name for f in User._meta.get_fields()])
  79. try:
  80. self.fields["username"].help_text = ugettext(
  81. "Only letters, numbers, dashes or underscores please")
  82. except KeyError:
  83. pass
  84. for field in self.fields:
  85. # Make user fields required.
  86. if field in user_fields:
  87. self.fields[field].required = True
  88. # Disable auto-complete for password fields.
  89. # Password isn't required for profile update.
  90. if field.startswith("password"):
  91. self.fields[field].widget.attrs["autocomplete"] = "off"
  92. self.fields[field].widget.attrs.pop("required", "")
  93. if not self._signup:
  94. self.fields[field].required = False
  95. if field == "password1":
  96. self.fields[field].help_text = ugettext(
  97. "Leave blank unless you want "
  98. "to change your password")
  99. # Add any profile fields to the form.
  100. try:
  101. profile_fields_form = self.get_profile_fields_form()
  102. profile_fields = profile_fields_form().fields
  103. self.fields.update(profile_fields)
  104. if not self._signup:
  105. user_profile = get_profile_for_user(self.instance)
  106. for field in profile_fields:
  107. value = getattr(user_profile, field)
  108. # Check for multiple initial values, i.e. a m2m field
  109. if isinstance(value, Manager):
  110. value = value.all()
  111. self.initial[field] = value
  112. except ProfileNotConfigured:
  113. pass
  114. def clean_username(self):
  115. """
  116. Ensure the username doesn't exist or contain invalid chars.
  117. We limit it to slugifiable chars since it's used as the slug
  118. for the user's profile view.
  119. """
  120. username = self.cleaned_data.get("username")
  121. if username.lower() != slugify(username).lower():
  122. raise forms.ValidationError(
  123. ugettext("Username can only contain letters, numbers, dashes "
  124. "or underscores."))
  125. lookup = {"username__iexact": username}
  126. try:
  127. User.objects.exclude(id=self.instance.id).get(**lookup)
  128. except User.DoesNotExist:
  129. return username
  130. raise forms.ValidationError(
  131. ugettext("This username is already registered"))
  132. def clean_password2(self):
  133. """
  134. Ensure the password fields are equal, and match the minimum
  135. length defined by ``ACCOUNTS_MIN_PASSWORD_LENGTH``.
  136. """
  137. password1 = self.cleaned_data.get("password1")
  138. password2 = self.cleaned_data.get("password2")
  139. if password1:
  140. errors = []
  141. if password1 != password2:
  142. errors.append(ugettext("Passwords do not match"))
  143. if len(password1) < settings.ACCOUNTS_MIN_PASSWORD_LENGTH:
  144. errors.append(
  145. ugettext("Password must be at least %s characters") %
  146. settings.ACCOUNTS_MIN_PASSWORD_LENGTH)
  147. if errors:
  148. self._errors["password1"] = self.error_class(errors)
  149. return password2
  150. def clean_email(self):
  151. """
  152. Ensure the email address is not already registered.
  153. """
  154. email = self.cleaned_data.get("email")
  155. qs = User.objects.exclude(id=self.instance.id).filter(email=email)
  156. if len(qs) == 0:
  157. return email
  158. raise forms.ValidationError(
  159. ugettext("This email is already registered"))
  160. def save(self, *args, **kwargs):
  161. """
  162. Create the new user. If no username is supplied (may be hidden
  163. via ``ACCOUNTS_PROFILE_FORM_EXCLUDE_FIELDS`` or
  164. ``ACCOUNTS_NO_USERNAME``), we generate a unique username, so
  165. that if profile pages are enabled, we still have something to
  166. use as the profile's slug.
  167. """
  168. kwargs["commit"] = False
  169. user = super(ProfileForm, self).save(*args, **kwargs)
  170. try:
  171. self.cleaned_data["username"]
  172. except KeyError:
  173. if not self.instance.username:
  174. try:
  175. username = ("%(first_name)s %(last_name)s" %
  176. self.cleaned_data).strip()
  177. except KeyError:
  178. username = ""
  179. if not username:
  180. username = self.cleaned_data["email"].split("@")[0]
  181. qs = User.objects.exclude(id=self.instance.id)
  182. user.username = unique_slug(qs, "username", slugify(username))
  183. password = self.cleaned_data.get("password1")
  184. if password:
  185. user.set_password(password)
  186. elif self._signup:
  187. try:
  188. user.set_unusable_password()
  189. except AttributeError:
  190. # This could happen if using a custom user model that
  191. # doesn't inherit from Django's AbstractBaseUser.
  192. pass
  193. user.save()
  194. try:
  195. profile = get_profile_for_user(user)
  196. profile_form = self.get_profile_fields_form()
  197. profile_form(self.data, self.files, instance=profile).save()
  198. except ProfileNotConfigured:
  199. pass
  200. if self._signup:
  201. if (settings.ACCOUNTS_VERIFICATION_REQUIRED or
  202. settings.ACCOUNTS_APPROVAL_REQUIRED):
  203. user.is_active = False
  204. user.save()
  205. else:
  206. token = default_token_generator.make_token(user)
  207. user = authenticate(uidb36=int_to_base36(user.id),
  208. token=token,
  209. is_active=True)
  210. return user
  211. def get_profile_fields_form(self):
  212. try:
  213. return ProfileFieldsForm
  214. except NameError:
  215. raise ProfileNotConfigured
  216. class PasswordResetForm(Html5Mixin, forms.Form):
  217. """
  218. Validates the user's username or email for sending a login
  219. token for authenticating to change their password.
  220. """
  221. username = forms.CharField(label=username_label)
  222. def clean(self):
  223. username = self.cleaned_data.get("username")
  224. username_or_email = Q(username=username) | Q(email=username)
  225. try:
  226. user = User.objects.get(username_or_email, is_active=True)
  227. except User.DoesNotExist:
  228. raise forms.ValidationError(
  229. ugettext("Invalid username/email"))
  230. else:
  231. self._user = user
  232. return self.cleaned_data
  233. def save(self):
  234. """
  235. Just return the authenticated user - used for sending login
  236. email.
  237. """
  238. return getattr(self, "_user", None)