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