Revision 18ffbee1 snf-astakos-app/astakos/im/forms.py
b/snf-astakos-app/astakos/im/forms.py | ||
---|---|---|
1 | 1 |
# Copyright 2011-2012 GRNET S.A. All rights reserved. |
2 |
#
|
|
2 |
# |
|
3 | 3 |
# Redistribution and use in source and binary forms, with or |
4 | 4 |
# without modification, are permitted provided that the following |
5 | 5 |
# conditions are met: |
6 |
#
|
|
6 |
# |
|
7 | 7 |
# 1. Redistributions of source code must retain the above |
8 | 8 |
# copyright notice, this list of conditions and the following |
9 | 9 |
# disclaimer. |
10 |
#
|
|
10 |
# |
|
11 | 11 |
# 2. Redistributions in binary form must reproduce the above |
12 | 12 |
# copyright notice, this list of conditions and the following |
13 | 13 |
# disclaimer in the documentation and/or other materials |
14 | 14 |
# provided with the distribution. |
15 |
#
|
|
15 |
# |
|
16 | 16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
17 | 17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | 18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
... | ... | |
25 | 25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
26 | 26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
27 | 27 |
# POSSIBILITY OF SUCH DAMAGE. |
28 |
#
|
|
28 |
# |
|
29 | 29 |
# The views and conclusions contained in the software and |
30 | 30 |
# documentation are those of the authors and should not be |
31 | 31 |
# interpreted as representing official policies, either expressed |
... | ... | |
42 | 42 |
from django.utils.http import int_to_base36 |
43 | 43 |
from django.core.urlresolvers import reverse |
44 | 44 |
from django.utils.functional import lazy |
45 |
from django.utils.safestring import mark_safe |
|
45 | 46 |
|
46 | 47 |
from astakos.im.models import AstakosUser, Invitation |
47 | 48 |
from astakos.im.settings import INVITATIONS_PER_LEVEL, DEFAULT_FROM_EMAIL, BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY, DEFAULT_CONTACT_EMAIL, RECAPTCHA_ENABLED |
... | ... | |
58 | 59 |
class LocalUserCreationForm(UserCreationForm): |
59 | 60 |
""" |
60 | 61 |
Extends the built in UserCreationForm in several ways: |
61 |
|
|
62 |
|
|
62 | 63 |
* Adds email, first_name, last_name, recaptcha_challenge_field, recaptcha_response_field field. |
63 | 64 |
* The username field isn't visible and it is assigned a generated id. |
64 |
* User created is not active.
|
|
65 |
* User created is not active. |
|
65 | 66 |
""" |
66 | 67 |
recaptcha_challenge_field = forms.CharField(widget=DummyWidget) |
67 | 68 |
recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='') |
68 |
|
|
69 |
|
|
69 | 70 |
class Meta: |
70 | 71 |
model = AstakosUser |
71 | 72 |
fields = ("email", "first_name", "last_name", "has_signed_terms") |
72 | 73 |
widgets = {"has_signed_terms":ApprovalTermsWidget(terms_uri=reverse_lazy('latest_terms'))} |
73 |
|
|
74 |
|
|
74 | 75 |
def __init__(self, *args, **kwargs): |
75 | 76 |
""" |
76 | 77 |
Changes the order of fields, and removes the username field. |
... | ... | |
86 | 87 |
if RECAPTCHA_ENABLED: |
87 | 88 |
self.fields.keyOrder.extend(['recaptcha_challenge_field', |
88 | 89 |
'recaptcha_response_field',]) |
89 |
|
|
90 |
|
|
91 |
if 'has_signed_terms' in self.fields: |
|
92 |
# Overriding field label since we need to apply a link |
|
93 |
# to the terms within the label |
|
94 |
terms_link_html = '<a href="%s" target="_blank">%s</a>' \ |
|
95 |
% (reverse('latest_terms'), _("the terms")) |
|
96 |
self.fields['has_signed_terms'].label = \ |
|
97 |
mark_safe("I agree with %s" % terms_link_html) |
|
98 |
|
|
90 | 99 |
def clean_email(self): |
91 | 100 |
email = self.cleaned_data['email'] |
92 | 101 |
if not email: |
... | ... | |
96 | 105 |
raise forms.ValidationError(_("This email is already used")) |
97 | 106 |
except AstakosUser.DoesNotExist: |
98 | 107 |
return email |
99 |
|
|
108 |
|
|
100 | 109 |
def clean_has_signed_terms(self): |
101 | 110 |
has_signed_terms = self.cleaned_data['has_signed_terms'] |
102 | 111 |
if not has_signed_terms: |
103 | 112 |
raise forms.ValidationError(_('You have to agree with the terms')) |
104 | 113 |
return has_signed_terms |
105 |
|
|
114 |
|
|
106 | 115 |
def clean_recaptcha_response_field(self): |
107 | 116 |
if 'recaptcha_challenge_field' in self.cleaned_data: |
108 | 117 |
self.validate_captcha() |
... | ... | |
119 | 128 |
check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip) |
120 | 129 |
if not check.is_valid: |
121 | 130 |
raise forms.ValidationError(_('You have not entered the correct words')) |
122 |
|
|
131 |
|
|
123 | 132 |
def save(self, commit=True): |
124 | 133 |
""" |
125 | 134 |
Saves the email, first_name and last_name properties, after the normal |
... | ... | |
127 | 136 |
""" |
128 | 137 |
user = super(LocalUserCreationForm, self).save(commit=False) |
129 | 138 |
user.renew_token() |
130 |
user.date_signed_terms = datetime.now() |
|
131 | 139 |
if commit: |
132 | 140 |
user.save() |
133 | 141 |
logger.info('Created user %s', user) |
... | ... | |
137 | 145 |
""" |
138 | 146 |
Extends the LocalUserCreationForm: adds an inviter readonly field. |
139 | 147 |
""" |
140 |
|
|
148 |
|
|
141 | 149 |
inviter = forms.CharField(widget=forms.TextInput(), label=_('Inviter Real Name')) |
142 |
|
|
150 |
|
|
143 | 151 |
class Meta: |
144 | 152 |
model = AstakosUser |
145 | 153 |
fields = ("email", "first_name", "last_name", "has_signed_terms") |
146 | 154 |
widgets = {"has_signed_terms":ApprovalTermsWidget(terms_uri=reverse_lazy('latest_terms'))} |
147 |
|
|
155 |
|
|
148 | 156 |
def __init__(self, *args, **kwargs): |
149 | 157 |
""" |
150 | 158 |
Changes the order of fields, and removes the username field. |
151 | 159 |
""" |
152 | 160 |
super(InvitedLocalUserCreationForm, self).__init__(*args, **kwargs) |
153 |
|
|
161 |
|
|
154 | 162 |
#set readonly form fields |
155 | 163 |
self.fields['inviter'].widget.attrs['readonly'] = True |
156 | 164 |
self.fields['email'].widget.attrs['readonly'] = True |
157 | 165 |
self.fields['username'].widget.attrs['readonly'] = True |
158 |
|
|
166 |
|
|
159 | 167 |
def save(self, commit=True): |
160 | 168 |
user = super(InvitedLocalUserCreationForm, self).save(commit=False) |
161 | 169 |
level = user.invitation.inviter.level + 1 |
... | ... | |
166 | 174 |
user.save() |
167 | 175 |
return user |
168 | 176 |
|
169 |
class ThirdPartyUserCreationForm(UserCreationForm):
|
|
177 |
class ThirdPartyUserCreationForm(forms.ModelForm):
|
|
170 | 178 |
class Meta: |
171 | 179 |
model = AstakosUser |
172 |
fields = ("email", "has_signed_terms") |
|
180 |
fields = ("email", "first_name", "last_name", "third_party_identifier", |
|
181 |
"has_signed_terms", "provider") |
|
173 | 182 |
widgets = {"has_signed_terms":ApprovalTermsWidget(terms_uri=reverse_lazy('latest_terms'))} |
174 | 183 |
|
175 | 184 |
def __init__(self, *args, **kwargs): |
... | ... | |
177 | 186 |
Changes the order of fields, and removes the username field. |
178 | 187 |
""" |
179 | 188 |
if 'ip' in kwargs: |
180 |
self.ip = kwargs['ip'] |
|
181 | 189 |
kwargs.pop('ip') |
182 | 190 |
super(ThirdPartyUserCreationForm, self).__init__(*args, **kwargs) |
183 |
self.fields.keyOrder = ['email'] |
|
191 |
self.fields.keyOrder = ['email', 'first_name', 'last_name', |
|
192 |
'provider', 'third_party_identifier'] |
|
184 | 193 |
if get_latest_terms(): |
185 | 194 |
self.fields.keyOrder.append('has_signed_terms') |
195 |
#set readonly form fields |
|
196 |
ro = ["provider", "third_party_identifier", "first_name", "last_name"] |
|
197 |
for f in ro: |
|
198 |
self.fields[f].widget.attrs['readonly'] = True |
|
199 |
|
|
200 |
if 'has_signed_terms' in self.fields: |
|
201 |
# Overriding field label since we need to apply a link |
|
202 |
# to the terms within the label |
|
203 |
terms_link_html = '<a href="%s" target="_blank">%s</a>' \ |
|
204 |
% (reverse('latest_terms'), _("the terms")) |
|
205 |
self.fields['has_signed_terms'].label = \ |
|
206 |
mark_safe("I agree with %s" % terms_link_html) |
|
207 |
|
|
208 |
def clean_email(self): |
|
209 |
email = self.cleaned_data['email'] |
|
210 |
if not email: |
|
211 |
raise forms.ValidationError(_("This field is required")) |
|
212 |
try: |
|
213 |
AstakosUser.objects.get(email = email) |
|
214 |
raise forms.ValidationError(_("This email is already used")) |
|
215 |
except AstakosUser.DoesNotExist: |
|
216 |
return email |
|
217 |
|
|
218 |
def clean_has_signed_terms(self): |
|
219 |
has_signed_terms = self.cleaned_data['has_signed_terms'] |
|
220 |
if not has_signed_terms: |
|
221 |
raise forms.ValidationError(_('You have to agree with the terms')) |
|
222 |
return has_signed_terms |
|
186 | 223 |
|
187 | 224 |
def save(self, commit=True): |
188 | 225 |
user = super(ThirdPartyUserCreationForm, self).save(commit=False) |
189 | 226 |
user.set_unusable_password() |
227 |
user.renew_token() |
|
190 | 228 |
if commit: |
191 | 229 |
user.save() |
192 | 230 |
logger.info('Created user %s', user) |
... | ... | |
198 | 236 |
#set readonly form fields |
199 | 237 |
self.fields['email'].widget.attrs['readonly'] = True |
200 | 238 |
|
239 |
class ShibbolethUserCreationForm(ThirdPartyUserCreationForm): |
|
240 |
def clean_email(self): |
|
241 |
email = self.cleaned_data['email'] |
|
242 |
if not email: |
|
243 |
raise forms.ValidationError(_("This field is required")) |
|
244 |
try: |
|
245 |
user = AstakosUser.objects.get(email = email) |
|
246 |
if user.provider == 'local': |
|
247 |
self.instance = user |
|
248 |
return email |
|
249 |
else: |
|
250 |
raise forms.ValidationError(_("This email is already associated with another shibboleth account.")) |
|
251 |
except AstakosUser.DoesNotExist: |
|
252 |
return email |
|
253 |
|
|
201 | 254 |
class LoginForm(AuthenticationForm): |
202 | 255 |
username = forms.EmailField(label=_("Email")) |
203 | 256 |
|
... | ... | |
205 | 258 |
""" |
206 | 259 |
Subclass of ``ModelForm`` for permiting user to edit his/her profile. |
207 | 260 |
Most of the fields are readonly since the user is not allowed to change them. |
208 |
|
|
261 |
|
|
209 | 262 |
The class defines a save method which sets ``is_verified`` to True so as the user |
210 | 263 |
during the next login will not to be redirected to profile page. |
211 | 264 |
""" |
212 | 265 |
renew = forms.BooleanField(label='Renew token', required=False) |
213 |
|
|
266 |
|
|
214 | 267 |
class Meta: |
215 | 268 |
model = AstakosUser |
216 |
fields = ('email', 'first_name', 'last_name', 'auth_token', 'auth_token_expires') |
|
217 |
|
|
269 |
fields = ('email', 'first_name', 'last_name', 'auth_token', 'auth_token_expires', 'groups')
|
|
270 |
|
|
218 | 271 |
def __init__(self, *args, **kwargs): |
219 | 272 |
super(ProfileForm, self).__init__(*args, **kwargs) |
220 | 273 |
instance = getattr(self, 'instance', None) |
221 |
ro_fields = ('auth_token', 'auth_token_expires', 'email')
|
|
274 |
ro_fields = ('auth_token', 'auth_token_expires', 'groups')
|
|
222 | 275 |
if instance and instance.id: |
223 | 276 |
for field in ro_fields: |
224 | 277 |
self.fields[field].widget.attrs['readonly'] = True |
225 |
|
|
278 |
|
|
226 | 279 |
def save(self, commit=True): |
227 | 280 |
user = super(ProfileForm, self).save(commit=False) |
228 | 281 |
user.is_verified = True |
... | ... | |
244 | 297 |
""" |
245 | 298 |
Form for sending an invitations |
246 | 299 |
""" |
247 |
|
|
300 |
|
|
248 | 301 |
email = forms.EmailField(required = True, label = 'Email address') |
249 | 302 |
first_name = forms.EmailField(label = 'First name') |
250 | 303 |
last_name = forms.EmailField(label = 'Last name') |
... | ... | |
253 | 306 |
""" |
254 | 307 |
Extends PasswordResetForm by overriding save method: |
255 | 308 |
passes a custom from_email in send_mail. |
256 |
|
|
309 |
|
|
257 | 310 |
Since Django 1.3 this is useless since ``django.contrib.auth.views.reset_password`` |
258 | 311 |
accepts a from_email argument. |
259 | 312 |
""" |
... | ... | |
263 | 316 |
Generates a one-use only link for resetting password and sends to the user. |
264 | 317 |
""" |
265 | 318 |
for user in self.users_cache: |
266 |
url = urljoin(BASEURL, |
|
267 |
'/im/local/reset/confirm/%s-%s' %(int_to_base36(user.id), |
|
268 |
token_generator.make_token(user))) |
|
319 |
url = reverse('django.contrib.auth.views.password_reset_confirm', |
|
320 |
kwargs={'uidb36':int_to_base36(user.id), |
|
321 |
'token':token_generator.make_token(user)}) |
|
322 |
url = request.build_absolute_uri(url) |
|
269 | 323 |
t = loader.get_template(email_template_name) |
270 | 324 |
c = { |
271 | 325 |
'email': user.email, |
... | ... | |
283 | 337 |
class Meta: |
284 | 338 |
model = AstakosUser |
285 | 339 |
fields = ("has_signed_terms",) |
286 |
|
|
340 |
|
|
287 | 341 |
def __init__(self, *args, **kwargs): |
288 | 342 |
super(SignApprovalTermsForm, self).__init__(*args, **kwargs) |
289 |
|
|
343 |
|
|
290 | 344 |
def clean_has_signed_terms(self): |
291 | 345 |
has_signed_terms = self.cleaned_data['has_signed_terms'] |
292 | 346 |
if not has_signed_terms: |
293 | 347 |
raise forms.ValidationError(_('You have to agree with the terms')) |
294 | 348 |
return has_signed_terms |
295 |
|
|
296 |
def save(self, commit=True): |
|
297 |
""" |
|
298 |
Updates date_signed_terms & has_signed_terms fields. |
|
299 |
""" |
|
300 |
user = super(SignApprovalTermsForm, self).save(commit=False) |
|
301 |
user.date_signed_terms = datetime.now() |
|
302 |
if commit: |
|
303 |
user.save() |
|
304 |
return user |
|
305 | 349 |
|
306 | 350 |
class InvitationForm(forms.ModelForm): |
307 | 351 |
username = forms.EmailField(label=_("Email")) |
308 | 352 |
|
353 |
def __init__(self, *args, **kwargs): |
|
354 |
super(InvitationForm, self).__init__(*args, **kwargs) |
|
355 |
|
|
309 | 356 |
class Meta: |
310 | 357 |
model = Invitation |
311 | 358 |
fields = ('username', 'realname') |
... | ... | |
317 | 364 |
raise forms.ValidationError(_('There is already invitation for this email.')) |
318 | 365 |
except Invitation.DoesNotExist: |
319 | 366 |
pass |
320 |
return username |
|
367 |
return username |
Also available in: Unified diff