gremmie@1
|
1 """forms for the accounts application"""
|
gremmie@1
|
2
|
bgneal@74
|
3 import logging
|
bgneal@74
|
4
|
gremmie@1
|
5 from django import forms
|
gremmie@1
|
6 from django.contrib.auth.models import User
|
gremmie@1
|
7 from django.core.urlresolvers import reverse
|
gremmie@1
|
8 from django.template.loader import render_to_string
|
gremmie@1
|
9 from django.contrib.sites.models import Site
|
bgneal@6
|
10 from django.conf import settings
|
gremmie@1
|
11
|
gremmie@1
|
12 from core.functions import send_mail
|
gremmie@1
|
13 from accounts.models import PendingUser
|
gremmie@1
|
14 from accounts.models import IllegalUsername
|
gremmie@1
|
15 from accounts.models import IllegalEmail
|
bgneal@472
|
16 from antispam.rate_limit import block_ip
|
gremmie@1
|
17
|
gremmie@1
|
18
|
gremmie@1
|
19 class RegisterForm(forms.Form):
|
bgneal@74
|
20 """Form used to register with the website"""
|
bgneal@498
|
21 username = forms.RegexField(
|
bgneal@498
|
22 max_length=30,
|
bgneal@498
|
23 regex=r'^\w+$',
|
bgneal@498
|
24 error_messages={'invalid': ('Your username must be 30 characters or'
|
bgneal@498
|
25 ' less and contain only letters, numbers and underscores.')},
|
bgneal@498
|
26 widget=forms.TextInput(attrs={'class': 'text'}),
|
bgneal@498
|
27 )
|
bgneal@498
|
28 email = forms.EmailField(widget=forms.TextInput(attrs={'class': 'text'}))
|
bgneal@498
|
29 password1 = forms.CharField(label="Password",
|
bgneal@498
|
30 widget=forms.PasswordInput(attrs={'class': 'text'}))
|
bgneal@498
|
31 password2 = forms.CharField(label="Password confirmation",
|
bgneal@498
|
32 widget=forms.PasswordInput(attrs={'class': 'text'}))
|
bgneal@316
|
33 agree_age = forms.BooleanField(required=True,
|
bgneal@155
|
34 label='I certify that I am over the age of 13',
|
bgneal@155
|
35 error_messages={
|
bgneal@347
|
36 'required': 'Sorry, but you must be over the age of 13 to '
|
bgneal@155
|
37 'register at our site.',
|
bgneal@155
|
38 })
|
bgneal@316
|
39 agree_tos = forms.BooleanField(required=True,
|
bgneal@155
|
40 label='I agree to the Terms of Service',
|
bgneal@155
|
41 error_messages={
|
bgneal@155
|
42 'required': 'You have not agreed to our Terms of Service.',
|
bgneal@155
|
43 })
|
bgneal@155
|
44 agree_privacy = forms.BooleanField(required=True,
|
bgneal@155
|
45 label='I agree to the Privacy Policy',
|
bgneal@155
|
46 error_messages={
|
bgneal@155
|
47 'required': 'You have not agreed to our Privacy Policy.',
|
bgneal@155
|
48 })
|
bgneal@498
|
49 question1 = forms.CharField(label="What number appears in the site name?",
|
bgneal@498
|
50 widget=forms.TextInput(attrs={'class': 'text'}))
|
bgneal@347
|
51 question2 = forms.CharField(label='', required=False,
|
bgneal@347
|
52 widget=forms.TextInput(attrs={'style': 'display: none;'}))
|
gremmie@1
|
53
|
bgneal@74
|
54 def __init__(self, *args, **kwargs):
|
bgneal@74
|
55 self.ip = kwargs.pop('ip', '?')
|
bgneal@74
|
56 super(RegisterForm, self).__init__(*args, **kwargs)
|
bgneal@74
|
57
|
bgneal@74
|
58 def clean_username(self):
|
bgneal@74
|
59 username = self.cleaned_data['username']
|
bgneal@74
|
60 try:
|
bgneal@565
|
61 User.objects.get(username=username)
|
bgneal@74
|
62 except User.DoesNotExist:
|
gremmie@1
|
63 try:
|
bgneal@565
|
64 PendingUser.objects.get(username=username)
|
bgneal@74
|
65 except PendingUser.DoesNotExist:
|
bgneal@74
|
66 try:
|
bgneal@565
|
67 IllegalUsername.objects.get(username=username)
|
bgneal@74
|
68 except IllegalUsername.DoesNotExist:
|
bgneal@74
|
69 return username
|
bgneal@74
|
70 self._validation_error("That username is not allowed.", username)
|
bgneal@74
|
71 self._validation_error("A pending user with that username already exists.", username)
|
bgneal@74
|
72 self._validation_error("A user with that username already exists.", username)
|
gremmie@1
|
73
|
bgneal@74
|
74 def clean_email(self):
|
bgneal@74
|
75 email = self.cleaned_data['email']
|
bgneal@565
|
76
|
bgneal@565
|
77 if User.objects.filter(email=email).count():
|
bgneal@565
|
78 self._validation_error("A user with that email address already exists.", email)
|
bgneal@565
|
79 elif PendingUser.objects.filter(email=email).count():
|
bgneal@74
|
80 self._validation_error("A pending user with that email address already exists.", email)
|
bgneal@565
|
81 elif IllegalEmail.objects.filter(email=email).count():
|
bgneal@565
|
82 self._validation_error("That email address is not allowed.", email)
|
bgneal@659
|
83
|
bgneal@565
|
84 # email is ok
|
bgneal@565
|
85 return email
|
gremmie@1
|
86
|
bgneal@74
|
87 def clean_password2(self):
|
bgneal@74
|
88 password1 = self.cleaned_data.get("password1", "")
|
bgneal@74
|
89 password2 = self.cleaned_data["password2"]
|
bgneal@74
|
90 if password1 != password2:
|
bgneal@74
|
91 self._validation_error("The two password fields didn't match.")
|
bgneal@155
|
92 if len(password1) < 6:
|
bgneal@155
|
93 self._validation_error("Please choose a password of 6 characters or more.")
|
bgneal@74
|
94 return password2
|
gremmie@1
|
95
|
bgneal@346
|
96 def clean_question1(self):
|
bgneal@346
|
97 answer = self.cleaned_data.get('question1')
|
bgneal@346
|
98 success = False
|
bgneal@346
|
99 if answer:
|
bgneal@346
|
100 try:
|
bgneal@346
|
101 val = int(answer)
|
bgneal@346
|
102 except ValueError:
|
bgneal@346
|
103 pass
|
bgneal@346
|
104 else:
|
bgneal@346
|
105 success = val == 101
|
bgneal@346
|
106 if not success:
|
bgneal@346
|
107 self._validation_error("Incorrect answer to our anti-spam question.", answer)
|
bgneal@346
|
108 return answer
|
bgneal@346
|
109
|
bgneal@347
|
110 def clean_question2(self):
|
bgneal@347
|
111 """
|
bgneal@347
|
112 Honeypot field should be empty.
|
bgneal@347
|
113 """
|
bgneal@347
|
114 answer = self.cleaned_data.get('question2')
|
bgneal@347
|
115 if answer:
|
bgneal@472
|
116 block_ip(self.ip)
|
bgneal@347
|
117 self._validation_error('Wrong answer #2: %s' % answer)
|
bgneal@347
|
118 return answer
|
bgneal@347
|
119
|
bgneal@74
|
120 def save(self):
|
bgneal@74
|
121 pending_user = PendingUser.objects.create_pending_user(self.cleaned_data['username'],
|
bgneal@74
|
122 self.cleaned_data['email'],
|
bgneal@74
|
123 self.cleaned_data['password1'])
|
gremmie@1
|
124
|
bgneal@74
|
125 # Send the confirmation email
|
gremmie@1
|
126
|
bgneal@74
|
127 site = Site.objects.get_current()
|
bgneal@74
|
128 admin_email = settings.ADMINS[0][1]
|
gremmie@1
|
129
|
bgneal@316
|
130 activation_link = 'http://%s%s' % (site.domain, reverse('accounts.views.register_confirm',
|
bgneal@74
|
131 kwargs = {'username' : pending_user.username, 'key' : pending_user.key}))
|
gremmie@1
|
132
|
bgneal@74
|
133 msg = render_to_string('accounts/registration_email.txt',
|
bgneal@74
|
134 {
|
bgneal@74
|
135 'site_name' : site.name,
|
bgneal@74
|
136 'site_domain' : site.domain,
|
bgneal@74
|
137 'user_email' : pending_user.email,
|
bgneal@74
|
138 'activation_link' : activation_link,
|
bgneal@74
|
139 'username' : pending_user.username,
|
bgneal@74
|
140 'admin_email' : admin_email,
|
bgneal@74
|
141 })
|
gremmie@1
|
142
|
bgneal@74
|
143 subject = 'Registration Confirmation for ' + site.name
|
bgneal@659
|
144 send_mail(subject, msg, admin_email, [self.cleaned_data['email']],
|
bgneal@659
|
145 defer=False)
|
bgneal@316
|
146 logging.info('Accounts/registration conf. email sent to %s for user %s; IP = %s',
|
bgneal@316
|
147 self.cleaned_data['email'], pending_user.username, self.ip)
|
gremmie@1
|
148
|
bgneal@74
|
149 return pending_user
|
gremmie@1
|
150
|
bgneal@74
|
151 def _validation_error(self, msg, param=None):
|
bgneal@316
|
152 logging.error('Accounts/registration [%s]: %s (%s)', self.ip, msg, param)
|
bgneal@74
|
153 raise forms.ValidationError(msg)
|
bgneal@659
|
154
|
bgneal@659
|
155
|
bgneal@659
|
156 class ForgotUsernameForm(forms.Form):
|
bgneal@659
|
157 """Form used to recover lost username"""
|
bgneal@659
|
158 email = forms.EmailField(widget=forms.TextInput(attrs={'class': 'text'}))
|
bgneal@659
|
159
|
bgneal@659
|
160 def save(self):
|
bgneal@659
|
161 """Email the username associated with the email address to the user."""
|
bgneal@659
|
162 email = self.cleaned_data['email']
|
bgneal@659
|
163 try:
|
bgneal@659
|
164 user = User.objects.get(email=email)
|
bgneal@659
|
165 except User.DoesNotExist:
|
bgneal@659
|
166 # nothing to do; we don't tell the user as this gives away info
|
bgneal@659
|
167 # about our database
|
bgneal@659
|
168 return
|
bgneal@659
|
169
|
bgneal@659
|
170 site = Site.objects.get_current()
|
bgneal@659
|
171 admin_email = settings.ADMINS[0][1]
|
bgneal@659
|
172
|
bgneal@659
|
173 subject = 'Forgotten username for %s' % site.name
|
bgneal@659
|
174 msg = render_to_string('accounts/forgot_user_email.txt', {
|
bgneal@659
|
175 'user': user,
|
bgneal@659
|
176 'site': site,
|
bgneal@659
|
177 'admin_email': admin_email,
|
bgneal@659
|
178 })
|
bgneal@659
|
179 send_mail(subject, msg, admin_email, [email], defer=False)
|
bgneal@659
|
180
|
bgneal@659
|
181 logging.info('Forgotten username email sent to {} <{}>'.format(
|
bgneal@659
|
182 user.username, email))
|